pyformex-0.8.6/0000755000211500021150000000000011705105304013226 5ustar benebene00000000000000pyformex-0.8.6/pyformex.desktop0000644000211500021150000000035011702357667016512 0ustar benebene00000000000000[Desktop Entry] Type=Application Exec=pyformex --redirect Icon=pyformex-64x64.xpm Name=pyFormex GenericName=pyFormex 3D Geometry Designer Comment=pyFormex is a program to design, manipulate and operate on 3D geometrical structures. pyformex-0.8.6/README0000644000211500021150000002122111705104656014115 0ustar benebene00000000000000.. $Id: README 2150 2012-01-16 20:33:48Z bverheg $ -*- rst -*- .. This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) pyFormex is a tool for generating, manipulating and transforming 3D geometrical models by sequences of mathematical operations. Home page: http://pyformex.org Project page: http://savannah.nongnu.org/projects/pyformex/ Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) Distributed under the GNU General Public License version 3 or later. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. What is pyFormex? ================= pyFormex is a tool for generating, manipulating and transforming large geometrical models of 3D structures by sequences of mathematical transformations. Thanks to a powerful (Python based) scripting language, pyFormex is very well suited for the automated design of spatial frame structures. It provides a wide range of operations on surface meshes, like STL type triangulated surfaces. There are provisions to import medical scan images. pyFormex can also be used as a pre- and post-processor for Finite Element analysis programs. Finally, it might be used just for creating some nice graphics. Using pyFormex, the topology of the elements and the final geometrical form can be decoupled. Often, topology is created first and then mapped onto the geometry. Through the scripting language, the user can define any sequence of transformations, built from provided or user defined functions. This way, building parametric models becomes a natural thing. While pyFormex is still under development (see the project page at http://savannah.nongnu.org/projects/pyformex/), it already provides a fairly stable scripting language and an OpenGL GUI environment for displaying and manipulating the generated structures. Installation ============ We detail here only the installation on Linux platforms. Prerequisites ------------- Essential: - python: http://www.python.org (version 2.4 or higher; 2.5 recommended) - numpy: /http://numpy.scipy.org/ (version 1.0 or higher; 1.1 recommended) - Qt4: http://www.trolltech.com/products/qt - PyQt4: http://www.riverbankcomputing.co.uk/pyqt/index.php - PyOpenGL: http://pyopengl.sourceforge.net/ To compile the acceleration library (highly recommended!), you will also need the appropriate header Python and OpenGL header files. Further, we recommend to install the following for extended functionality: - python-gnuplot - python-doc - python-scipy - units - imagemagick - admesh For the following extensions, which are not easily packaged format, an install script is provided in this distribution. - calpy - pygl2ps - gts - tetgen Easy way to install prerequisites on Debian GNU/Linux or Ubuntu --------------------------------------------------------------- On Debian (lenny) systems (and Debian-derivatives like Ubuntu) you can install all basic prerequisites and their dependencies with the command: ``apt-get install python-qt4-gl python-numpy`` If you want to compile the acceleration library (which is highly recommended) you also need the header files: ``apt-get install python-dev libgl1-mesa-dev`` Install the extras: ``apt-get install python-gnuplot python-doc python-scipy units imagemagick admesh`` Howto install pyFormex: ----------------------- If a previous version of pyFormex was already installed on your system, you may want to remove the older version first before installing the new one. See further (Howto uninstall pyFormex) We suppose you have installed all the prerequisites and downloaded the pyformex source tarball: pyformex-VERSION.tar.gz Unpack the pyFormex source tarball: ``tar xvzf pyformex-VERSION.tar.gz`` Go to the created pyformex directory: ``cd pyformex-VERSION`` Do (with root privileges) ``python setup.py install --prefix=/usr/local`` This will install pyFormex under /usr/local/. You can change the prefix to install in some other place. After installation you can remove the directory where you unpacked the tarball. The installation procedure installs everything into a single directory, and creates a symlink to the executable in /usr/local/bin. You can use the command pyformex --whereami to find out where pyFormex is installed. If you have xdg-utils on your system, the installation procedure will also install a menu and desktop starter for pyFormex. Howto uninstall pyFormex: ------------------------- A pyFormex installation of version 0.5 or later can be removed with the command ``pyformex --remove`` and then answering 'yes' to the question. Howto install the extra packages ----------------------------------- pyFormex makes use of extra software components to enhance its functionality. While they are not required for the core operation of pyFormex, many users may want to install them. Some of these extras (admesh, units) can easily be installed from your regular distribution repositories. Some extra components however either are not available in packaged format, or the existing packages do not work together well with pyFormex. For these components, the pyFormex distribution contains an adhoc install procedure to help our users with the installation. The procedures are located in dedicated subdirectories of the pyformex-VERSION/pyformex/extra directory of the unpacked release tree. Each subdirectory contains a install script `install.sh`. To install the corresponding package, execute the script from within the subdirectory, with root privileges and with a single parameter 'all': ``(sudo) ./install.sh all`` - gl2ps: Image output to in vector formats (PS, EPS, PDF and SVG). Requires a Python interface, provided by our install procedure. - gts: Operations on triangulated surfaces. The available standard packages do not install all required files. Our install procedure will also fix some bugs. - tetgen: Create quality tetraeder meshes. - calpy: Simple finite element simulation framework. Needed for the *_calpy examples. Has to be installed from source, because is has to be compiled against the same numpy version as pyFormex itself. Running pyFormex from Subversion (SVN) sources ---------------------------------------------- If the officially released pyFormex packages are not suitable for your needs, you can try installing pyFormex from the Subversion sources. Besides the basic prerequisites (see above), you will also need to have 'Subversion' installed. The `SVN repository`_ on the developer site is accessible by anonymous SVN. The command ``svn co svn://svn.savannah.nongnu.org/pyformex/trunk/pyformex MYDIR`` will checkout the source to a local directory ``MYDIR``. Provided you have all the prerequisites installed, pyFormex can then be run directly from the checked out source with the command: ``MYDIR/pyformex/pyformex``. If you want to use the compiled accelerator library however, you will have to create it first: ``cd MYDIR/pyformex/lib; ./configure; make`` should do it. Once you have a checked-out source tree, you can easily sync it to the latest repository version by just issuing the command ``svn up`` from your checkout directory. Documentation ============= The documentation is included with the pyFormex distribution. Execute the command pyformex --whereami to find out where the pyformex files are installed. The pyformex path contains a directory doc/html with the full html ocumentation. When working with the pyFormex GUI, you can load this local documentation in your browser using the help menu, or view the online documentation at http://pyformex.org/doc. To use pyFormex, you create a script that builds a structure layout and geometry. Look at the examples to get the feeling of how it is working. Use the File->Play menu selection to display the structure. The examples and the Python source are relatively well documented. Help and Feedback ================= For any questions or feedback, please go to the pyFormex support tracker at http://savannah.nongnu.org/support/?group=pyformex If you find any bugs or malfunctions in pyFormex, please submit them to the pyFormex Bug tracker http://savannah.nongnu.org/bugs/?group=pyformex .. _`SVN repository`: http://savannah.nongnu.org/svn/?group=pyformex .. target-notes:: .. End pyformex-0.8.6/PKG-INFO0000644000211500021150000000261111705105304014323 0ustar benebene00000000000000Metadata-Version: 1.1 Name: pyformex Version: 0.8.6 Summary: Program to generate and transform 3D geometries from scripts. Home-page: http://pyformex.org Author: Benedict Verhegghe Author-email: benedict.verhegghe@ugent.be License: GNU General Public License (GPL) Description: pyFormex is a tool to generate, transform and manipulate large and complex geometrical models of 3D structures by sequences of mathematical operations in a Python script. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Environment :: X11 Applications :: Qt Classifier: Intended Audience :: End Users/Desktop Classifier: Intended Audience :: Science/Research Classifier: Intended Audience :: Education Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: POSIX Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: C Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering Classifier: Topic :: Scientific/Engineering :: Mathematics Classifier: Topic :: Scientific/Engineering :: Visualization Classifier: Topic :: Scientific/Engineering :: Physics Requires: numpy Requires: OpenGL Requires: PyQt4 pyformex-0.8.6/ReleaseNotes0000644000211500021150000001025311705104656015554 0ustar benebene00000000000000.. $Id: ReleaseNotes 2150 2012-01-16 20:33:48Z bverheg $ -*- rst -*- .. This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) pyFormex is a tool for generating, manipulating and transforming 3D geometrical models by sequences of mathematical operations. Home page: http://pyformex.org Project page: http://savannah.nongnu.org/projects/pyformex/ Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) Distributed under the GNU General Public License version 3 or later. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. pyFormex 0.8.6 Releasenotes ============================ This is a collection of the most prominent changes in pyFormex 0.8.6 compared to the previous release 0.8.5. There are not many new features in this release. The major change is the installation procedure, triggered by our attempts to create Debian packages. And we have a couple of bug fixes. Installation ------------ - The installation procedure now includes a configuration file (setup.py) which sets the default installation prefix to '/usr/local'. The command to install (if you accept the defaults) is thus:: python setup.py install - Secondly, if the compilation of the acceleration libraries fails, the installation procedure continues without the libraries. - Finally, it is now possible to enforce installation without the libraries, by using the command:: python setup.py install --no-accel Gui --- - Camera Settings dialog, with e.g. interactive front and back plane clipping - Save current camera settings (and restore) pyFormex 0.8.5 Releasenotes ============================ This is a collection of the most prominent changes in pyFormex 0.8.5 compared to the previous release 0.8.4. Like always, besides the new things mentioned below, we also have lots of bug fixes, code cleanup and functionality enhancements. Documentation ------------- - The official documentation is now on the new website at http://www.nongnu.org/pyformex/doc/index.html Installation ------------ - The post-install script will normally not need to run pyformex, easing the installation in sandboxed environments. Command line ------------ Configuration ------------- Core ---- - coords: new Coords methods: average, inertia, centered; new class BoundVectors; new functions pattern and xpattern - formex: the pattern and mpattern functions are replaced by the functions pattern and xpattern in the coords module. Some functions are provided for compatibility, but users are urged to transform their code to the new conventions. - geomtools: new functions pointsAtLines, pointsAtSegments, distancesPFL, distancesPFS; mode parameter to intersection* functions - Mesh: new methods: partitionByAngle, avgNodes, removeDuplicate, enhanced and unified connect, extrude and revolve methods Gui --- - canvas background can have a horizontal grading - more configuration settings can be changed from the GUI Widgets ------- Drawing ------- - faster drawing of large lists - improved Nurbs drawing Scripting --------- Plugin menus ------------ - geometry_menu: added(moved) some functionality from Surface menu, added (some) DXF import Plugins ------- - curve: Curve.endpoints method; Arc (enhanced) and Line (new) classes, PolyLine concatenation (append), BezierSpline.toMesh - dxf: added import of some entities from .DXF files: Line, Polyline, Arc External -------- - a new program *dxfparser* is provided to import (parts of) .DXF files. - pyformex-search script for searching the pyFormex source Examples -------- New examples: - Multicut - ExtrudeMesh - Connect .. End pyformex-0.8.6/setup.cfg0000644000211500021150000000003411700305342015043 0ustar benebene00000000000000[global] prefix=/usr/local pyformex-0.8.6/pyformex/0000755000211500021150000000000011705105304015077 5ustar benebene00000000000000pyformex-0.8.6/pyformex/doc/0000755000211500021150000000000011705105304015644 5ustar benebene00000000000000pyformex-0.8.6/pyformex/doc/html/0000755000211500021150000000000011705105304016610 5ustar benebene00000000000000pyformex-0.8.6/pyformex/doc/html/ref/0000755000211500021150000000000011705105304017364 5ustar benebene00000000000000pyformex-0.8.6/pyformex/doc/html/ref/arraytools.html0000644000211500021150000024120711705104247022464 0ustar benebene00000000000000 3. arraytools — A collection of numerical array utilities. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

2. formex — Formex algebra in Python

Next topic

4. script — Basic pyFormex script functions

[FSF Associate Member]

Valid XHTML 1.0 Transitional

3. arraytools — A collection of numerical array utilities.

These are general utility functions that depend only on the numpy array model. All pyformex modules needing numpy should import everything from this module:

from arraytools import * 

Classes defined in module arraytools

Functions defined in module arraytools

arraytools.sind(arg, angle_spec=0.017453292519943295)

Return the sine of an angle in degrees.

For convenience, this can also be used with an angle in radians, by specifying angle_spec=Rad.

>>> print sind(30), sind(pi/6,Rad)
0.5 0.5
arraytools.cosd(arg, angle_spec=0.017453292519943295)

Return the cosine of an angle in degrees.

For convenience, this can also be used with an angle in radians, by specifying angle_spec=Rad.

>>> print cosd(60), cosd(pi/3,Rad)
0.5 0.5
arraytools.tand(arg, angle_spec=0.017453292519943295)

Return the tangens of an angle in degrees.

For convenience, this can also be used with an angle in radians, by specifying angle_spec=Rad.

arraytools.arcsind(arg, angle_spec=0.017453292519943295)

Return the angle whose sine is equal to the argument.

By default, the angle is returned in Degrees. Specifying angle_spec=Rad will return the angle in radians.

>>> print arcsind(0.5), arcsind(1.0,Rad)
30.0 1.57079632679
arraytools.arccosd(arg, angle_spec=0.017453292519943295)

Return the angle whose cosine is equal to the argument.

By default, the angle is returned in Degrees. Specifying angle_spec=Rad will return the angle in radians.

>>> print arccosd(0.5), arccosd(-1.0,Rad)
60.0 3.14159265359
arraytools.arctand(arg, angle_spec=0.017453292519943295)

Return the angle whose tangens is equal to the argument.

By default, the angle is returned in Degrees. Specifying angle_spec=Rad will return the angle in radians.

>>> print arctand(1.0), arctand(-1.0,Rad)
45.0 -0.785398163397
arraytools.arctand2(sin, cos, angle_spec=0.017453292519943295)

Return the angle whose sine and cosine values are given.

By default, the angle is returned in Degrees. Specifying angle_spec=Rad will return the angle in radians. This returns an angle in the range ]-180,180].

>>> print arctand2(0.0,-1.0), arctand2(-sqrt(0.5),-sqrt(0.5),Rad)
180.0 -2.35619449019
arraytools.niceLogSize(f)

Return the smallest integer e such that 10**e > abs(f).

This returns the number of digits before the decimal point.

>>> print [ niceLogSize(a) for a in [1.3, 35679.23, 0.4, 0.00045676] ]
[1, 5, 0, -3]
arraytools.niceNumber(f, below=False)

Return a nice number close to f.

f is a float number, whose sign is disregarded.

A number close to abs(f) but having only 1 significant digit is returned. By default, the value is above abs(f). Setting below=True returns a value above.

Example:

>>> [ str(niceNumber(f)) for f in [ 0.0837, 0.837, 8.37, 83.7, 93.7] ]
['0.09', '0.9', '9.0', '90.0', '100.0']
>>> [ str(niceNumber(f,below=True)) for f in [ 0.0837, 0.837, 8.37, 83.7, 93.7] ]
['0.08', '0.8', '8.0', '80.0', '90.0']
arraytools.dotpr(A, B, axis=-1)

Return the dot product of vectors of A and B in the direction of axis.

This multiplies the elements of the arrays A and B, and the sums the result in the direction of the specified axis. Default is the last axis. Thus, if A and B are sets of vectors in their last array direction, the result is the dot product of vectors of A with vectors of B. A and B should be broadcast compatible.

>>> A = array( [[1.0, 1.0], [1.0,-1.0], [0.0, 5.0]] )
>>> B = array( [[5.0, 3.0], [2.0, 3.0], [1.33,2.0]] )
>>> print dotpr(A,B)
[  8.  -1.  10.]
arraytools.length(A, axis=-1)

Returns the length of the vectors of A in the direction of axis.

The components of the vectors are stored along the specified array axis (default axis is the last).

arraytools.normalize(A, axis=-1)

Normalize the vectors of A in the direction of axis.

The components of the vectors are stored along the specified array axis (default axis is the last).

arraytools.projection(A, B, axis=-1)

Return the (signed) length of the projection of vector of A on B.

The components of the vectors are stored along the specified array axis (default axis is the last).

arraytools.orthog(A, B, axis=-1)

Return the (signed) length of the projection of vector of A on B.

The components of the vectors are stored along the specified array axis (default axis is the last).

arraytools.norm(v, n=2)

Return thr n-norm of the vector v.

Default is the quadratic norm (vector length). n == 1 returns the sum. n <= 0 returns the max absolute value.

arraytools.horner(a, u)

Compute the value of a polynom using Horner’s rule.

Params:

  • a: float(n+1,nd), nd-dimensional coefficients of the polynom of degree n, starting from lowest degree.
  • u: float(nu), parametric values where the polynom is evaluated

Returns: float(nu,nd), nd-dimensional values of the polynom.

>>> print horner([[1.,1.,1.],[1.,2.,3.]],[0.5,1.0])
[[ 1.5  2.   2.5]
 [ 2.   3.   4. ]]
arraytools.solveMany(A, b, direct=True)

Solve many systems of linear equations.

Parameters:

  • A: (ndof,ndof,nsys) shaped float array.
  • b: (ndof,nrhs,nsys) shaped float array.

Returns: a float array x with same shape as b, where x[:,i,j] solves the system of linear equations A[:,:,j].x[:,i,j] = b[:,i,j].

For ndof in [1,2,3], all solutions are by default computed directly and simultaneously. If direct=False is specified, a general linear equation solver is called for each system of equations. This is also the method used if ndof > 4.

arraytools.inside(p, mi, ma)

Return true if point p is inside bbox defined by points mi and ma

arraytools.isClose(values, target, rtol=1e-05, atol=1e-08)

Returns an array flagging the elements close to target.

values is a float array, target is a float value. values and target should be broadcastable to the same shape.

The return value is a boolean array with shape of values flagging where the values are close to target. Two values a and b are considered close if | a - b | < atol + rtol * | b |

arraytools.anyVector(v)

Create a 3D vector.

v is some data compatible with a (3)-shaped float array. Returns v as such an array.

arraytools.unitVector(v)

Return a unit vector in the direction of v.

v is either an integer specifying one of the global axes (0,1,2), or a 3-element array or compatible.

arraytools.rotationMatrix(angle, axis=None, angle_spec=0.017453292519943295)

Return a rotation matrix over angle, optionally around axis.

The angle is specified in degrees, unless angle_spec=Rad is specified. If axis==None (default), a 2x2 rotation matrix is returned. Else, axis should specifying the rotation axis in a 3D world. It is either one of 0,1,2, specifying a global axis, or a vector with 3 components specifying an axis through the origin. In either case a 3x3 rotation matrix is returned. Note that:

  • rotationMatrix(angle,[1,0,0]) == rotationMatrix(angle,0)
  • rotationMatrix(angle,[0,1,0]) == rotationMatrix(angle,1)
  • rotationMatrix(angle,[0,0,1]) == rotationMatrix(angle,2)

but the latter functions calls are more efficient. The result is returned as an array.

arraytools.rotmat(x)

Create a rotation matrix defined by 3 points in space.

x is an array of 3 points. After applying the resulting rotation matrix to the global axes, the 0 axis becomes // to the vecors x0-x1, the 1 axis lies in the plane x0,x1,x2 and is orthogonal to x0-x1, and the 3 axis is orthogonal to the plane x0,x1,x2.

arraytools.trfMatrix(x, y)

Find the transformation matrix from points x0 to x1.

x and y are each arrays of 3 non-colinear points. The return value is a tuple of a translation vector and a rotation matrix. The returned translation trl and rotationmatrix rot transform the points x thus that:

  • point x0 coincides with point y0,
  • line x0,x1 coincides with line y0,y1
  • plane x0,x1,x2 coincides with plane y0,y1,y2

The rotation is to be applied first and should be around the first point x0. The full transformation of a Coords object is thus obtained by

(coords - x0) * rot + trl + x0  = coords * rot + (trl+x0-x0*rot)
arraytools.rotMatrix(u, w=[0.0, 0.0, 1.0], n=3)

Create a rotation matrix that rotates axis 0 to the given vector.

u is a vector representing the Return either a 3x3(default) or 4x4(if n==4) rotation matrix.

arraytools.rotationAnglesFromMatrix(mat, angle_spec=0.017453292519943295)

Return rotation angles from rotation matrix mat.

This returns the three angles around the global axes 0, 1 and 2. The angles are returned in degrees, unless angle_spec=Rad.

arraytools.vectorRotation(vec1, vec2, upvec=[0.0, 0.0, 1.0])

Return a rotation matrix for rotating vector vec1 to vec2

The rotation matrix will be such that the plane of vec2 and the rotated upvec will be parallel to the original upvec.

This function is like arraytools.rotMatrix(), but allows the specification of vec1. The returned matrix should be used in postmultiplication to the Coords.

arraytools.growAxis(a, add, axis=-1, fill=0)

Increase the length of a single array axis.

The specified axis of the array a is increased with a value add and the new elements all get the value fill.

Parameters:

  • a: array
  • add: int The value to add to the axis length. If <= 0, the unchanged array is returned.
  • axis: int The axis to change, default -1 (last).
  • fill: int or float The value to set the new elements to.
Returns:
An array with same dimension and type as a, but with a length along axis equal to a.shape[axis] + add. The new elements all have the value fill.

Example:

>>> growAxis([[1,2,3],[4,5,6]],2)
array([[1, 2, 3, 0, 0],
       [4, 5, 6, 0, 0]])
arraytools.reorderAxis(a, order, axis=-1)

Reorder the planes of an array along the specified axis.

The elements of the array are reordered along the specified axis according to the specified order.

Parameters:

  • a: array_like

  • order: specifies how to reorder the elements. It is either one of the special string values defined below, or else it is an index holding a permutation of arange(self.nelems(). Each value specifies the index of the old element that should be placed at its position. Thus, the order values are the old index numbers at the position of the new index number.

    order can also take one of the following predefined values, resulting in the corresponding renumbering scheme being generated:

    • ‘reverse’: the elements along axis are placed in reverse order
    • ‘random’: the elements along axis are placed in random order
Returns:
An array with the same elements of self, where only the order along the specified axis has been changed.

Example:

>>> reorderAxis([[1,2,3],[4,5,6]],[2,0,1])
array([[3, 1, 2],
       [6, 4, 5]])
arraytools.reverseAxis(a, axis=-1)

Reverse the elements along a computed axis.

Example:

>>> reverseAxis([[1,2,3],[4,5,6]],0)
array([[4, 5, 6],
       [1, 2, 3]])

Remark: if the axis is known in advance, it may be more efficient to use an indexing operation, like a[:,::-1,:]

arraytools.addAxis(a, axis=0)

Add an additional axis with length 1 to an array.

The new axis is inserted before the specified one. Default is to add it at the front.

arraytools.stack(al, axis=0)

Stack a list of arrays along a new axis.

al is a list of arrays all of the same shape. The return value is a new array with one extra axis, along which the input arrays are stacked. The position of the new axis can be specified, and is the first axis by default.

arraytools.checkArray(a, shape=None, kind=None, allow=None)

Check that an array a has the correct shape and type.

The input a is anything that can be converted into a numpy array. Either shape and/or kind can be specified. and will then be checked. The dimensions where shape contains a -1 value are not checked. The number of dimensions should match. If kind does not match, but the value is included in allow, conversion to the requested type is attempted.

Returns the array if valid; else, an error is raised.

arraytools.checkArray1D(a, size=None, kind=None, allow=None)

Check that an array a has the correct size and type.

Either size and or kind can be specified. If kind does not match, but is included in allow, conversion to the requested type is attempted. Returns the array if valid. Else, an error is raised.

arraytools.checkArrayDim(a, ndim=-1)

Check that an array has the correct dimensionality.

Returns asarray(a) if ndim < 0 or a.ndim == ndim Else, an error is raised.

arraytools.checkUniqueNumbers(nrs, nmin=0, nmax=None)

Check that an array contains a set of unique integers in a given range.

This functions tests that all integer numbers in the array are within the range math:nmin <= i < nmax

nrs: an integer array of any shape. nmin: minimum allowed value. If set to None, the test is skipped. nmax: maximum allowed value + 1! If set to None, the test is skipped. Default range is [0,unlimited].

If the numbers are no unique or one of the limits is passed, an error is raised. Else, the sorted list of unique values is returned.

arraytools.readArray(file, dtype, shape, sep=' ')

Read an array from an open file.

This uses numpy.fromfile() to read an array with known shape and data type from an open file. The sep parameter can be specified as in fromfile.

arraytools.writeArray(file, array, sep=' ')

Write an array to an open file.

This uses numpy.tofile() to write an array to an open file. The sep parameter can be specified as in tofile.

arraytools.cubicEquation(a, b, c, d)

Solve a cubiq equation using a direct method.

a,b,c,d are the (floating point) coefficients of a third degree polynomial equation:

a * x**3  +  b * x**2  +  c * x  +  d   =   0

This function computes the three roots (real and complex) of this equation and returns full information about their kind, sorting order, occurrence of double roots. It uses scaling of the variables to enhance the accuracy.

The return value is a tuple (r1,r2,r3,kind), where r1,r2 and r3 are three float values and kind is an integer specifying the kind of roots.

Depending on the value of kind, the roots are defined as follows:

kind roots
0 three real roots r1 < r2 < r3
1 three real roots r1 < r2 = r3
2 three real roots r1 = r2 < r3
3 three real roots r1 = r2 = r3
4 one real root r1 and two complex conjugate roots with real part r2 and imaginary part r3; the complex roots are thus: r2+i*r3 en r2-i*r3, where i = sqrt(-1).

If the coefficient a==0, a ValueError is raised.

Example:

>>> cubicEquation(1.,-3.,3.,-1.)
([1.0, 1.0, 1.0], 3)
arraytools.uniqueOrdered(ar1, return_index=False, return_inverse=False)

Find the unique elements of an array.

This works like numpy’s unique, but uses a stable sorting algorithm. The returned index may therefore hold other entries for multiply occurring values. In such case, uniqueOrdered returns the first occurrence in the flattened array. The unique elements and the inverse index are always the same as those returned by numpy’s unique.

Parameters:

  • ar1 : array_like

    This array will be flattened if it is not already 1-D.

  • return_index : bool, optional

    If True, also return the indices against ar1 that result in the unique array.

  • return_inverse : bool, optional

    If True, also return the indices against the unique array that result in ar1.

Returns:

  • unique : ndarray

    The unique values.

  • unique_indices : ndarray, optional

    The indices of the unique values. Only provided if return_index is True.

  • unique_inverse : ndarray, optional

    The indices to reconstruct the original array. Only provided if return_inverse is True.

Example:

>>> a = array([2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,7,8])
>>> unique(a,True,True)
(array([1, 2, 3, 4, 5, 6, 7, 8]), array([ 7,  0,  1, 10,  3,  4,  5,  6]), array([1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7]))
>>> uniqueOrdered(a,True,True)
(array([1, 2, 3, 4, 5, 6, 7, 8]), array([7, 0, 1, 2, 3, 4, 5, 6]), array([1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7]))

Notice the difference in the 4-th entry of the second array.

arraytools.renumberIndex(index)

Renumber an index sequentially.

Given a one-dimensional integer array with only non-negative values, and nval being the number of different values in it, and you want to replace its elements with values in the range 0..nval, such that identical numbers are always replaced with the same number and the new values at their first occurrence form an increasing sequence 0..nval. This function will give you the old numbers corresponding with each position 0..nval.

Parameters:

  • index: array_like, 1-D, integer An array with non-negative integer values
Returns:
A 1-D integer array with length equal to nval, where nval is the number of different values in index, and holding the original values corresponding to the new value 0..nval.
Remark:
Use inverseUniqueIndex() to find the inverse mapping needed to replace the values in the index by the new ones.

Example:

>>> renumberIndex([0,5,2,2,6,0])
array([0, 5, 2, 6])
>>> inverseUniqueIndex(renumberIndex([0,5,2,2,6,0]))[[0,5,2,2,6,0]]
array([0, 1, 2, 2, 3, 0])
arraytools.inverseUniqueIndex(index)

Inverse an index.

Given a 1-D integer array with unique non-negative values, and max being the highest value in it, this function returns the position in the array of the values 0..max. Values not occurring in input index get a value -1 in the inverse index.

Parameters:

  • index: array_like, 1-D, integer An array with non-negative values, wihch all have to be unique.
Returns:
A 1-D integer array with length max+1, with the positions in index of the values 0..max, or -1 if the value does not occur in index.
Remark:
The inverse index translates the unique index numbers in a sequential index, so that inverseUniqueIndex(index)[index] == arange(1+index.max()).

Example:

>>> inverseUniqueIndex([0,5,2,6])
array([ 0, -1,  2, -1, -1,  1,  3])
>>> inverseUniqueIndex([0,5,2,6])[[0,5,2,6]]
array([0, 1, 2, 3])
arraytools.sortByColumns(a)

Sort an array on all its columns, from left to right.

The rows of a 2-dimensional array are sorted, first on the first column, then on the second to resolve ties, etc..

Parameters:

  • a: array_like, 2-D
Returns:
A 1-D integer array specifying the order in which the rows have to be taken to obtain an array sorted by columns.

Example:

>>> sortByColumns([[1,2],[2,3],[3,2],[1,3],[2,3]])
array([0, 3, 1, 4, 2])
arraytools.uniqueRows(a, permutations=False)

Return (the indices of) the unique rows of a 2-D array.

Parameters:

  • a: array_like, 2-D
  • permutations: bool If True, rows which are permutations of the same data are considered equal. The default is to consider permutations as different.

Returns:

  • uniq: a 1-D integer array with the numbers of the unique rows from a. The order of the elements in uniq is determined by the sorting procedure, which in the current implementation is sortByColumns(). If permutations==True, a is sorted along its axis -1 before calling this sorting function.
  • uniqid: a 1-D integer array with length equal to a.shape[0] with the numbers of uniq corresponding to each of the rows of a.

Example:

>>> uniqueRows([[1,2],[2,3],[3,2],[1,3],[2,3]])
(array([0, 3, 1, 2]), array([0, 2, 3, 1, 2]))
>>> uniqueRows([[1,2],[2,3],[3,2],[1,3],[2,3]],permutations=True)
(array([0, 3, 1]), array([0, 2, 2, 1, 2]))
arraytools.argNearestValue(values, target)

Return the index of the item nearest to target.

Parameters:

  • values: a list of float values
  • target: a float value

Returns: the position of the item in values that is nearest to target.

Example:

>>> argNearestValue([0.1,0.5,0.9],0.7)
1
arraytools.nearestValue(values, target)

Return the item nearest to target.

values: a list of float values

target: a single value

Return value: the item in values values that is nearest to target.

arraytools.inverseIndex(index, maxcon=4)

Return an inverse index.

An index is an array pointing at other items by their position. The inverse index is a collection of the reverse pointers. Negative values in the input index are disregarded.

Parameters:

  • index: an array of integers, where only non-negative values are meaningful, and negative values are silently ignored. A Connectivity is a suitable argument.
  • maxcon: int: an initial estimate for the maximum number of rows a single element of index occurs at. The default will usually do well, because the procedure will automatically enlarge it when needed.
Returns:

An (mr,mc) shaped integer array where:

  • mr will be equal to the highest positive value in index, +1.
  • mc will be equal to the highest row-multiplicity of any number in index.

Row i of the inverse index contains all the row numbers of index that contain the number i. Because the number of rows containing the number i is usually not a constant, the resulting array will have a number of columns mc corresponding to the highest row-occurrence of any single number. Shorter rows are padded with -1 values to flag non-existing entries.

Example:

>>> inverseIndex([[0,1],[0,2],[1,2],[0,3]])
array([[ 0,  1,  3],
       [-1,  0,  2],
       [-1,  1,  2],
       [-1, -1,  3]])
arraytools.matchIndex(target, values)

Find position of values in target.

This function finds the position in the array target of the elements from the array values.

Parameters:

  • target: an index array with all non-negative values. If not 1-D, it will be flattened.
  • values: an index array with all non-negative values. If not 1-D, it will be flattened.

Returns: an index array with the same size as values. For each number in values, the index contains the position of that value in the flattened target, or -1 if that number does not occur in target. If an element from values occurs more than once in target, it is currently undefined which of those positions is returned.

Remark that after m = matchIndex(target,values) the equality values[m] == target holds in all the non-negative positions of m.

Example:

>>> A = array([1,3,4,5,7,8,9])
>>> B = array([0,6,7,1,2])
>>> matchIndex(A,B)
array([-1, -1,  4,  0, -1])
arraytools.groupArgmin(val, gid)

Compute the group minimum

Computes the minimum value per group of a set of values tagged with a group number.

Parameters:

  • val: (nval,) shaped array of values
  • gid: (nval,) shaped int array of group identifiers

Returns:

  • ugid: (ngrp,) shaped int array with unique group identifiers
  • minpos: (ngrp,p) shape int array giving the position in val of the minimum of all values with the corresponding group identifier in ugid.

After return, the minimum values corresponding to the groups in ugid are given by val[minpos].

>>> val = array([ 0.0, 1.0, 2.0, 3.0, 4.0, -5.0 ])
>>> gid = array([ 2, 1, 1, 6, 6, 1 ])
>>> print groupArgmin(val,gid)
(array([1, 2, 6]), array([5, 0, 3]))
arraytools.vectorLength(vec)

Return the lengths of a set of vectors.

vec is an (n,3) shaped array holding a collection of vectors. The result is an (n,) shaped array with the length of each vector.

arraytools.vectorNormalize(vec)

Normalize a set of vectors.

vec is a (n,3) shaped arrays holding a collection of vectors. The result is a tuple of two arrays:

  • length (n): the length of the vectors vec
  • normal (n,3): unit-length vectors along vec.
arraytools.vectorPairAreaNormals(vec1, vec2)

Compute area of and normals on parallellograms formed by vec1 and vec2.

vec1 and vec2 are (n,3) shaped arrays holding collections of vectors. The result is a tuple of two arrays:

  • area (n) : the area of the parallellogram formed by vec1 and vec2.
  • normal (n,3) : (normalized) vectors normal to each couple (vec1,2).

These are calculated from the cross product of vec1 and vec2, which indeed gives area * normal.

Note that where two vectors are parallel, an area zero results and an axis with components NaN.

arraytools.vectorPairArea(vec1, vec2)

Compute area of the parallellogram formed by a vector pair vec1,vec2.

vec1 and vec2 are (n,3) shaped arrays holding collections of vectors. The result is an (n) shaped array with the area of the parallellograms formed by each pair of vectors (vec1,vec2).

arraytools.vectorPairNormals(vec1, vec2)

Compute vectors normal to vec1 and vec2.

vec1 and vec2 are (n,3) shaped arrays holding collections of vectors. The result is an (n,3) shaped array of unit length vectors normal to each couple (edg1,edg2).

arraytools.vectorTripleProduct(vec1, vec2, vec3)

Compute triple product vec1 . (vec2 x vec3).

vec1, vec2, vec3 are (n,3) shaped arrays holding collections of vectors. The result is a (n,) shaped array with the triple product of each set of corresponding vectors from vec1,vec2,vec3. This is also the square of the volume of the parallellepid formex by the 3 vectors. If vec1 is a unit normal, the result is also the area of the parallellogram (vec2,vec3) projected in the direction vec1.

arraytools.vectorPairCosAngle(v1, v2)

Return the cosinus of the angle between the vectors v1 and v2.

vec1 and vec2 are (n,3) shaped arrays holding collections of vectors. The result is an (n) shaped array with the cosinus of the angle between each pair of vectors (vec1,vec2).

arraytools.vectorPairAngle(v1, v2)

Return the angle (in radians) between the vectors v1 and v2.

vec1 and vec2 are (n,3) shaped arrays holding collections of vectors. The result is an (n) shaped array with the angle between each pair of vectors (vec1,vec2).

arraytools.histogram2(a, bins, range=None)

Compute the histogram of a set of data.

This function is a like numpy’s histogram function, but also returns the bin index for each individual entry in the data set.

Parameters:

  • a: array_like. Input data. The histogram is computed over the flattened array.
  • bins: int or sequence of scalars. If bins is an int, it defines the number of equal-width bins in the given range. If bins is a sequence, it defines the bin edges, allowing for non-uniform bin widths. Both the leftmost and rightmost edges are included, thus the number of bins is len(bins)-1.
  • range: (float, float), optional. The lower and upper range of the bins. If not provided, range is simply (a.min(), a.max()). Values outside the range are ignored. This parameter is ignored if bins is a sequence.

Returns:

  • hist: integer array with length nbins, holding the number of elements in each bin,
  • ind: a sequence of nbins integer arrays, each holding the indices of the elements fitting in the respective bins,
  • xbins: array of same type as data and with length nbins+1: returns the bin edges.

Example:

>>> histogram2([1,2,3,4,2,3,1],[1,2,3,4,5])
(array([2, 2, 2, 1]), [array([0, 6]), array([1, 4]), array([2, 5]), array([3])], array([1, 2, 3, 4, 5]))
arraytools.movingView(a, size)

Create a moving view along the first axis of an array

Parameters:

  • a : array_like: array for wihch to create a moving view
  • size : int: size of the moving view

Returns:

An array that is a view of the original array with an extra first axis of length w.

Using swapaxes(0,axis) moving views over any axis can be created.

Examples:

>>> x=arange(10).reshape((5,2))
>>> print x
[[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]
>>> print movingView(x, 3)
[[[0 1]
  [2 3]
  [4 5]]
<BLANKLINE>
 [[2 3]
  [4 5]
  [6 7]]
<BLANKLINE>
 [[4 5]
  [6 7]
  [8 9]]]

Calculate rolling sum of first axis:

>>> print movingView(x, 3).sum(axis=0)
[[ 6  9]
 [12 15]
 [18 21]]
arraytools.movingAverage(a, n, m0=None, m1=None)

Compute the moving average along the first axis of an array.

Parameters:

  • a : array_like: array to be averaged
  • n : int: moving sample size
  • m0 : optional, int: if specified, the first data set of a will be prepended this number of times
  • m1 : optional, int: if specified, the last data set of a will be appended this number of times

Returns:

An array with the moving average over n data sets along the first axis of a. The array has the same shape as a, except possibly for the length of the first axis. If neither m0 nor m1 are set, the first axis will have a length of a.shape[0] - (n-1). If both m0 and m1 are give, the first axis will have a length of a.shape[0] - (n-1) + m0 + m1. If either m0 or m1 are set and the other not, the missing value m0 or m1 will be computed thus that the return array has a first axis with length a.shape[0].

Examples:

>>> x=arange(10).reshape((5,2))
>>> print movingAverage(x,3)
[[ 2.  3.]
 [ 4.  5.]
 [ 6.  7.]]
>>> print movingAverage(x,3,2)
[[ 0.          1.        ]
 [ 0.66666667  1.66666667]
 [ 2.          3.        ]
 [ 4.          5.        ]
 [ 6.          7.        ]]
arraytools.randomNoise(shape, min=0.0, max=1.0)

Create an array with random values between min and max

pyformex-0.8.6/pyformex/doc/html/ref/image.html0000644000211500021150000004401711705104253021344 0ustar benebene00000000000000 24. image — Saving OpenGL renderings to image files. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

23. camera — OpenGL camera handling

Next topic

25. imagearray — Convert bitmap images into numpy arrays.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

24. image — Saving OpenGL renderings to image files.

This module defines some functions that can be used to save the OpenGL rendering and the pyFormex GUI to image files. There are even provisions for automatic saving to a series of files and creating a movie from these images.

Classes defined in module image

Functions defined in module image

image.initialize()

Initialize the image module.

image.imageFormats()

Return a list of the valid image formats.

image formats are lower case strings as ‘png’, ‘gif’, ‘ppm’, ‘eps’, etc. The available image formats are derived from the installed software.

image.checkImageFormat(fmt, verbose=False)

Checks image format; if verbose, warn if it is not.

Returns the image format, or None if it is not OK.

image.imageFormatFromExt(ext)

Determine the image format from an extension.

The extension may or may not have an initial dot and may be in upper or lower case. The format is equal to the extension characters in lower case. If the supplied extension is empty, the default format ‘png’ is returned.

image.save_canvas(canvas, fn, fmt='png', quality=-1, size=None)

Save the rendering on canvas as an image file.

canvas specifies the qtcanvas rendering window. fn is the name of the file fmt is the image file format

image.save_window(filename, format, quality=-1, windowname=None)

Save a window as an image file.

This function needs a filename AND format. If a window is specified, the named window is saved. Else, the main pyFormex window is saved.

image.save_main_window(filename, format, quality=-1, border=False)

Save the main pyFormex window as an image file.

This function needs a filename AND format. This is an alternative for save_window, by grabbin it from the root window, using save_rect. This allows us to grab the border as well.

image.save_rect(x, y, w, h, filename, format, quality=-1)

Save a rectangular part of the screen to a an image file.

image.save(filename=None, window=False, multi=False, hotkey=True, autosave=False, border=False, rootcrop=False, format=None, quality=-1, size=None, verbose=False)

Saves an image to file or Starts/stops multisave mode.

With a filename and multi==False (default), the current viewport rendering is saved to the named file.

With a filename and multi==True, multisave mode is started. Without a filename, multisave mode is turned off. Two subsequent calls starting multisave mode without an intermediate call to turn it off, do not cause an error. The first multisave mode will implicitely be ended before starting the second.

In multisave mode, each call to saveNext() will save an image to the next generated file name. Filenames are generated by incrementing a numeric part of the name. If the supplied filename (after removing the extension) has a trailing numeric part, subsequent images will be numbered continuing from this number. Otherwise a numeric part ‘-000’ will be added to the filename.

If window is True, the full pyFormex window is saved. If window and border are True, the window decorations will be included. If window is False, only the current canvas viewport is saved.

If hotkey is True, a new image will be saved by hitting the ‘S’ key. If autosave is True, a new image will be saved on each execution of the ‘draw’ function. If neither hotkey nor autosave are True, images can only be saved by executing the saveNext() function from a script.

If no format is specified, it is derived from the filename extension. fmt should be one of the valid formats as returned by imageFormats()

If verbose=True, error/warnings are activated. This is usually done when this function is called from the GUI.

image.saveImage(filename=None, window=False, multi=False, hotkey=True, autosave=False, border=False, rootcrop=False, format=None, quality=-1, size=None, verbose=False)

Saves an image to file or Starts/stops multisave mode.

With a filename and multi==False (default), the current viewport rendering is saved to the named file.

With a filename and multi==True, multisave mode is started. Without a filename, multisave mode is turned off. Two subsequent calls starting multisave mode without an intermediate call to turn it off, do not cause an error. The first multisave mode will implicitely be ended before starting the second.

In multisave mode, each call to saveNext() will save an image to the next generated file name. Filenames are generated by incrementing a numeric part of the name. If the supplied filename (after removing the extension) has a trailing numeric part, subsequent images will be numbered continuing from this number. Otherwise a numeric part ‘-000’ will be added to the filename.

If window is True, the full pyFormex window is saved. If window and border are True, the window decorations will be included. If window is False, only the current canvas viewport is saved.

If hotkey is True, a new image will be saved by hitting the ‘S’ key. If autosave is True, a new image will be saved on each execution of the ‘draw’ function. If neither hotkey nor autosave are True, images can only be saved by executing the saveNext() function from a script.

If no format is specified, it is derived from the filename extension. fmt should be one of the valid formats as returned by imageFormats()

If verbose=True, error/warnings are activated. This is usually done when this function is called from the GUI.

image.saveNext()

In multisave mode, saves the next image.

This is a quiet function that does nothing if multisave was not activated. It can thus safely be called on regular places in scripts where one would like to have a saved image and then either activate the multisave mode or not.

image.saveIcon(fn, size=32)

Save the current rendering as an icon.

image.autoSaveOn()

Returns True if autosave multisave mode is currently on.

Use this function instead of directly accessing the autosave variable.

image.createMovie(encoder='ffmpeg')

Create a movie from a saved sequence of images.

encoder is one of: ‘ffmpeg, mencoder, convert’

image.saveMovie(filename, format, windowname=None)

Create a movie from the pyFormex window.

pyformex-0.8.6/pyformex/doc/html/ref/menu.html0000644000211500021150000007010211705104253021220 0ustar benebene00000000000000 15. menu — Menus for the pyFormex GUI. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

14. widgets — A collection of custom widgets used in the pyFormex GUI

Next topic

16. colorscale — Color mapping of a range of values.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

make[2]: Map ‘/home/bene/prj’ wordt binnengegaan make[2]: Map ‘/home/bene/prj’ wordt verlaten .. $Id$ -- rst -- .. pyformex reference manual — menu .. CREATED WITH py2rst.py: DO NOT EDIT

15. menu — Menus for the pyFormex GUI.

This modules implements specialized classes and functions for building the pyFormex GUI menu system.

Classes defined in module menu

A general menu class.

A hierarchical menu that keeps a list of its item names and actions. The item names are normalized by removing all ‘&’ characters and converting the result to lower case. It thus becomes easy to search for an existing item in a menu.

This class is not intended for direct use, but through subclasses. Subclasses should implement at least the following methods:

  • addSeparator()
  • insertSeperator(before)
  • addAction(text,action)
  • insertAction(before,text,action)
  • addMenu(text,menu)
  • insertMenu(before,text,menu)

QtGui.Menu and QtGui.MenuBar provide these methods.

Return a list with the current actions.

Return the index of the specified item in the actionlist.

If the requested item is not in the actionlist, -1 is returned.

Return the action with specified text.

First, a normal action is tried. If none is found, a separator is tried.

Return the item with specified text.

For a normal action or a separator, an qction is returned. For a menu action, a menu is returned.

Returns the name of the next item.

This can be used to replace the current item with another menu. If the item is the last, None is returned.

Remove an item from this menu.

Create and insert a separator

Insert an existing menu.

Insert an action.

Create and insert an action.

Insert a list of items in the menu.

Parameters:

  • items: a list of menuitem tuples. Each item is a tuple of two or three elements: (text, action, options):
    • text: the text that will be displayed in the menu item. It is stored in a normalized way: all lower case and with ‘&’ removed.
    • action: can be any of the following:
      • a Python function or instance method : it will be called when the item is selected,
      • a string with the name of a function/method,
      • a list of Menu Items: a popup Menu will be created that will appear when the item is selected,
      • an existing Menu,
      • None : this will create a separator item with no action.
    • options: optional dictionary with following honoured fields:
      • icon: the name of an icon to be displayed with the item text. This name should be that of one of the icons in the pyFormex icondir.
      • shortcut: is an optional key combination to select the item.
      • tooltip: a text that is displayed as popup help.
  • before: if specified, should be the text or the action of one of the items in the Menu (not the items list!): the new list of items will be inserted before the specified item.

A popup/pulldown menu.

Return a list with the current actions.

Return the index of the specified item in the actionlist.

If the requested item is not in the actionlist, -1 is returned.

Return the action with specified text.

First, a normal action is tried. If none is found, a separator is tried.

Return the item with specified text.

For a normal action or a separator, an qction is returned. For a menu action, a menu is returned.

Returns the name of the next item.

This can be used to replace the current item with another menu. If the item is the last, None is returned.

Remove an item from this menu.

Create and insert a separator

Insert an existing menu.

Insert an action.

Create and insert an action.

Insert a list of items in the menu.

Parameters:

  • items: a list of menuitem tuples. Each item is a tuple of two or three elements: (text, action, options):
    • text: the text that will be displayed in the menu item. It is stored in a normalized way: all lower case and with ‘&’ removed.
    • action: can be any of the following:
      • a Python function or instance method : it will be called when the item is selected,
      • a string with the name of a function/method,
      • a list of Menu Items: a popup Menu will be created that will appear when the item is selected,
      • an existing Menu,
      • None : this will create a separator item with no action.
    • options: optional dictionary with following honoured fields:
      • icon: the name of an icon to be displayed with the item text. This name should be that of one of the icons in the pyFormex icondir.
      • shortcut: is an optional key combination to select the item.
      • tooltip: a text that is displayed as popup help.
  • before: if specified, should be the text or the action of one of the items in the Menu (not the items list!): the new list of items will be inserted before the specified item.

A menu bar allowing easy menu creation.

Return a list with the current actions.

Return the index of the specified item in the actionlist.

If the requested item is not in the actionlist, -1 is returned.

Return the action with specified text.

First, a normal action is tried. If none is found, a separator is tried.

Return the item with specified text.

For a normal action or a separator, an qction is returned. For a menu action, a menu is returned.

Returns the name of the next item.

This can be used to replace the current item with another menu. If the item is the last, None is returned.

Remove an item from this menu.

Create and insert a separator

Insert an existing menu.

Insert an action.

Create and insert an action.

Insert a list of items in the menu.

Parameters:

  • items: a list of menuitem tuples. Each item is a tuple of two or three elements: (text, action, options):
    • text: the text that will be displayed in the menu item. It is stored in a normalized way: all lower case and with ‘&’ removed.
    • action: can be any of the following:
      • a Python function or instance method : it will be called when the item is selected,
      • a string with the name of a function/method,
      • a list of Menu Items: a popup Menu will be created that will appear when the item is selected,
      • an existing Menu,
      • None : this will create a separator item with no action.
    • options: optional dictionary with following honoured fields:
      • icon: the name of an icon to be displayed with the item text. This name should be that of one of the icons in the pyFormex icondir.
      • shortcut: is an optional key combination to select the item.
      • tooltip: a text that is displayed as popup help.
  • before: if specified, should be the text or the action of one of the items in the Menu (not the items list!): the new list of items will be inserted before the specified item.

A DAction is a QAction that emits a signal with a string parameter.

When triggered, this action sends a signal (default ‘Clicked’) with a custom string as parameter. The connected slot can then act depending on this parameter.

Menu and toolbar with named actions.

An action list is a list of strings, each connected to some action. The actions can be presented in a menu and/or a toolbar. On activating one of the menu or toolbar buttons, a given signal is emitted with the button string as parameter. A fixed function can be connected to this signal to act dependent on the string value.

Add a new name to the actions list and create a matching DAction.

If the actions list has an associated menu or toolbar, a matching button will be inserted in each of these. If an icon is specified, it will be used on the menu and toolbar. The icon is either a filename or a QIcon object. If text is specified, it is displayed instead of the action’s name.

Return an ordered list of names of the action items.

Functions defined in module menu

Reset the GUI to its default operating mode.

When an exception is raised during the execution of a script, the GUI may be left in a non-consistent state. This function may be called to reset most of the GUI components to their default operating mode.

Reset the warning filters to the default.

Returns the default pyFormex GUI menu data.

pyformex-0.8.6/pyformex/doc/html/ref/imagecolor.html0000644000211500021150000002132711705104253022402 0ustar benebene00000000000000 26. imagecolor — Using bitmap images as colors. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

25. imagearray — Convert bitmap images into numpy arrays.

Next topic

27. imageViewer — A general image viewer

[FSF Associate Member]

Valid XHTML 1.0 Transitional

26. imagecolor — Using bitmap images as colors.

This module contains functions to use bitmap images as colors on a pyFormex geometry.

Classes defined in module imagecolor

Functions defined in module imagecolor

imagecolor.image2glcolor(im, flip=True)

Convert a bitmap image to corresponding OpenGL colors.

im is a QImage or any data from which a QImage can be initialized. The image RGB colors are converted to OpenGL colors. The return value is a (w,h,3) shaped array of values in the range 0.0 to 1.0. By default the image is flipped upside-down because the vertical OpenGL axis points upwards, while bitmap images are stored downwards.

pyformex-0.8.6/pyformex/doc/html/ref/datareader.html0000644000211500021150000002625111705104251022354 0ustar benebene00000000000000 39. datareader — Numerical data reader — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

38. units — A Python wrapper for unit conversion of physical quantities.

Next topic

40. properties — General framework for attributing properties to geometrical elements.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

39. datareader — Numerical data reader

Classes defined in module datareader

Functions defined in module datareader

datareader.splitFloat(s)

Match a floating point number at the beginning of a string

If the beginning of the string matches a floating point number, a list is returned with the float and the remainder of the string; if not, None is returned. Example: splitFloat('123e4rt345e6') returns [1230000.0, 'rt345e6']

datareader.readData(s, type, strict=False)

Read data from a line matching the ‘type’ specification.

This is a powerful function for reading, interpreting and converting numerical data from a string. Fields in the string s are separated by commas. The ‘type’ argument is a list where each element specifies how the corresponding field should be interpreted. Available values are ‘int’, ‘float’ or some unit (‘kg’, ‘m’, etc.). If the type field is ‘int’ or ‘float’, the data field is converted to the matching type. If the type field is a unit, the data field should be a number and a unit separated by space or not, or just a number. If it is just a number, its value is returned unchanged (as float). If the data contains a unit, the number is converted to the requested unit. It is an error if the datafield holds a non-conformable unit. The function returns a list of ints and/or floats (without the units). If the number of data fields is not equal to the number of type specifiers, the returned list will correspond to the shortest of both and the surplus data or types are ignored, UNLESS the strict flag has been set, in which case a RuntimError is raised. Example:

readData('12, 13, 14.5e3, 12 inch, 1hr, 31kg ', ['int','float','kg','cm','s'])

returns [12, 13.0, 14500.0, 30.48, 3600.0]

..warning

You need to have the GNU ``units`` command installed for the unit
conversion to work. 
pyformex-0.8.6/pyformex/doc/html/ref/section2d.html0000644000211500021150000002657311705104255022165 0ustar benebene00000000000000 36. section2d — Some functions operating on 2D structures. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

35. isopar — Isoparametric transformations

Next topic

37. inertia — inertia.py

[FSF Associate Member]

Valid XHTML 1.0 Transitional

36. section2d — Some functions operating on 2D structures.

This is a plugin for pyFormex. (C) 2002 Benedict Verhegghe

See the Section2D example for an example of its use.

Classes defined in module section2d

class section2d.PlaneSection(F)

A class describing a general 2D section.

The 2D section is the area inside a closed curve in the (x,y) plane. The curve is decribed by a finite number of points and by straight segments connecting them.

Functions defined in module section2d

section2d.sectionChar(F)

Compute characteristics of plane sections.

The plane sections are described by their circumference, consisting of a sequence of straight segments. The segment end point data are gathered in a plex-2 Formex. The segments should form a closed curve. The z-value of the coordinates does not have to be specified, and will be ignored if it is. The resulting path through the points should rotate positively around the z axis to yield a positive surface.

The return value is a dict with the following characteristics:

  • L : circumference,
  • A : enclosed surface,
  • Sx : first area moment around global x-axis
  • Sy : first area moment around global y-axis
  • Ixx : second area moment around global x-axis
  • Iyy : second area moment around global y-axis
  • Ixy : product moment of area around global x,y-axes
section2d.extendedSectionChar(S)

Computes extended section characteristics for the given section.

S is a dict with section basic section characteristics as returned by sectionChar(). This function computes and returns a dict with the following:

  • xG, yG : coordinates of the center of gravity G of the plane section
  • IGxx, IGyy, IGxy : second area moments and product around axes through G and parallel with the global x,y-axes
  • alpha : angle(in radians) between the global x,y axes and the principal axes (X,Y) of the section (X and Y always pass through G)
  • IXX, IYY : principal second area moments around X,Y respectively. (The second area product is always zero.)
section2d.princTensor2D(Ixx, Iyy, Ixy)

Compute the principal values and directions of a 2D tensor.

Returns a tuple with three values:

  • alpha : angle (in radians) from x-axis to principal X-axis
  • IXX,IYY : principal values of the tensor
pyformex-0.8.6/pyformex/doc/html/ref/connectivity.html0000644000211500021150000020030411705104247022774 0ustar benebene00000000000000 8. connectivity — A class and functions for handling nodal connectivity. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

7. geometry — A generic interface to the Coords transformation methods

Next topic

9. simple — Predefined geometries with a simple shape.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

8. connectivity — A class and functions for handling nodal connectivity.

This module defines a specialized array class for representing nodal connectivity. This is e.g. used in mesh models, where geometry is represented by a set of numbered points (nodes) and the geometric elements are described by refering to the node numbers. In a mesh model, points common to adjacent elements are unique, and adjacency of elements can easily be detected from common node numbers.

Classes defined in module connectivity

class connectivity.Connectivity

A class for handling element/node connectivity.

A connectivity object is a 2-dimensional integer array with all non-negative values. Each row of the array defines an element by listing the numbers of its lower entity types. A typical use is a Mesh object, where each element is defined in function of its nodes. While in a Mesh the word ‘node’ will normally refer to a geometrical point, here we will use ‘node’ for the lower entity whatever its nature is. It doesn’t even have to be a geometrical entity.

The current implementation limits a Connectivity object to numbers that are smaller than 2**31.

In a row (element), the same node number may occur more than once, though usually all numbers in a row are different. Rows containing duplicate numbers are called degenerate elements. Rows containing the same node sets, albeit different permutations thereof, are called duplicates.

A new Connectivity object is created with the following syntax

Connectivity(data=[],dtyp=None,copy=False,nplex=0)

Parameters:

  • data: should be compatible with an integer array with shape (nelems,nplex), where nelems is the number of elements and nplex is the plexitude of the elements.
  • dtype: can be specified to force an integer type but is set by default from the passed data.
  • copy: can be set True to force copying the data. By default, the specified data will be used without copying, if possible.
  • nplex: can be specified to force a check on the plexitude of the data, or to set the plexitude for an empty Connectivity. An error will be raised if the specified data do not match the specified plexitude.

Example:

>>> print Connectivity([[0,1,2],[0,1,3],[0,3,2],[0,5,3]])
[[0 1 2]
 [0 1 3]
 [0 3 2]
 [0 5 3]]
nelems()

Return the number of elements in the Connectivity table.

Example:

>>> Connectivity([[0,1,2],[0,1,3],[0,3,2],[0,5,3]]).nelems()
4
nplex()

Return the plexitude of the elements in the Connectivity table.

Example:

>>> Connectivity([[0,1,2],[0,1,3],[0,3,2],[0,5,3]]).nplex()
3
report()

Format a Connectivity table

testDegenerate()

Flag the degenerate elements (rows).

A degenerate element is a row which contains at least two equal values.

Returns: a boolean array with shape (self.nelems(),).
The True values flag the degenerate rows.

Example:

>>> Connectivity([[0,1,2],[0,1,1],[0,3,2]]).testDegenerate()
array([False,  True, False], dtype=bool)
listDegenerate()

Return a list with the numbers of the degenerate elements.

Example:

>>> Connectivity([[0,1,2],[0,1,1],[0,3,2]]).listDegenerate()
array([1])
listNonDegenerate()

Return a list with the numbers of the non-degenerate elements.

Example:

>>> Connectivity([[0,1,2],[0,1,1],[0,3,2]]).listNonDegenerate()
array([0, 2])
removeDegenerate()

Remove the degenerate elements from a Connectivity table.

Degenerate elements are rows with repeating values. Returns a Connectivity with the degenerate elements removed.

Example:

>>> Connectivity([[0,1,2],[0,1,1],[0,3,2]]).removeDegenerate()
Connectivity([[0, 1, 2],
       [0, 3, 2]])
reduceDegenerate(target=None)

Reduce degenerate elements to lower plexitude elements.

This will try to reduce the degenerate elements of the Connectivity to a lower plexitude. This is only possible if an element type was set in the Connectivity. This function uses the data of the Element database in elements.

If a target element type is given, only the reductions to that element type are performed. Else, all the target element types for which a reduction scheme is available, will be tried.

Returns:

A list of Connectivities of which the first one contains the originally non-degenerate elements and the last one contains the elements that could not be reduced and may be empty. If the original Connectivity does not have an element type set, or the element type does not have any reduction schemes defined, a list with only the original is returned.

Remark: If the Connectivity is part of a Mesh, you should use the Mesh.reduceDegenerate method instead, as that one will preserve the property numbers into the resulting Meshes.

Example:

>>> C = Connectivity([[0,1,2],[0,1,1],[0,3,2]],eltype='line3')
>>> print C.reduceDegenerate()
[Connectivity([[0, 1]]), Connectivity([[0, 1, 2],
       [0, 3, 2]])]
testDuplicate(permutations=True)

Test the Connectivity list for duplicates.

By default, duplicates are elements that consist of the same set of nodes, in any particular order. Setting permutations to False will only find the duplicate rows that have matching values at every position.

This function returns a tuple with two arrays:

  • an index used to sort the elements
  • a flags array with the value True for indices of the unique elements and False for those of the duplicates.

Example:

>>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).testDuplicate()
(array([0, 1, 2]), Connectivity([ True, False,  True], dtype=bool))
listUnique(permutations=True)

Return a list with the numbers of the unique elements.

Example:

>>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).listUnique()
array([0, 2])
listDuplicate(permutations=True)

Return a list with the numbers of the duplicate elements.

Example:

>>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).listDuplicate()
array([1])
removeDuplicate(permutations=True)

Remove duplicate elements from a Connectivity list.

By default, duplicates are elements that consist of the same set of nodes, in any particular order. Setting permutations to False will only remove the duplicate rows that have matching values at matching positions.

Returns a new Connectivity with the duplicate elements removed.

Example:

>>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).removeDuplicate()
Connectivity([[0, 1, 2],
       [0, 3, 2]])
reorder(order='nodes')

Reorder the elements of a Connectivity in a specified order.

This does not actually reorder the elements itself, but returns an index with the order of the rows (elements) in the connectivity table that meets the specified requirements.

Parameters:

  • order: specifies how to reorder the elements. It is either one of the special string values defined below, or else it is an index with length equal to the number of elements. The index should be a permutation of the numbers in range(self.nelems(). Each value gives of the number of the old element that should be placed at this position. Thus, the order values are the old element numbers on the position of the new element number.

    order can also take one of the following predefined values, resulting in the corresponding renumbering scheme being generated:

    • ‘nodes’: the elements are renumbered in order of their appearance in the inverse index, i.e. first are the elements connected to node 0, then the as yet unlisted elements connected to node 1, etc.
    • ‘random’: the elements are randomly renumbered.
    • ‘reverse’: the elements are renumbered in reverse order.
Returns:
A 1-D integer array which is a permutation of arange(self.nelems(), such that taking the elements in this order will produce a Connectivity reordered as requested. In case an explicit order was specified as input, this order is returned after checking that it is indeed a permutation of range(self.nelems().

Example:

>>> A = Connectivity([[1,2],[2,3],[3,0],[0,1]])
>>> A[A.reorder('reverse')]
Connectivity([[0, 1],
       [3, 0],
       [2, 3],
       [1, 2]])
>>> A.reorder('nodes')
array([3, 2, 0, 1])
>>> A[A.reorder([2,3,0,1])]
Connectivity([[3, 0],
       [0, 1],
       [1, 2],
       [2, 3]])
inverse()

Return the inverse index of a Connectivity table.

This returns the inverse index of the Connectivity, as computed by arraytools.inverseIndex(). See

Example:

>>> Connectivity([[0,1,2],[0,1,3],[0,3,2]]).inverse()
array([[ 0,  1,  2],
       [-1,  0,  1],
       [-1,  0,  2],
       [-1,  1,  2]])
adjacency(kind='e')

Return a table of adjacent items.

Returns an element adjacency table (kind=’e’) or node adjacency table (kind=’n’).

An element i is said to be ajacent to element j, if the two elements have at least one common node.

A node i is said to be adjacent to node j, if there is at least one element containing both nodes.

Parameters:

  • kind: ‘e’ or ‘n’, requesting resp. element or node adjacency.

Returns: an integer array with shape (nr,nc), where row i holds a sorted list of all the items that are adjacent to item i, padded with -1 values to create an equal list length for all items.

Example:

>>> Connectivity([[0,1],[0,2],[1,3],[0,5]]).adjacency('e')
array([[ 1,  2,  3],
       [-1,  0,  3],
       [-1, -1,  0],
       [-1,  0,  1]])
>>> Connectivity([[0,1],[0,2],[1,3],[0,5]]).adjacency('n')
array([[ 1,  2,  5],
       [-1,  0,  3],
       [-1, -1,  0],
       [-1, -1,  1],
       [-1, -1, -1],
       [-1, -1,  0]])
>>> Connectivity([[0,1,2],[0,1,3],[2,4,5]]).adjacency('n')
array([[-1,  1,  2,  3],
       [-1,  0,  2,  3],
       [ 0,  1,  4,  5],
       [-1, -1,  0,  1],
       [-1, -1,  2,  5],
       [-1, -1,  2,  4]])
selectNodes(selector)

Return a Connectivity containing subsets of the nodes.

Parameters:

  • selector: an object that can be converted to a 1-dim or 2-dim int array. Examples are a tuple of local node numbers, or a list of such tuples all having the same length. Each row of selector holds a list of the local node numbers that should be retained in the new Connectivity table.

Returns a Connectivity object with shape (self.nelems*selector.nelems,selector.nplex). This function does not collapse the duplicate elements. The eltype of the result is equal to that of the selector, possibly None.

Example:

>>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).selectNodes([[0,1],[0,2]])
Connectivity([[0, 1],
       [0, 2],
       [0, 2],
       [0, 1],
       [0, 3],
       [0, 2]])
insertLevel(selector, lower_only=False)

Insert an extra hierarchical level in a Connectivity table.

A Connectivity table identifies higher hierchical entities in function of lower ones. This method inserts an extra hierarchical level. For example, if you have volumes defined in function of points, you can insert an intermediate level of edges, or faces. Multiple intermediate level entities may be created from each element.

Parameters:

  • selector: an object that can be converted to a 1-dim or 2-dim integer array. Examples are a tuple of local node numbers, or a list of such tuples all having the same length. Each row of selector holds a list of the local node numbers that should be retained in the new Connectivity table.
  • lower_only: if True, only the definition of the new (lower) entities is returned, complete without removing duplicates. This is equivalent to using selectNodes(), which is prefered when you do not need the higher level info.

Return value: a tuple of two Connectivities hi,`lo`, where:

  • hi: defines the original elements in function of the intermediate level ones,
  • lo: defines the intermediate level items in function of the lowest level ones (the original nodes). If the selector has an eltype attribute, then lo will inherit the same eltype value.

Intermediate level items that consist of the same items in any permutation order are collapsed to single items. The low level items respect the numbering order inside the original elements, but it is undefined which of the collapsed sequences is returned.

Because the precise order of the data in the collapsed rows is lost, it is in general not possible to restore the exact original table from the two result tables. See however Mesh.getBorder() for an application where an inverse operation is possible, because the border only contains unique rows. See also Mesh.combine(), which is an almost inverse operation for the general case, if the selector is complete. The resulting rows may however be permutations of the original.

Example:

>>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).insertLevel([[0,1],[1,2],[2,0]])
(Connectivity([[0, 3, 1],
       [1, 3, 0],
       [2, 4, 1]]), Connectivity([[0, 1],
       [2, 0],
       [0, 3],
       [1, 2],
       [3, 2]]))
combine(lo)

Combine two hierarchical Connectivity levels to a single one.

self and lo are two hierarchical Connectivity tables, representing higher and lower level respectively. This means that the elements of self hold numbers which point into lo to obtain the lowest level items.

In the current implementation, the plexitude of lo should be 2!

As an example, in a structure of triangles, hi could represent triangles defined by 3 edges and lo could represent edges defined by 2 vertices. This method will then result in a table with plexitude 3 defining the triangles in function of the vertices.

This is the inverse operation of insertLevel() with a selector which is complete. The algorithm only works if all vertex numbers of an element are unique.

Example:

>>> hi,lo = Connectivity([[0,1,2],[0,2,1],[0,3,2]]).insertLevel([[0,1],[1,2],[2,0]])
>>> hi.combine(lo)
Connectivity([[0, 1, 2],
       [0, 2, 1],
       [0, 3, 2]])
resolve()

Resolve the connectivity into plex-2 connections.

Creates a Connectivity table with a plex-2 (edge) connection between any two nodes that are connected to a common element.

There is no point in resolving a plexitude 2 structure. Plexitudes lower than 2 can not be resolved.

Returns a plex-2 Connectivity with all connections between node pairs. In each element the nodes are sorted.

Example:

>>> print([ i for i in combinations(range(3),2) ])
[(0, 1), (0, 2), (1, 2)]
>>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).resolve()
Connectivity([[0, 1],
       [0, 2],
       [0, 3],
       [1, 2],
       [2, 3]])

Functions defined in module connectivity

connectivity.sortAdjacency(adj)

Sort an adjacency table.

An adjacency table is an integer array where each row lists the numbers of the items that are connected to the item with number equal to the row index. Rows are padded with -1 value to create rows of equal length.

This function sorts the rows of the adjacency table in ascending order and removes all columns containing only -1 values.

Paramaters:

  • adj: an 2-D integer array with values >=0 or -1

Returns: an integer array with shape (adj.shape[0],maxc), with maxc <= adj.shape[1], where the rows are sorted in ascending order and where columns with only -1 values are removed.

Example:

>>> a = array([[ 0,  2,  1, -1],
...            [-1,  3,  1, -1],
...            [ 3, -1,  0,  1],
...            [-1, -1, -1, -1]])
>>> sortAdjacency(a)
array([[ 0,  1,  2],
       [-1,  1,  3],
       [ 0,  1,  3],
       [-1, -1, -1]])
connectivity.reduceAdjacency(adj)

Reduce an adjacency table.

An adjacency table is an integer array where each row lists the numbers of the items that are connected to the item with number equal to the row index. Rows are padded with -1 value to create rows of equal length.

A reduced adjacency table is one where each row:

  • does not contain the row index itself,
  • does not contain duplicates,
  • is sorted in ascending order,

and that has at least one row without -1 value.

Paramaters:

  • adj: an 2-D integer array with value >=0 or -1

Returns: an integer array with shape (adj.shape[0],maxc), with maxc <= adj.shape[1], where row i retains the unique non-negative numbers of the original array except the valu i, and is possibly padded with -1 values.

Example:

>>> a = array([[ 0,  0,  0,  1,  2,  5],
...            [-1,  0,  1, -1,  1,  3],
...            [-1, -1,  0, -1, -1,  2],
...            [-1, -1,  1, -1, -1,  3],
...            [-1, -1, -1, -1, -1, -1],
...            [-1, -1,  0, -1, -1,  5]])
>>> reduceAdjacency(a)
array([[ 1,  2,  5],
       [-1,  0,  3],
       [-1, -1,  0],
       [-1, -1,  1],
       [-1, -1, -1],
       [-1, -1,  0]])
connectivity.findConnectedLineElems(elems)

Find a single path of connected line elems.

This function is intended as a helper function for connectedLineElems(). It should probably not be used directly, because, as a side-effect, it changes the data in the elems argument. connectedLineElems() does not have this inconvenience.

The function searches a Connectivity table for a chain of elements in which the first node of all but the first element is equal to the last node of the previous element. To create such a chain, elements may be reordered and the node sequence of an element may be reversed.

Parameters:

  • elems: Connectivity-like. Any plexitude is allowed, but only the first and the last columna are relevant.
Returns: a Connectivity with a single chain extracted from the input

Connectivity. The result will not necessarily be the longest path. It will however contain the first element of the input table.

As a side-effect, all elements contained in the output chain will have their entries in the input table elems changed to -1.

Example:

>>> findConnectedLineElems([[0,1],[1,2],[0,4],[4,2]])
Connectivity([[0, 1],
       [1, 2],
       [2, 4],
       [4, 0]])
>>> findConnectedLineElems([[0,1],[1,2],[0,4]])
Connectivity([[2, 1],
       [1, 0],
       [0, 4]])
>>> C = Connectivity([[0,1],[0,2],[0,3],[4,5]])
>>> findConnectedLineElems(C)
Connectivity([[ 1,  0],
       [ 0,  2],
       [-1, -1],
       [-1, -1]])
>>> print C
[[-1 -1]
 [-1 -1]
 [ 0  3]
 [ 4  5]]
connectivity.connectedLineElems(elems)

Partition a segmented curve into connected segments.

The input argument is a (nelems,2) shaped array of integers. Each row holds the two vertex numbers of a single line segment.

The return value is a list of (nsegi,2) shaped array of integers.

Example:

>>> connectedLineElems([[0,1],[1,2],[0,4],[4,2]])
[Connectivity([[0, 1],
       [1, 2],
       [2, 4],
       [4, 0]])]
>>> connectedLineElems([[0,1],[1,2],[0,4]])
[Connectivity([[2, 1],
       [1, 0],
       [0, 4]])]
>>> connectedLineElems([[0,1],[0,2],[0,3],[4,5]])
[Connectivity([[1, 0],
       [0, 2]]), Connectivity([[0, 3]]), Connectivity([[4, 5]])]
>>> connectedLineElems([[0,1,2],[2,0,3],[0,3,1],[4,5,2]])
[Connectivity([[3, 0, 2],
       [2, 1, 0],
       [0, 3, 1]]), Connectivity([[4, 5, 2]])]
connectivity.adjacencyArrays(elems, nsteps=1)

Create adjacency arrays for 2-node elements.

elems is a (nr,2) shaped integer array. The result is a list of adjacency arrays, where row i of adjacency array j holds a sorted list of the nodes that are connected to node i via a shortest path of j elements, padded with -1 values to create an equal list length for all nodes. This is: [adj0, adj1, ..., adjj, ... , adjn] with n=nsteps.

Example:

>>> adjacencyArrays([[0,1],[1,2],[2,3],[3,4],[4,0]],3)
[array([[0],
       [1],
       [2],
       [3],
       [4]]), array([[1, 4],
       [0, 2],
       [1, 3],
       [2, 4],
       [0, 3]]), array([[2, 3],
       [3, 4],
       [0, 4],
       [0, 1],
       [1, 2]]), array([], shape=(5, 0), dtype=int64)]
pyformex-0.8.6/pyformex/doc/html/ref/widgets.html0000644000211500021150000030625211705104256021735 0ustar benebene00000000000000 14. widgets — A collection of custom widgets used in the pyFormex GUI — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

13. mesh — Finite element meshes in pyFormex.

Next topic

15. menu — Menus for the pyFormex GUI.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

14. widgets — A collection of custom widgets used in the pyFormex GUI

The widgets in this module were primarily created in function of the pyFormex GUI. The user can apply them to change the GUI or to add interactive widgets to his scripts. Of course he can also use all the Qt widgets directly.

Classes defined in module widgets

class widgets.InputItem(name, *args, **kargs)

A single input item.

This is the base class for widgets holding a single input item. A single input item is any item that is treated as a unit and refered to by a single name.

This base class is rarely used directly. Most of the components of an InputDialog are subclasses of hereof, each specialized in some form of input data or representation. There is e.g. an InputInteger class to input an integer number and an InputString for the input of a string. The base class groups the functionality that is common to the different input widgets.

The InputItem widget holds a horizontal layout box (QHBoxLayout) to group its its components. In most cases there are just two components: a label with the name of the field, and the actual input field. Other components, such as buttons or sliders, may be added. This is often done in subclasses.

The constructor has one required argument: name. Other (optional) positional parameters are passed to the QtGui.QWidget constructor. The remaining keyword parameters are options that somehow change the default behavior of the InputItem class.

Parameters:

  • name: the name used to identify the item. It should be unique for all InputItems in the same InputDialog. It will be used as a key in the dictionary that returns all the input values in the dialog. It will also be used as the label to display in front of the input field, in case no text value was specified.
  • text: if specified, this text will be displayed in the label in front of the input field. This allows for showing descriptive texts for the input fields in the dialog, while keeping short and simple names for the items in the programming. text can be set to an empty string to suppress the creation of a label in front of the input field. This is useful if the input field widget itself already provides a label (see e.g. InputBool). text can also be a QtGui.QPixmap, allowing for icons to be used as labels.
  • buttons: a list of (label,function) tuples. For each tuple a button will be added after the input field. The button displays the text and when pressed, the specified function will be executed. The function takes no arguments.
  • data: any extra data that you want to be stored into the widget. These data are not displayed, but can be useful in the functioning of the widget.
  • enabled: boolean. If False, the InputItem will not be enabled, meaning that the user can not enter any values there. Disabled fields are usually displayed in a greyed-out fashion.
  • readonly: boolean. If True, the data are read-only and can not be changed by the user. Unlike disabled items, they are displayed in a normal fashion.
  • tooltip: A descriptive text which is only shown when the user pauses the cursor for some time on the widget. It can be used to give more comprehensive explanation to first time users.

Subclasses should have an __init__() method which first constructs a proper widget for the input field, and stores it in the attribute self.input. Then the baseclass should be properly initialized, passing any optional parameters:

self.input = SomeInputWidget()
InputItem.__init__(self,name,*args,**kargs)

Subclasses should also override the following default methods of the InputItem base class:

  • text(): if the subclass calls the superclass __init__() method with a value text=''. This method should return the value of the displayed text.
  • value(): if the value of the input field is not given by self.input.text(), i.e. in most cases. This method should return the value of the input field.
  • setValue(val): always, unless the field is readonly. This method should change the value of the input widget to the specified value.

Subclasses are allowed to NOT have a self.input attribute, IFF they redefine both the value() and the setValue() methods.

Subclasses can set validators on the input, like:

self.input.setValidator(QtGui.QIntValidator(self.input))

Subclasses can define a show() method e.g. to select the data in the input field on display of the dialog.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

class widgets.InputInfo(name, value, *args, **kargs)

An unchangeable input field with a label in front.

It is just like an InputString, but the text can not be edited. The value should be a simple string without newlines.

There are no specific options.

value()

Return the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

setValue(val)

Change the widget’s value.

class widgets.InputLabel(name, value, *args, **kargs)

An unchangeable information field.

The value is displayed as a string, but may contain more complex texts.

By default, the text format will be guessed to be either plain text, ReStructuredText ot html. Specify plain=True to display in plain text.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

value()

Return the widget’s value.

class widgets.InputString(name, value, max=None, *args, **kargs)

A string input field with a label in front.

If the type of value is not a string, the input string will be eval’ed before returning.

Options:

  • max: the maximum number of characters in the string.
show()

Select all text on first display.

value()

Return the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

setValue(val)

Change the widget’s value.

class widgets.InputText(name, value, *args, **kargs)

A scrollable text input field with a label in front.

By default, the text format will be guessed to be either plain text, ReStructuredText ot html.

Specify plain=True to display in plain text.

If the type of value is not a string, the input text will be eval’ed before returning.

show()

Select all text on first display.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

class widgets.InputBool(name, value, *args, **kargs)

A boolean input item.

Creates a new checkbox for the input of a boolean value.

Displays the name next to a checkbox, which will initially be set if value evaluates to True. (Does not use the label) The value is either True or False,depending on the setting of the checkbox.

text()

Return the displayed text.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

class widgets.InputList(name, default=[], choices=[], sort=False, single=False, check=False, fast_sel=False, *args, **kargs)

A list selection InputItem.

A list selection is a widget allowing the selection of zero, one or more items from a list.

choices is a list/tuple of possible values. default is the initial/default list of selected items. Values in default that are not in the choices list, are ignored. If default is None or an empty list, nothing is selected initially.

By default, the user can select multiple items and the return value is a list of all currently slected items. If single is True, only a single item can be selected.

If check is True, all items have a checkbox and only the checked items are returned. This option sets single==False.

setSelected(selected, flag=True)

Mark the specified items as selected or not.

setChecked(selected, flag=True)

Mark the specified items as checked or not.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

setAll()

Mark all items as selected/checked.

setNone()

Mark all items as not selected/checked.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

class widgets.InputCombo(name, default, choices=[], onselect=None, func=None, *args, **kargs)

A combobox InputItem.

A combobox is a widget allowing the selection of an item from a drop down list.

choices is a list/tuple of possible values. default is the initial/default choice. If default is not in the choices list, it is prepended. If default is None, the first item of choices is taken as the default.

The choices are presented to the user as a combobox, which will initially be set to the default value.

An optional onselect function may be specified, which will be called whenever the current selection changes.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

class widgets.InputRadio(name, default, choices=[], direction='h', *args, **kargs)

A radiobuttons InputItem.

Radio buttons are a set of buttons used to select a value from a list.

choices is a list/tuple of possible values. default is the initial/default choice. If default is not in the choices list, it is prepended. If default is None, the first item of choices is taken as the default.

The choices are presented to the user as a hbox with radio buttons, of which the default will initially be pressed. If direction == ‘v’, the options are in a vbox.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

class widgets.InputPush(name, default=None, choices=[], direction='h', *args, **kargs)

A pushbuttons InputItem.

Creates pushbuttons for the selection of a value from a list.

choices is a list/tuple of possible values. default is the initial/default choice. If default is not in the choices list, it is prepended. If default is None, the first item of choices is taken as the default.

The choices are presented to the user as a hbox with radio buttons, of which the default will initially be selected. If direction == ‘v’, the options are in a vbox.

setText(text, index=0)

Change the text on button index.

setIcon(icon, index=0)

Change the icon on button index.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

class widgets.InputInteger(name, value, *args, **kargs)

An integer input item.

Options:

  • min, max: range of the scale (integer)
show()

Select all text on first display.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

class widgets.InputFloat(name, value, *args, **kargs)

An float input item.

show()

Select all text on first display.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

class widgets.InputSlider(name, value, *args, **kargs)

An integer input item using a slider.

Options:

  • min, max: range of the scale (integer)
  • ticks: step for the tick marks (default range length / 10)
  • func: an optional function to be called whenever the value is changed. The function takes a float/integer argument.
name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

show()

Select all text on first display.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

class widgets.InputFSlider(name, value, *args, **kargs)

A float input item using a slider.

Options:

  • min, max: range of the scale (integer)
  • scale: scale factor to compute the float value
  • ticks: step for the tick marks (default range length / 10)
  • func: an optional function to be called whenever the value is changed. The function receives the input field as argument. With this argument, the fields attirbutes like name, value, text, can be retrieved.
name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

show()

Select all text on first display.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

class widgets.InputPoint(name, value, *args, **kargs)

A 3D point/vector input item.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

class widgets.InputIVector(name, value, *args, **kargs)

A vector of int values.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

class widgets.InputButton(name, value, *args, **kargs)

A button input item.

The button input field is a button displaying the current value. Clicking on the button executes a function responsible for changing the value.

Extra parameters:

  • func: the function to call when the button is clicked. The current input value is passed as an argument. The function should return the value to be set, or None if it is to be unchanged. If no function is specified, the value can not be changed.
value()

Return the widget’s value.

doFunc()

Set the value by calling the button’s func

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

setValue(val)

Change the widget’s value.

class widgets.InputColor(name, value, *args, **kargs)

A color input item. Creates a new color input field with a label in front.

The color input field is a button displaying the current color. Clicking on the button opens a color dialog, and the returned value is set in the button.

setValue(value)

Change the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

value()

Return the widget’s value.

class widgets.InputFont(name, value, *args, **kargs)

An input item to select a font.

value()

Return the widget’s value.

name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

setValue(val)

Change the widget’s value.

class widgets.InputWidget(name, value, *args, **kargs)

An input item containing any other widget.

The widget should have:

  • a results attribute that is set to a dict with the resulting input values when the widget’s acceptData() is called.
  • an acceptData() method, that sets the widgets results dict.
  • a setValue(dict) method that sets the widgets values to those specified in the dict.

The return value of this item is an ODict.

text()

Return the displayed text.

setValue(val)

Change the widget’s value.

name()

Return the name of the InputItem.

value()

Return the widget’s value.

class widgets.InputForm

An input form.

The input form is a layout box in which the items are layed out vertically. The layout can also contain any number of tab widgets in which items can be layed out using tab pages.

class widgets.InputGroup(name, *args, **kargs)

A boxed group of InputItems.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

class widgets.InputTab(name, tab, *args, **kargs)

A tab page in an input form.

class widgets.InputDialog(items, caption=None, parent=None, flags=None, actions=None, default=None, store=None, prefix='', autoprefix=False, flat=None, modal=None, enablers=[])

A dialog widget to interactively set the value of one or more items.

Overview

The pyFormex user has full access to the Qt4 framework on which the GUI was built. Therefore he can built input dialogs as complex and powerful as he can imagine. However, directly dealing with the Qt4 libraries requires some skills and, for simple input widgets, more effort than needed.

The InputDialog class presents a unified system for quick and easy creation of common dialog types. The provided dialog can become quite sophisticated with tabbed pages, groupboxes and custom widgets. Both modal and modeless (non-modal) dialogs can be created.

Items

Each basic input item is a dictionary, where the fields have the following meaning:

  • name: the name of the field,
  • value: the initial or default value of the field,
  • itemtype: the type of values the field can accept,
  • options: a dict with options for the field.
  • text: if specified, the text value will be displayed instead of the name. The name value will remain the key in the return dict. Use this field to display a more descriptive text for the user, while using a short name for handling the value in your script.
  • buttons:
  • tooltip:
  • min:
  • max:
  • scale:
  • func:

Other arguments

  • caption: the window title to be shown in the window decoration
  • actions: a list of action buttons to be added at the bottom of the input form. By default, a Cancel and Ok button will be added, to either reject or accept the input values.
  • default: the default action
  • parent: the parent widget (by default, this is the pyFormex main window)
  • autoprefix: if True, the names of items inside tabs and group boxes will get prefixed with the tab and group names, separated with a ‘/’.
  • flat: if True, the results are returned in a single (flat) dictionary, with keys being the specified or autoprefixed ones. If False, the results will be structured: the value of a tab or a group is a dictionary with the results of its fields. The default value is equal to the value of autoprefix.
  • flags:
  • modal:
  • enablers: a list of tuples (key,value,key1,...) where the first two items indicate the key and value of the enabler, and the next items are keys of fields that are enabled when the field key has the specified value. Currentley, key should be a field of type boolean, [radio], combo or group. Also, any input field should only have one enabler!
add_items(items, form, prefix='')

Add input items to form.

items is a list of input item data layout is the widget layout where the input widgets will be added

add_tab(form, prefix, name, items, **extra)

Add a Tab page of input items.

add_group(form, prefix, name, items, **extra)

Add a group of input items.

add_input(form, prefix, **item)

Add a single input item to the form.

timeout()

Hide the dialog and set the result code to TIMEOUT

timedOut()

Returns True if the result code was set to TIMEOUT

show(timeout=None, timeoutfunc=None, modal=False)

Show the dialog.

For a non-modal dialog, the user has to call this function to display the dialog. For a modal dialog, this is implicitely executed by getResult().

If a timeout is given, start the timeout timer.

acceptData(result=1)

Update the dialog’s return value from the field values.

This function is connected to the ‘accepted()’ signal. Modal dialogs should normally not need to call it. In non-modal dialogs however, you can call it to update the results without having to raise the accepted() signal (which would close the dialog).

updateData(d)

Update a dialog from the data in given dictionary.

d is a dictionary where the keys are field names in the dialog. The values will be set in the corresponding input items.

getResults(timeout=None)

Get the results from the input dialog.

This fuction is used to present a modal dialog to the user (i.e. a dialog that must be ended before the user can continue with the program. The dialog is shown and user interaction is processed. The user ends the interaction either by accepting the data (e.g. by pressing the OK button or the ENTER key) or by rejecting them (CANCEL button or ESC key). On accept, a dictionary with all the fields and their values is returned. On reject, an empty dictionary is returned.

If a timeout (in seconds) is given, a timer will be started and if no user input is detected during this period, the input dialog returns with the default values set. A value 0 will timeout immediately, a negative value will never timeout. The default is to use the global variable input_timeout.

The result() method can be used to find out how the dialog was ended. Its value will be one of ACCEPTED, REJECTED ot TIMEOUT.

getResult(timeout=None)

Get the results from the input dialog.

This fuction is used to present a modal dialog to the user (i.e. a dialog that must be ended before the user can continue with the program. The dialog is shown and user interaction is processed. The user ends the interaction either by accepting the data (e.g. by pressing the OK button or the ENTER key) or by rejecting them (CANCEL button or ESC key). On accept, a dictionary with all the fields and their values is returned. On reject, an empty dictionary is returned.

If a timeout (in seconds) is given, a timer will be started and if no user input is detected during this period, the input dialog returns with the default values set. A value 0 will timeout immediately, a negative value will never timeout. The default is to use the global variable input_timeout.

The result() method can be used to find out how the dialog was ended. Its value will be one of ACCEPTED, REJECTED ot TIMEOUT.

class widgets.FileSelection(path='.', pattern='*.*', exist=False, multi=False, dir=False)

A file selection dialog.

You can specify a default path/filename that will be suggested initially. If a pattern is specified, only matching files will be shown. A pattern can be something like Images (*.png *.jpg) or a list of such strings. Default mode is to accept any filename. You can specify exist=True to accept only existing files. Or set exist=True and multi=True to accept multiple files. If dir==True, a single existing directory is asked.

getFilename(timeout=None)

Ask for a filename by user interaction.

Return the filename selected by the user. If the user hits CANCEL or ESC, None is returned.

class widgets.ProjectSelection(path=None, pattern=None, exist=False, compression=4, access=None, default=None, convert=True)

A file selection dialog specialized for opening projects.

getFilename(timeout=None)

Ask for a filename by user interaction.

Return the filename selected by the user. If the user hits CANCEL or ESC, None is returned.

class widgets.SaveImageDialog(path=None, pattern=None, exist=False, multi=False)

A dialog for saving to an image file.

The dialog contains the normal file selection widget plus some extra fields to set the Save Image parameters:

  • Whole Window: If checked, the whole pyFormex main window will be saved. If unchecked, only the current OpenGL viewport is saved.
  • Crop Root: If checked, the window will be cropped from the root window. This mode is required if you want to include the window decorations.
getFilename(timeout=None)

Ask for a filename by user interaction.

Return the filename selected by the user. If the user hits CANCEL or ESC, None is returned.

class widgets.ListSelection(choices, caption='ListSelection', default=[], single=False, check=False, sort=False, *args, **kargs)

A dialog for selecting one or more items from a list.

This is a convenient class which constructs an input dialog with a single input item: an InputList. It allows the user to select one or more items from a list. The constructor supports all arguments of the InputDialog and the InputList classes. The return value is the value of the InputList, not the result of the InputDialog.

setValue(selected)

Mark the specified items as selected.

value()

Return the selected items.

getResult()

Show the modal dialog and return the list of selected values.

If the user cancels the selection operation, the return value is None. Else, the result is always a list, possibly empty or with a single value.

add_items(items, form, prefix='')

Add input items to form.

items is a list of input item data layout is the widget layout where the input widgets will be added

add_tab(form, prefix, name, items, **extra)

Add a Tab page of input items.

add_group(form, prefix, name, items, **extra)

Add a group of input items.

add_input(form, prefix, **item)

Add a single input item to the form.

timeout()

Hide the dialog and set the result code to TIMEOUT

timedOut()

Returns True if the result code was set to TIMEOUT

show(timeout=None, timeoutfunc=None, modal=False)

Show the dialog.

For a non-modal dialog, the user has to call this function to display the dialog. For a modal dialog, this is implicitely executed by getResult().

If a timeout is given, start the timeout timer.

acceptData(result=1)

Update the dialog’s return value from the field values.

This function is connected to the ‘accepted()’ signal. Modal dialogs should normally not need to call it. In non-modal dialogs however, you can call it to update the results without having to raise the accepted() signal (which would close the dialog).

updateData(d)

Update a dialog from the data in given dictionary.

d is a dictionary where the keys are field names in the dialog. The values will be set in the corresponding input items.

getResults(timeout=None)

Get the results from the input dialog.

This fuction is used to present a modal dialog to the user (i.e. a dialog that must be ended before the user can continue with the program. The dialog is shown and user interaction is processed. The user ends the interaction either by accepting the data (e.g. by pressing the OK button or the ENTER key) or by rejecting them (CANCEL button or ESC key). On accept, a dictionary with all the fields and their values is returned. On reject, an empty dictionary is returned.

If a timeout (in seconds) is given, a timer will be started and if no user input is detected during this period, the input dialog returns with the default values set. A value 0 will timeout immediately, a negative value will never timeout. The default is to use the global variable input_timeout.

The result() method can be used to find out how the dialog was ended. Its value will be one of ACCEPTED, REJECTED ot TIMEOUT.

class widgets.GenericDialog(widgets, title=None, parent=None, actions=[('OK',)], default='OK')

A generic dialog widget.

The dialog is formed by a number of widgets stacked in a vertical box layout. At the bottom is a horizontal button box with possible actions.

  • widgets: a list of widgets to include in the dialog
  • title: the window title for the dialog
  • parent: the parent widget. If None, it is set to pf.GUI.
  • actions: the actions to include in the bottom button box. By default, an ‘OK’ button is displayed to close the dialog. Can be set to None to avoid creation of a button box.
  • default: the default action, ‘OK’ by default.
class widgets.TableModel(data, chead=None, rhead=None, edit=True)

A table model that represent data as a two-dimensional array of items.

data is any tabular data organized in a fixed number of rows and colums. This means that an item at row i and column j can be addressed as data[i][j]. Optional lists of column and row headers can be specified.

flags(index)

Return the TableModel flags.

class widgets.ArrayModel(data, chead=None, rhead=None, edit=True)

A TableModel specialized for represents 2D array data.

  • data: a numpy array with two dimensions.
  • chead, rhead: column and row headers. The default will show column and row numbers.
  • edit: if True (default), the data can be edited. Set to False to make the data readonly.
flags(index)

Return the TableModel flags.

class widgets.Table(data, chead=None, rhead=None, label=None, edit=True, parent=None)

A widget to show/edit a two-dimensional array of items.

  • data: a 2-D array of items, with nrow rows and ncol columns. If data is an ndarray instance, the Table will use an ArrayModel, else a TableModel. The difference is important when editing the table. Also, an ArrayModel has default row and column headers, while a TableModel doesn’t.
  • chead: an optional list of ncol column headers.
  • rhead: an optional list of nrow row headers.
  • label: currently unused (intended to display an optional label in the upper left corner if both chead and rhead are specified.
class widgets.Tabs(items, parent=None)

Present a list of widgets as a single tabbed widget.

  • items: a list of (header,widget) tuples.
  • caption:
  • parent:
class widgets.TableDialog(data, chead=None, rhead=None, title=None, parent=None, actions=[('OK',)], default='OK')

A dialog widget to show/edit a two-dimensional array of items.

A convenience class representing a Table within a dialog.

class widgets.MessageBox(text, format='', level='info', actions=['OK'], default=None, timeout=None, modal=None, parent=None, check=None)

A message box is a widget displaying a short text for the user.

The message box displays a text, an optional icon depending on the level and a number of push buttons.

  • text: the text to be shown. This can be either plain text or html or reStructuredText.
  • format: the text format: either ‘plain’, ‘html’ or ‘rest’. Any other value will try automatic recognition. Recognition of plain text and html is automatic. A text is autorecognized to be reStructuredText if its first line starts with ‘..’ and is followed by a blank line.
  • level: defines the icon that will be shown together with the text. If one of ‘question’, ‘info’, ‘warning’ or ‘error’, a matching icon will be shown to hint the user about the type of message. Any other value will suppress the icon.
  • actions: a list of strings. For each string a pushbutton will be created which can be used to exit the dialog and remove the message. By default there is a single button labeled ‘OK’.

When the MessageBox is displayed with the getResult() method, a modal dialog is created, i.e. the user will have to click a button or hit the ESC key before he can continue.

If you want a modeless dialog, allowing the user to continue while the message stays open, use the show() mehod to display it.

addCheck(text)

Add a check field at the bottom of the layout.

getResult()

Display the message box and wait for user to click a button.

This will show the message box as a modal dialog, so that the user has to click a button (or hit the ESC key) before he can continue. Returns the text of the button that was clicked or an empty string if ESC was hit.

class widgets.WarningBox

A message box is a widget displaying a short text for the user.

The message box displays a text, an optional icon depending on the level and a number of push buttons.

  • text: the text to be shown. This can be either plain text or html or reStructuredText.
  • format: the text format: either ‘plain’, ‘html’ or ‘rest’. Any other value will try automatic recognition. Recognition of plain text and html is automatic. A text is autorecognized to be reStructuredText if its first line starts with ‘..’ and is followed by a blank line.
  • level: defines the icon that will be shown together with the text. If one of ‘question’, ‘info’, ‘warning’ or ‘error’, a matching icon will be shown to hint the user about the type of message. Any other value will suppress the icon.
  • actions: a list of strings. For each string a pushbutton will be created which can be used to exit the dialog and remove the message. By default there is a single button labeled ‘OK’.

When the MessageBox is displayed with the getResult() method, a modal dialog is created, i.e. the user will have to click a button or hit the ESC key before he can continue.

If you want a modeless dialog, allowing the user to continue while the message stays open, use the show() mehod to display it.

class widgets.TextBox(text, format=None, actions=['OK', None], modal=None, parent=None, caption=None, mono=False, timeout=None, flags=None)

Display a text and wait for user response.

Possible choices are ‘OK’ and ‘CANCEL’. The function returns True if the OK button was clicked or ‘ENTER’ was pressed, False if the ‘CANCEL’ button was pressed or ESC was pressed.

class widgets.ButtonBox(name, actions=[], parent=None, stretch=[-1, -1])

A box with action buttons.

  • name: a label to be displayed in front of the button box. An empty string will suppress it.
  • actions: a list of (button label, button function) tuples. The button function can be a normal callable function, or one of the values widgets.ACCEPTED or widgets.REJECTED. In the latter case, parent should be specified.
  • parent: the parent dialog holding this button box. It should be specified if one of the buttons actions is widgets.ACCEPTED or widgets.REJECTED.
name()

Return the name of the InputItem.

text()

Return the displayed text of the InputItem.

setText(text, index=0)

Change the text on button index.

setIcon(icon, index=0)

Change the icon on button index.

value()

Return the widget’s value.

setValue(val)

Change the widget’s value.

class widgets.CoordsBox(ndim=3, readonly=False, *args)

A widget displaying the coordinates of a point.

getValues()

Return the current x,y,z values as a list of floats.

setValues(values)

Set the three values of the widget.

class widgets.ImageView(image=None, maxheight=None, parent=None)

A widget displaying an image.

showImage(image)

Show an image in the viewer.

image: either a filename or an existing QImage instance. If a filename, it should be an image file that can be read by the QImage constructor. Most image formats are understood by QImage. The variable gui.image.image_formats_qtr provides a list.

Functions defined in module widgets

widgets.maxSize()

Return the maximum widget size.

The maximum widget size is the (available) screen size. This may be smaller than the physical screen size (e.g. excluding docking panels).

widgets.addTimeOut(widget, timeout=None, timeoutfunc=None)

Add a timeout to a widget.

  • timeoutfunc is a callable. If None it will be set to the widget’s timeout method if one exists.
  • timeout is a float value. If None, it will be set to to the global input_timeout.

If timeout is positive, a timer will be installed into the widget which will call the timeoutfunc after timeout seconds have elapsed. The timeoutfunc can be any callable, but usually will emit a signal to make the widget accept or reject the input. The timeoutfunc will not be called is if the widget is destructed before the timer has finished.

widgets.defaultItemType(item)

Guess the InputItem type from the value

widgets.simpleInputItem(name, value=None, itemtype=None, **kargs)

A convenience function to create an InputItem dictionary

widgets.groupInputItem(name, items=[], **kargs)

A convenience function to create an InputItem dictionary

widgets.tabInputItem(name, items=[], **kargs)

A convenience function to create an InputItem dictionary

widgets.compatInputItem(name, value, itemtype=None, kargs={})

A convenience function to create an InputItem dictionary

This function accepts InputItem data in the old format:

( name, value, [ itemtype, [ optionsdict ] ] )

and turns them into a dictionary as required by the new InputItem format.

widgets.convertInputItem(data)

Convert InputItem data to a proper dict.

This function tries to convert some old style or sloppy InputItem data to a proper InputItem data dict.

The conversion does the following:

  • if data is a dict, it is considered proper data and returned as is.
  • if data is a tuple or a list, first conversion with simpleInputItem is tried, then conversion with compatInputItem, using the data items as arguments.
  • if neither succeeds, an error is raised.
widgets.inputAny(name, value, itemtype, **options)

Create an InputItem of any type, depending on the arguments.

Arguments: only name, value and itemtype are required

  • name: name of the item, also the key for the return value
  • value: initial value,
  • itemtype: one of the available itemtypes
widgets.updateDialogItems(data, newdata)

Update the input data fields with new data values

  • data: a list of dialog items, as required by an InputDialog.
  • newdata: a dictionary with new values for (some of) the items.

The data items with a name occurring as a key in newdata will have their value replaced with the corresponding value in newdata, unless this value is None.

The user should make sure to set only values of the proper type!

widgets.selectFont()

Ask the user to select a font.

A font selection dialog widget is displayed and the user is requested to select a font. Returns a font if the user exited the dialog with the OK button. Returns None if the user clicked CANCEL.

widgets.getColor(col=None, caption=None)

Create a color selection dialog and return the selected color.

col is the initial selection. If a valid color is selected, its string name is returned, usually as a hex #RRGGBB string. If the dialog is canceled, None is returned.

widgets.updateText(widget, text, format='')

Update the text of a text display widget.

  • widget: a widget that has the setText(), setPlainText() and setHtml() methods to set the widget’s text. Examples are QMessageBox and QTextEdit.
  • text: a multiline string with the text to be displayed.
  • format: the format of the text. If empty, autorecognition will be tried. Currently available formats are: plain, html and rest (reStructuredText).

This function allows to display other text formats besides the plain text and html supported by the widget. Any format other than plain or html will be converted to one of these before sending it to the widget. Currently, we convert the following formats:

  • rest (reStructuredText): If the :mod:docutils is available, rest text is converted to html, otherwise it will be displayed as plain text. A text is autorecognized as reStructuredText if its first line starts with ‘..’. Note: If you add a ‘..’ line to your text to have it autorecognized as reST, be sure to have it followed with a blank line, or your first paragraph could be turned into comments.
widgets.dialogButtons(dialog, actions=None, default=None)

Create a set of dialog buttons

dia is a dialog widget

actions is a list of tuples (name,) or (name,function). If a function is specified, it will be executed on pressing the button. If no function is specified, and name is one of ‘ok’ or ‘cancel’ (case is ignored), the button will be bound to the dialog’s ‘accept’ or ‘reject’ slot. If actions==None (default), it will be set to the default [('Cancel',),('OK',)].

Specify actions=[] if you want an empty dialogDuttons. default is the name of the action to set as the default. If no default is given, it is set to the LAST button.

Returns a horizontal box layout with the buttons.

pyformex-0.8.6/pyformex/doc/html/ref/tetgen.html0000644000211500021150000004220511705104255021547 0ustar benebene00000000000000 50. tetgen — Interface with tetgen — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

49. export — Classes and functions for exporting geometry in various formats.

Next topic

51. tools — tools.py

[FSF Associate Member]

Valid XHTML 1.0 Transitional

50. tetgen — Interface with tetgen

A collection of functions to read/write tetgen files and to run the tetgen program

tetgen is a quality tetrahedral mesh generator and a 3D Delaunay triangulator. See http://tetgen.org

Classes defined in module tetgen

Functions defined in module tetgen

tetgen.readNodeFile(fn)

Read a tetgen .node file.

Returns a tuple as described in readNodesBlock.

tetgen.readEleFile(fn)

Read a tetgen .ele file.

Returns a tuple as described in readElemsBlock.

tetgen.readFaceFile(fn)

Read a tetgen .face file.

Returns a tuple as described in readFacesBlock.

tetgen.readSmeshFile(fn)

Read a tetgen .smesh file.

Returns an array of triangle elements.

tetgen.readPolyFile(fn)

Read a tetgen .poly file.

Returns an array of triangle elements.

tetgen.readSurface(fn)

Read a tetgen surface from a .node/.face file pair.

The given filename is either the .node or .face file. Returns a tuple of (nodes,elems).

tetgen.skipComments(fil)

Skip comments and blank lines on a tetgen file.

Reads from a file until the first non-comment and non-empty line. Then returns the non-empty, non-comment line, stripped from possible trailing comments. Returns None if end of file is reached.

tetgen.stripLine(line)

Strip blanks, newline and comments from a line of text.

tetgen.getInts(line, nint)

Read a number of ints from a line, adding zero for omitted values.

line is a string with b;anks separated integer values. Returns a list of nint integers. The trailing ones are set to zero if the strings contains less values.

tetgen.addElem(elems, nrs, e, n, nplex)

Add an element to a collection.

tetgen.readNodesBlock(fil, npts, ndim, nattr, nbmark)

Read a tetgen nodes block.

Returns a tuple with:

  • coords: Coords array with nodal coordinates
  • nrs: node numbers
  • attr: node attributes
  • bmrk: node boundary marker

The last two may be None.

tetgen.readElemsBlock(fil, nelems, nplex, nattr)

Read a tetgen elems block.

Returns a tuple with:

  • elems: Connectivity of type ‘tet4’ or ‘tet10’
  • nrs: the element numbers
  • attr: the element attributes

The last can be None.

tetgen.readFacesBlock(fil, nelems, nbmark)

Read a tetgen faces block.

Returns a a tuple with:

  • elems: Connectivity of type ‘tri3’
  • nrs: face numbers
  • bmrk: face boundary marker

The last can be None.

tetgen.readSmeshFacetsBlock(fil, nfacets, nbmark)

Read a tetgen .smesh facets bock.

Returns a tuple of dictionaries with plexitudes as keys:

  • elems: for each plexitude a Connectivity array
  • nrs: for each plexitude a list of element numbers in corresponding elems
tetgen.readNeigh(fn)

Read a tetgen .neigh file.

Returns an arrays containing the tetrahedra neighbours:

tetgen.writeNodes(fn, coords, offset=0)

Write a tetgen .node file.

tetgen.writeSmesh(fn, facets, coords=None, holes=None, regions=None)

Write a tetgen .smesh file.

Currently it only writes the facets of a triangular surface mesh. Coords should be written independently to a .node file.

tetgen.writeSurface(fn, coords, elems)

Write a tetgen surface model to .node and .smesh files.

The provided file name is the .node or the .smesh filename.

tetgen.nextFilename(fn)

Returns the next file name in a family of tetgen file names.

tetgen.runTetgen(fn, options='')

Run tetgen mesher on the specified file.

The input file is a closed triangulated surface. tetgen will generate a volume tetraeder mesh inside the surface, and create a new approximation of the surface as a by-product.

tetgen.readTetgen(fn)

Read and draw a tetgen file.

This is an experimental function for the geometry import menu.

pyformex-0.8.6/pyformex/doc/html/ref/imagearray.html0000644000211500021150000002174711705104253022410 0ustar benebene00000000000000 25. imagearray — Convert bitmap images into numpy arrays. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

24. image — Saving OpenGL renderings to image files.

Next topic

26. imagecolor — Using bitmap images as colors.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

25. imagearray — Convert bitmap images into numpy arrays.

This module contains functions to convert bitmap images into numpy arrays and vice versa.

Most of this code was borrowed from the PyQwt mailing list

Classes defined in module imagearray

Functions defined in module imagearray

imagearray.gray2qimage(gray)

Convert the 2D numpy array gray into a 8-bit QImage with a gray colormap. The first dimension represents the vertical image axis.

imagearray.rgb2qimage(rgb)

Convert the 3D numpy array rgb into a 32-bit QImage. rgb must have three dimensions with the vertical, horizontal and RGB image axes.

pyformex-0.8.6/pyformex/doc/html/ref/camera.html0000644000211500021150000006531211705104247021516 0ustar benebene00000000000000 23. camera — OpenGL camera handling — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

22. viewport — Interactive OpenGL Canvas embedded in a Qt4 widget.

Next topic

24. image — Saving OpenGL renderings to image files.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

23. camera — OpenGL camera handling

Classes defined in module camera

class camera.ViewAngles(data={'right': (90.0, 0.0, 0.0), 'back': (180.0, 0.0, 0.0), 'iso1': (45.0, 135.0, 0.0), 'iso0': (45.0, 45.0, 0.0), 'iso3': (45.0, 315.0, 0.0), 'iso2': (45.0, 225.0, 0.0), 'iso5': (-45.0, 135.0, 0.0), 'iso4': (-45.0, 45.0, 0.0), 'iso7': (-45.0, 315.0, 0.0), 'iso6': (-45.0, 225.0, 0.0), 'front': (0.0, 0.0, 0.0), 'bottom': (0.0, -90.0, 0.0), 'top': (0.0, 90.0, 0.0), 'left': (270.0, 0.0, 0.0)})

A dict to keep named camera angle settings.

This class keeps a dictionary of named angle settings. Each value is a tuple of (longitude, latitude, twist) camera angles. This is a static class which should not need to be instantiated.

There are seven predefined values: six for looking along global coordinate axes, one isometric view.

get(name)

Get the angles for a named view.

Returns a tuple of angles (longitude, latitude, twist) if the named view was defined, or None otherwise

class camera.Camera(center=[0.0, 0.0, 0.0], long=0.0, lat=0.0, twist=0.0, dist=1.0)

A camera for OpenGL rendering.

The Camera class holds all the camera related settings related to the rendering of a scene in OpenGL. These include camera position, the viewing direction of the camera, and the lens parameters (opening angle, front and back clipping planes). This class also provides convenient methods to change the settings so as to get smooth camera manipulation.

Camera position and orientation:

The camera viewing line is defined by two points: the position of the camera and the center of the scene the camera is looking at. We use the center of the scene as the origin of a local coordinate system to define the camera position. For convenience, this could be stored in spherical coordinates, as a distance value and two angles: longitude and latitude. Furthermore, the camera can also rotate around its viewing line. We can define this by a third angle, the twist. From these four values, the needed translation vector and rotation matrix for the scene rendering may be calculated.

Inversely however, we can not compute a unique set of angles from a given rotation matrix (this is known as ‘gimball lock’). As a result, continuous (smooth) camera rotation by e.g. mouse control requires that the camera orientation be stored as the full rotation matrix, rather than as three angles. Therefore we store the camera position and orientation as follows:

  • ctr: [ x,y,z ] : the reference point of the camera: this is always a point on the viewing axis. Usually, it is set to the center of the scene you are looking at.
  • dist: distance of the camera to the reference point.
  • rot: a 3x3 rotation matrix, rotating the global coordinate system thus that the z-direction is oriented from center to camera.

These values have influence on the ModelView matrix.

Camera lens settings:

The lens parameters define the volume that is seen by the camera. It is described by the following parameters:

  • fovy: the vertical lens opening angle (Field Of View Y),
  • aspect: the aspect ratio (width/height) of the lens. The product fovy * aspect is the horizontal field of view.
  • near, far: the position of the front and back clipping planes. They are given as distances from the camera and should both be strictly positive. Anything that is closer to the camera than the near plane or further away than the far plane, will not be shown on the canvas.

Camera methods that change these values will not directly change the ModelView matrix. The loadModelView() method has to be called explicitely to make the settings active.

These values have influence on the Projection matrix.

Methods that change the camera position, orientation or lens parameters will not directly change the related ModelView or Projection matrix. They will just flag a change in the camera settings. The changes are only activated by a call to the loadModelView() or loadProjection() method, which will test the flags to see whether the corresponding matrix needs a rebuild.

The default camera is at distance 1.0 of the center point [0.,0.,0.] and looking in the -z direction. Near and far clipping planes are by default set to 0.1, resp 10 times the camera distance.

getCenter()

Return the camera reference point (the scene center).

getRot()

Return the camera rotation matrix.

getDist()

Return the camera distance.

lock(onoff=True)

Lock/unlock a camera.

When a camera is locked, its position and lens parameters can not be changed. This can e.g. be used in multiple viewports layouts to create fixed views from different angles.

setCenter(x, y, z)

Set the center of the camera in global cartesian coordinates.

setAngles(angles)

Set the rotation angles.

angles is either:

  • a tuple of angles (long,lat,twist)
  • a named view corresponding to angles in view_angles
  • None
setRotation(long, lat, twist=0)

Set the rotation matrix of the camera from three angles.

setDist(dist)

Set the distance.

report()

Return a report of the current camera settings.

dolly(val)

Move the camera eye towards/away from the scene center.

This has the effect of zooming. A value > 1 zooms out, a value < 1 zooms in. The resulting enlargement of the view will approximately be 1/val. A zero value will move the camera to the center of the scene. The front and back clipping planes may need adjustment after a dolly operation.

pan(val, axis=0)

Rotate the camera around axis through its eye.

The camera is rotated around an axis through the eye point. For axes 0 and 1, this will move the center, creating a panning effect. The default axis is parallel to the y-axis, resulting in horizontal panning. For vertical panning (axis=1) a convenience alias tilt is created. For axis = 2 the operation is equivalent to the rotate operation.

tilt(val)

Rotate the camera up/down around its own horizontal axis.

The camera is rotated around and perpendicular to the plane of the y-axis and the viewing axis. This has the effect of a vertical pan. A positive value tilts the camera up, shifting the scene down. The value is specified in degrees.

move(dx, dy, dz)

Move the camera over translation (dx,dy,dz) in global coordinates.

The center of the camera is moved over the specified translation vector. This has the effect of moving the scene in opposite direction.

rotate(val, vx, vy, vz)

Rotate the camera around current camera axes.

saveModelView()

Save the ModelView matrix.

setModelView()

Set the ModelView matrix from camera parameters.

loadModelView(m=None)

Load the ModelView matrix.

There are thrre uses of this function:

  • Without argument and if the viewing parameters have not changed since the last save of the ModelView matrix, this will just reload the ModelView matrix from the saved value.
  • If an argument is supplied, it should be a legal ModelView matrix and that matrix will be loaded (and saved) as the new ModelView matrix.
  • Else, a new ModelView matrix is set up from the camera parameters, and it is loaded and saved.

In the latter two cases, the new ModelView matrix is saved, and if a camera attribute modelview_callback has been set, a call to this function is done, passing the camera instance as parameter.

loadCurrentRotation()

Load the current ModelView matrix with translations canceled out.

transform(v)

Transform a vertex using the currently saved Modelview matrix.

toWorld(v, trl=False)

Transform a vertex from camera to world coordinates.

The specified vector can have 3 or 4 (homogoneous) components. This uses the currently saved rotation matrix.

setLens(fovy=None, aspect=None)

Set the field of view of the camera.

We set the field of view by the vertical opening angle fovy and the aspect ratio (width/height) of the viewing volume. A parameter that is not specified is left unchanged.

resetArea()

Set maximal camera area.

Resets the camera window area to its maximum values corresponding to the fovy setting, symmetrical about the camera axes.

setArea(hmin, vmin, hmax, vmax, relative=True, center=False, clip=True)

Set the viewable area of the camera.

zoomArea(val=0.5, area=None)

Zoom in/out by shrinking/enlarging the camera view area.

The zoom factor is relative to the current setting. Values smaller than 1.0 zoom in, larger values zoom out.

transArea(dx, dy)

Pan by moving the vamera area.

dx and dy are relative movements in fractions of the current area size.

setClip(near, far)

Set the near and far clipping planes

setPerspective(on=True)

Set perspective on or off

loadProjection(force=False, pick=None, keepmode=False)

Load the projection/perspective matrix.

The caller will have to setup the correct GL environment beforehand. No need to set matrix mode though. This function will switch to GL_PROJECTION mode before loading the matrix

!! CHANGED: does not switch back to GL_MODELVIEW mode!

A pick region can be defined to use the camera in picking mode. pick defines the picking region center and size (x,y,w,h).

This function does it best at autodetecting changes in the lens settings, and will only reload the matrix if such changes are detected. You can optionally force loading the matrix.

project(x, y, z)

Map the object coordinates (x,y,z) to window coordinates.

unProject(x, y, z)

Map the window coordinates (x,y,z) to object coordinates.

setTracking(onoff=True)

Enable/disable coordinate tracking using the camera

Functions defined in module camera

camera.tand(arg)

Return the tan of an angle in degrees.

pyformex-0.8.6/pyformex/doc/html/ref/toolbar.html0000644000211500021150000003153211705104255021724 0ustar benebene00000000000000 29. toolbar — Toolbars for the pyFormex GUI. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

28. scriptMenu — Menu with pyFormex scripts.

Next topic

30. curve — Definition of curves in pyFormex.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

29. toolbar — Toolbars for the pyFormex GUI.

This module defines the functions for creating the pyFormex window toolbars.

Classes defined in module toolbar

Functions defined in module toolbar

toolbar.addActionButtons(toolbar)

Add the script action buttons to the toolbar.

toolbar.addButton(toolbar, tooltip, icon, func, repeat=False, toggle=False, checked=False, icon0=None)

Add a button to a toolbar.

  • toolbar: the toolbar where the button will be added
  • tooltip: the text to appears as tooltip
  • icon: name of the icon to be displayed on the button,
  • func: function to be called when the button is pressed,
  • repeat: if True, the func will repeatedly be called if button is held down.
  • toggle: if True, the button is a toggle and stays in depressed state until pressed again.
  • checked: initial state for a toggle buton.
  • icon1: for a toggle button, icon to display when button is not checked.
toolbar.removeButton(toolbar, button)

Remove a button from a toolbar.

toolbar.addCameraButtons(toolbar)

Add the camera buttons to a toolbar.

toolbar.toggleButton(attr, state=None)

Update the corresponding viewport attribute.

This does not update the button state.

toolbar.updateButton(button, attr)

Update the button to correct state.

toolbar.updateTransparencyButton()

Update the transparency button to correct state.

toolbar.updateLightButton()

Update the light button to correct state.

toolbar.updateNormalsButton(state=True)

Update the normals button to correct state.

toolbar.updatePerspectiveButton()

Update the normals button to correct state.

toolbar.addTimeoutButton(toolbar)

Add or remove the timeout button,depending on cfg.

toolbar.timeout(onoff=None)

Programmatically toggle the timeout button

pyformex-0.8.6/pyformex/doc/html/ref/dxf.html0000644000211500021150000004325111705104252021041 0ustar benebene00000000000000 48. dxf — Read/write geometry in DXF format. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

47. turtle — Turtle graphics for pyFormex

Next topic

49. export — Classes and functions for exporting geometry in various formats.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

48. dxf — Read/write geometry in DXF format.

This module allows to import and export some simple geometrical items in DXF format.

Classes defined in module dxf

class dxf.DxfExporter(filename, terminator='n')

Export geometry in DXF format.

While we certainly do not want to promote proprietary software, some of our users occasionally needed to export some model in DXF format. This class provides a minimum of functionality.

write(s)

Write a string to the dxf file.

The string does not include the line terminator.

out(code, data)

Output a string data item to the dxf file.

code is the group code, data holds the data

close()

Finalize and close the DXF file

section(name)

Start a new section

endSection()

End the current section

entities()

Start the ENTITIES section

layer(layer)

Export the layer

line(x, layer=0)

Export a line.

x is a (2,3) shaped array

Functions defined in module dxf

dxf.importDXF(filename)

Import (parts of) a DXF file into pyFormex.

This function scans a DXF file for recognized entities and imports those entities as pyFormex objects. It is only a very partial importer, but has proven to be already very valuable for many users.

filename: name of a DXF file. The return value is a list of pyFormex objects.

Importing a DXF file is done in two steps:

  • First the DXF file is scanned and the recognized entities are formatted into a text with standard function calling syntax. See readDXF().
  • Then the created text is executed as a Python script, producing equivalent pyFormex objects. See convertDXF().
dxf.readDXF(filename)

Read a DXF file and extract the recognized entities.

filename: name of a .DXF file.

Returns a multiline string with one line for each recognized entity, in a format that can directly be used by convertDXF().

This function requires the external program dxfparser which comes with the pyFormex distribution. It currently recognizes entities of type ‘Arc’, ‘Line’, ‘Polyline’, ‘Vertex’.

dxf.convertDXF(text)

Convert a textual representation of a DXF format to pyFormex objects.

text : a multiline text representation of the contents of a DXF file.

This text representation can e.g. be obtained by the function readDXF(). It contains lines defining DXF entities. A small example:

Arc(0.0,0.0,0.0,1.0,-90.,90.)
Arc(0.0,0.0,0.0,3.0,-90.,90.)
Line(0.0,-1.0,0.0,0.0,1.0,0.0)
Polyline(0)
Vertex(0.0,3.0,0.0)
Vertex(-2.0,3.0,0.0)
Vertex(-2.0,-7.0,0.0)
Vertex(0.0,-7.0,0.0)
Vertex(0.0,-3.0,0.0)

Each line of the text defines a single entity or starts a multiple component entity. The text should be well aligned to constitute a proper Python script. Currently, the only defined entities are ‘Arc’, ‘Line’, ‘Polyline’, ‘Vertex’.

Returns a list of pyFormex objects corresponding to the text. The returned objects are of the following type:

function name object
Arc plugins.curve.Arc
Line plugins.curve.Line
Polyline plugins.curve.PolyLine

No object is returned for the Vertex function: they define the vertices of a PolyLine.

dxf.exportDXF(filename, F)

Export a Formex to a DXF file

Currently, only plex-2 Formices can be exported to DXF.

pyformex-0.8.6/pyformex/doc/html/ref/flavia.html0000644000211500021150000002414111705104252021517 0ustar benebene00000000000000 45. flavia — — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

44. postproc — Postprocessing functions

Next topic

46. lima — Lindenmayer Systems

[FSF Associate Member]

Valid XHTML 1.0 Transitional

45. flavia

  1. 2010 Benedict Verhegghe.

Classes defined in module flavia

Functions defined in module flavia

flavia.readMesh(fn)

Read a flavia mesh file.

Returns a list of Meshes if succesful.

flavia.readCoords(fil, ndim)

Read a set of coordinates from a flavia file

flavia.readElems(fil, nplex)

Read a set of coordinates from a flavia file

flavia.readResults(fn, nnodes, ndim)

Read a flavia results file for an ndim mesh.

flavia.readResult(fil, nvalues, nres)

Read a set of results from a flavia file

flavia.createFeResult(model, results)

Create an FeResult from meshes and results

flavia.readFlavia(meshfile, resfile)

Read flavia results files

Currently we only read matching pairs of meshfile,resfile files.

pyformex-0.8.6/pyformex/doc/html/ref/marks.html0000644000211500021150000003625011705104253021377 0ustar benebene00000000000000 19. marks — OpenGL marks for annotating 3D actors. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

18. decors — 2D decorations for the OpenGL canvas.

Next topic

20. gluttext — 2D text decorations using GLUT fonts

[FSF Associate Member]

Valid XHTML 1.0 Transitional

19. marks — OpenGL marks for annotating 3D actors.

Classes defined in module marks

class marks.Mark(pos, nolight=True, **kargs)

A 2D drawing inserted at a 3D position of the scene.

The minimum attributes and methods are:
pos : 3D point where the mark will be drawn draw() : function to draw the mark
drawGL(**kargs)

Perform the OpenGL drawing functions to display the actor.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class marks.AxesMark(pos, color=None, **kargs)

Two viewport axes drawn at a 3D position.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class marks.TextMark(pos, text, color=None, font='sans', size=18, **kargs)

A text drawn at a 3D position.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class marks.MarkList(pos, val, color=(0.0, 0.0, 0.0), font='sans', size=18, leader='', gravity='', **kargs)

A list of numbers drawn at 3D positions.

drawpick()

This functions mimicks the drawing of a number list for picking.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

Functions defined in module marks

pyformex-0.8.6/pyformex/doc/html/ref/plot2d.html0000644000211500021150000002123011705104254021457 0ustar benebene00000000000000 53. plot2d — plot2d.py — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

52. objects — Selection of objects from the global dictionary.

Next topic

54. olist — Some convenient shortcuts for common list operations.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

53. plot2d — plot2d.py

Generic 2D plotting functions for pyFormex.

Classes defined in module plot2d

Functions defined in module plot2d

plot2d.showHistogram(x, y, txt, **options)

Show a histogram of x,y data.

plot2d.createHistogram(data, cumulative=False, **kargs)

Create a histogram from data

pyformex-0.8.6/pyformex/doc/html/ref/gluttext.html0000644000211500021150000002746011705104253022145 0ustar benebene00000000000000 20. gluttext — 2D text decorations using GLUT fonts — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

19. marks — OpenGL marks for annotating 3D actors.

Next topic

21. canvas — This implements an OpenGL drawing widget for painting 3D scenes.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

20. gluttext — 2D text decorations using GLUT fonts

This module provides the basic functions for using the GLUT library in the rendering of text on an OpenGL canvas.

Classes defined in module gluttext

Functions defined in module gluttext

gluttext.glutSelectFont(font=None, size=None)

Select one of the glut fonts using a font + size description.

  • font: ‘fixed’, ‘serif’ or ‘sans’
  • size: an int that will be rounded to the nearest available size.

The return value is a 4-character string representing one of the GLUT fonts.

gluttext.glutFont(font)

Return GLUT font designation for the named font.

The recognized font names are:

  • fixed: ‘9x15’, ‘8x13’,
  • times-roman: ‘tr10’, ‘tr24’
  • helvetica: ‘hv10’, ‘hv12’, ‘hv18’

If an unrecognized string is given, the default is ‘hv18’.

gluttext.glutFontHeight(font)

Return the height of the named glut font.

This supposes that the last two characters of the name hold the font height.

gluttext.glutRenderText(text, font, gravity='')

Draw a text in given font at the current rasterpoint.

font should be one of the legal fonts returned by glutFont(). If text is not a string, it will be formatted to a string before drawing. After drawing, the rasterpos will have been updated!

gluttext.glutBitmapLength(font, text)

Compute the length in pixels of a text string in given font.

We use our own function to calculate the length because the builtin has a bug.

gluttext.glutDrawText(text, x, y, font='hv18', gravity='', spacing=1.0)

Draw a text at given 2D position in window.

  • text: a simple string, a multiline string or a list of strings. If it is a string, it will be splitted on the occurrence of ‘n’ characters.
  • x,y: insertion position on the canvas
  • gravity: a string that determines the adjusting of the text with respect to the insert position. It can be a combination of one of the characters ‘N or ‘S’ to specify the vertical positon, and ‘W’ or ‘E’ for the horizontal. The default(empty) string will center the text.
pyformex-0.8.6/pyformex/doc/html/ref/tools.html0000644000211500021150000002461111705104255021422 0ustar benebene00000000000000 51. tools — tools.py — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

50. tetgen — Interface with tetgen

Next topic

52. objects — Selection of objects from the global dictionary.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

51. tools — tools.py

Graphic Tools for pyFormex.

Classes defined in module tools

Functions defined in module tools

tools.getObjectItems(obj, items, mode)

Get the specified items from object.

tools.getCollection(K)

Returns a collection.

tools.growCollection(K, **kargs)

Grow the collection with n frontal rings.

K should be a collection of elements. This currently only works on surfaces. Objects that do not have a nodeFront() generator function are

tools.partitionCollection(K)

Partition the collection according to node adjacency.

The actor numbers will be connected to a collection of property numbers, e.g. 0 [1 [4,12] 2 [6,20]], where 0 is the actor number, 1 and 2 are the property numbers and 4, 12, 6 and 20 are the element numbers.

tools.getPartition(K, prop)

Remove all partitions with property not in prop.

tools.exportObjects(obj, name, single=False)

Export a list of objects under the given name.

If obj is a list, and single=True, each element of the list is exported as a single item. The items will be given the names name-0, name-1, etc. Else, the obj is exported as is under the name.

pyformex-0.8.6/pyformex/doc/html/ref/fe.html0000644000211500021150000007766411705104252020671 0ustar benebene00000000000000 41. fe — Finite Element Models in pyFormex. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

40. properties — General framework for attributing properties to geometrical elements.

Next topic

42. fe_abq — Exporting finite element models in Abaqus™ input file format.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

41. fe — Finite Element Models in pyFormex.

Finite element models are geometrical models that consist of a unique set of nodal coordinates and one of more sets of elements.

Classes defined in module fe

class fe.Model(coords, elems)

Contains all FE model data.

nnodes()

Return the number of nodes in the model.

nelems()

Return the number of elements in the model.

ngroups()

Return the number of element groups in the model.

mplex()

Return the maximum plexitude of the model.

splitElems(set)

Splits a set of element numbers over the element groups.

Returns two lists of element sets, the first in global numbering, the second in group numbering. Each item contains the element numbers from the given set that belong to the corresponding group.

elemNrs(group, set)

Return the global element numbers for elements set in group

getElems(sets)

Return the definitions of the elements in sets.

sets should be a list of element sets with length equal to the number of element groups. Each set contains element numbers local to that group.

As the elements can be grouped according to plexitude, this function returns a list of element arrays matching the element groups in self.elems. Some of these arrays may be empty.

It also provide the global and group element numbers, since they had to be calculated anyway.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

copy()

Return a deep copy of the object.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

renumber(old=None, new=None)

Renumber a set of nodes.

old and new are equally sized lists with unique node numbers, each smaller that the number of nodes in the model. The old numbers will be renumbered to the new numbers. If one of the lists is None, a range with the length of the other is used. If the lists are shorter than the number of nodes, the remaining nodes will be numbered in an unspecified order. If both lists are None, the nodes are renumbered randomly.

This function returns a tuple (old,new) with the full renumbering vectors used. The first gives the old node numbers of the current numbers, the second gives the new numbers cooresponding with the old ones.

Functions defined in module fe

fe.mergedModel(meshes, **kargs)

Returns the fe Model obtained from merging individual meshes.

The input arguments are (coords,elems) tuples. The return value is a merged fe Model.

pyformex-0.8.6/pyformex/doc/html/ref/mesh_ext.html0000644000211500021150000004010711705104254022073 0ustar benebene00000000000000 31. mesh_ext — Extended functionality of the Mesh class. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

30. curve — Definition of curves in pyFormex.

Next topic

32. trisurface — Operations on triangulated surfaces.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

31. mesh_ext — Extended functionality of the Mesh class.

This module defines extended Mesh functionality which is considered to be experimental, maybe incomplete or even buggy.

The functions in this module can be called as functions operating on a Mesh object, but are also available as Mesh methods.

Classes defined in module mesh_ext

Functions defined in module mesh_ext

mesh_ext.report(self)

Create a report on the Mesh shape and size.

The report contains the number of nodes, number of elements, plexitude, element type, bbox and size.

mesh_ext.alt_report(self)

Create a report on the Mesh shape and size.

The report contains the number of nodes, number of elements, plexitude, element type, bbox and size.

mesh_ext.nodeFront(self, startat=0, front_increment=1)

Generator function returning the frontal elements.

startat is an element number or list of numbers of the starting front. On first call, this function returns the starting front. Each next() call returns the next front.

mesh_ext.partitionByNodeFront(self, firstprop=0, startat=0)

Detects different parts of the Mesh using a frontal method.

okedges flags the edges where the two adjacent elems are to be in the same part of the Mesh. startat is a list of elements that are in the first part. The partitioning is returned as a property type array having a value corresponding to the part number. The lowest property number will be firstprop.

mesh_ext.partitionByConnection(self)

Detect the connected parts of a Mesh.

The Mesh is partitioned in parts in which all elements are connected. Two elements are connected if it is possible to draw a continuous (poly)line from a point in one element to a point in the other element without leaving the Mesh. The partitioning is returned as a property type array having a value corresponding to the part number. The lowest property number will be firstprop.

mesh_ext.splitByConnection(self)

Split the Mesh into connected parts.

Returns a list of Meshes that each form a connected part.

mesh_ext.largestByConnection(self)

Return the largest connected part of the Mesh.

mesh_ext.rings(self, sources, nrings)

It finds the rings of elems connected to sources by node.

Sources is a list of elem indices. A list of rings is returned, from zero (equal to sources) to step. If step is -1, all rings are returned.

mesh_ext.correctNegativeVolumes(self)

Modify the connectivity of negative-volume elements to make positive-volume elements.

Negative-volume elements (hex or tet with inconsistent face orientation) may appear by error during geometrical trnasformations (e.g. reflect, sweep, extrude, revolve). This function fixes those elements. Currently it only works with linear tet and hex.

mesh_ext.scaledJacobian(self, scaled=True)

Returns a quality measure for volume meshes.

If scaled if False, it returns the Jacobian at the corners of each element. If scaled is True, it returns a quality metrics, being the minumum value of the scaled Jacobian in each element (at one corner, the Jacobian divided by the volume of a perfect brick). Each tet or hex element gives a value between -1 and 1. Acceptable elements have a positive scaled Jacobian. However, good quality requires a minimum of 0.2. Quadratic meshes are first converted to linear. If the mesh contain mainly negative Jacobians, it probably has negative volumes and can be fixed with the correctNegativeVolumes.

mesh_ext.areas(self)

area of elements

For surface element the faces’ area is returned. For volume elements the sum of the faces’areas is returned.

mesh_ext.area(self)

Return the total area of the Mesh.

For a Mesh with dimensionality 2, the total area of the Mesh is returned. For a Mesh with dimensionality 3, the total area of all the element faces is returned. Use Mesh.getBorderMesh().area() if you only want the total area of the border faces. For a Mesh with dimensionality < 2, 0 is returned.

mesh_ext.partitionByAngle(self, **arg)

Partition a surface Mesh by the angle between adjacent elements.

The Mesh is partitioned in parts bounded by the sharp edges in the surface. The arguments and return value are the same as in TriSurface.partitionByAngle().

Currently this only works for ‘tri3’ and ‘quad4’ type Meshes. Also, the ‘quad4’ partitioning method currently only works corectly if the quads are nearly planar.

mesh_ext.initialize()

Initialize the Mesh extensions.

Calling this function will install some of the mesh functions defined in this modules as Mesh methods.

pyformex-0.8.6/pyformex/doc/html/ref/colors.html0000644000211500021150000002535511705104247021572 0ustar benebene00000000000000 6. colors — Definition of some RGB colors and color conversion functions — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

5. draw — Create 3D graphical representations.

Next topic

7. geometry — A generic interface to the Coords transformation methods

[FSF Associate Member]

Valid XHTML 1.0 Transitional

6. colors — Definition of some RGB colors and color conversion functions

Classes defined in module colors

Functions defined in module colors

colors.GLColor(color)

Convert a color to an OpenGL RGB color.

The output is a tuple of three RGB float values ranging from 0.0 to 1.0. The input can be any of the following: - a QColor - a string specifying the Xwindow name of the color - a hex string ‘#RGB’ with 1 to 4 hexadecimal digits per color - a tuple or list of 3 integer values in the range 0..255 - a tuple or list of 3 float values in the range 0.0..1.0 Any other input may give unpredictable results.

colors.colorName(color)

Return a string designation for the color.

color can be anything that is accepted by GLColor. In most cases If color can not be converted, None is returned.

colors.RGBcolor(color)

Return an RGB (0-255) tuple for an OpenGL color

colors.WEBcolor(color)

Return an RGB hex string for an OpenGL color

colors.closestColorName(color)

Return the closest color name.

colors.RGBA(rgb, alpha=1.0)

Adds an alpha channel to an RGB color

colors.GREY(val, alpha=1.0)

Returns a grey OpenGL color of given intensity (0..1)

pyformex-0.8.6/pyformex/doc/html/ref/postproc.html0000644000211500021150000002224511705104254022133 0ustar benebene00000000000000 44. postproc — Postprocessing functions — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

43. fe_post — A postprocessor for ABAQUS output files.

Next topic

45. flavia

[FSF Associate Member]

Valid XHTML 1.0 Transitional

44. postproc — Postprocessing functions

Postprocessing means collecting a geometrical model and computed values from a numerical simulation, and render the values on the domain.

Classes defined in module postproc

Functions defined in module postproc

postproc.frameScale(nframes=10, cycle='up', shape='linear')

Return a sequence of scale values between -1 and +1.

nframes : the number of steps between 0 and -1/+1 values.

cycle: determines how subsequent cycles occur:

'up': ramping up

'updown': ramping up and down

'revert': ramping up and down then reverse up and down

shape: determines the shape of the amplitude curve:

'linear': linear scaling

'sine': sinusoidal scaling

pyformex-0.8.6/pyformex/doc/html/ref/decors.html0000644000211500021150000010030711705104251021532 0ustar benebene00000000000000 18. decors — 2D decorations for the OpenGL canvas. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

17. actors — OpenGL actors for populating the 3D scene.

Next topic

19. marks — OpenGL marks for annotating 3D actors.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

18. decors — 2D decorations for the OpenGL canvas.

Classes defined in module decors

class decors.Decoration(x, y, **kargs)

A decoration is a 2-D drawing at canvas position x,y.

All decorations have at least the following attributes:

  • x,y : (int) window coordinates of the insertion point

  • drawGL() : function that draws the decoration at (x,y).

    This should only use openGL function that are allowed in a display list.

drawGL(**kargs)

Perform the OpenGL drawing functions to display the actor.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class decors.Mark(x, y, mark='dot', color=None, linewidth=None, **kargs)

A mark at a fixed position on the canvas.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class decors.Line(x1, y1, x2, y2, color=None, linewidth=None, **kargs)

A straight line on the canvas.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class decors.GlutText(text, x, y, font='9x15', size=None, gravity=None, color=None, zoom=None, **kargs)

A viewport decoration showing a text string.

  • text: a simple string, a multiline string or a list of strings. If it is a string, it will be splitted on the occurrence of ‘n’ characters.
  • x,y: insertion position on the canvas
  • gravity: a string that determines the adjusting of the text with respect to the insert position. It can be a combination of one of the characters ‘N or ‘S’ to specify the vertical positon, and ‘W’ or ‘E’ for the horizontal. The default(empty) string will center the text.
drawGL(**kargs)

Draw the text.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

decors.Text

alias of GlutText

class decors.ColorLegend(colorlegend, x, y, w, h, ngrid=0, linewidth=None, nlabel=-1, font=None, size=None, dec=2, scale=0, lefttext=False, **kargs)

A labeled colorscale legend.

When showing the distribution of some variable over a domain by means of a color encoding, the viewer expects some labeled colorscale as a guide to decode the colors. The ColorLegend decoration provides such a color legend. This class only provides the visual details of the scale. The conversion of the numerical values to the matching colors is provided by the colorscale.ColorLegend class.

Parameters:

  • colorlegend: a colorscale.ColorLegend instance providing conversion between numerical values and colors
  • x,y,w,h: four integers specifying the position and size of the color bar rectangle
  • ngrid: int: number of intervals for the grid lines to be shown. If > 0, grid lines are drawn around the color bar and between the ngrid intervals. If = 0, no grid lines are drawn. If < 0 (default), the value is set equal to the number of colors (as set in the colorlegend) or to 0 if this number is higher than 50.
  • linewidth: float: width of the grid lines. If not specified, the current canvas line width is used.
  • nlabel: int: number of intervals for the labels to be shown. If > 0, labels will be displayed at nlabel interval borders, if possible. The number of labels displayed thus will be nlabel+1, or less if the labels would otherwise be too close or overlapping. If 0, no labels are shown. If < 0 (default), a default number of labels is shown.
  • font, size: font and size to be used for the labels
  • dec: int: number of decimals to be used in the labels
  • scale: int: exponent of 10 for the scaling factor of the label values. The displayed values will be equal to the real values multiplied with 10**scale.
  • lefttext: bool: if True, the labels will be drawn to the left of the color bar. The default is to draw the labels at the right.

Some practical guidelines:

  • The number of colors is defined by the colorlegend argument.
  • Large numbers of colors result inb a quasi continuous color scheme.
  • With a high number of colors, grid lines disturb the image, so either use ngrid=0 or ngrid= to only draw a border around the colors.
  • With a small number of colors, set ngrid = len(colorlegend.colors) to add gridlines between each color. Without it, the individual colors in the color bar may seem to be not constant, due to an optical illusion. Adding the grid lines reduces this illusion.
  • When using both grid lines and labels, set both ngrid and nlabel to the same number or make one a multiple of the other. Not doing so may result in a very confusing picture.
  • The best practices are to use either a low number of colors (<=20) and the default ngrid and nlabel, or a high number of colors (>=200) and the default values or a low value for nlabel.

The ColorScale example script provides opportunity to experiment with different settings.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class decors.Rectangle(x1, y1, x2, y2, color=None, **kargs)

A 2D-rectangle on the canvas.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class decors.Grid(x1, y1, x2, y2, nx=1, ny=1, color=None, linewidth=None, **kargs)

A 2D-grid on the canvas.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class decors.LineDrawing(data, color=None, linewidth=None, **kargs)

A collection of straight lines on the canvas.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class decors.Triade(pos='lb', siz=100, pat='3:012934', legend='xyz', color=[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0), (0.0, 1.0, 1.0), (1.0, 0.0, 1.0), (1.0, 1.0, 0.0)], **kargs)

An OpenGL actor representing a triade of global axes.

  • pos: position on the canvas: two characters, of which first sets

    horizontal position (‘l’, ‘c’ or ‘r’) and second sets vertical position (‘b’, ‘c’ or ‘t’).

  • size: size in pixels of the zone displaying the triade.

  • pat: shape to be drawn in the coordinate planes. Default is a square. ‘16’ givec a triangle. ‘’ disables the planes.

  • legend: text symbols to plot at the end of the axes. A 3-character string or a tuple of 3 strings.

drawGL(**kargs)

Perform the OpenGL drawing functions to display the actor.

pickGL(**kargs)

Mimick the OpenGL drawing functions to pick (from) the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

Functions defined in module decors

decors.drawDot(x, y)

Draw a dot at canvas coordinates (x,y).

decors.drawLine(x1, y1, x2, y2)

Draw a straight line from (x1,y1) to (x2,y2) in canvas coordinates.

decors.drawGrid(x1, y1, x2, y2, nx, ny)

Draw a rectangular grid of lines

The rectangle has (x1,y1) and and (x2,y2) as opposite corners. There are (nx,ny) subdivisions along the (x,y)-axis. So the grid has (nx+1) * (ny+1) lines. nx=ny=1 draws a rectangle. nx=0 draws 1 vertical line (at x1). nx=-1 draws no vertical lines. ny=0 draws 1 horizontal line (at y1). ny=-1 draws no horizontal lines.

decors.drawRect(x1, y1, x2, y2)

Draw the circumference of a rectangle.

decors.drawRectangle(x1, y1, x2, y2, color)

Draw a single rectangular quad.

pyformex-0.8.6/pyformex/doc/html/ref/elements.html0000644000211500021150000004033011705104252022067 0ustar benebene00000000000000 12. elements — Definition of elements. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

11. utils — A collection of miscellaneous utility functions.

Next topic

13. mesh — Finite element meshes in pyFormex.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

12. elements — Definition of elements.

This modules allows for a consistent local numbering scheme of element connectivities throughout pyFormex. When interfacing with other programs, one should be aware that conversions may be necessary. Conversions to/from external programs should be done by the interface modules.

Classes defined in module elements

class elements.Element(name, doc, ndim, vertices, edges=(''[], ), faces=(''[], ), **kargs)

Element base class: an empty element.

All derived classes should have a capitalized name: starting with an uppercase character and further only lower case and digits.

Each element is defined by the following attributes:

  • vertices: the natural coordinates of its vertices,
  • edges: a list of edges, each defined by 2 or 3 node numbers,
  • faces: a list of faces, each defined by a list of minimum 3 node numbers,
  • element: a list of all node numbers
  • drawfaces: a list of faces to be drawn, if different from faces. This is an optional attribute. If defined, it will be used instead of the faces attribute to draw the element. This can e.g. be used to draw approximate representations for higher order elements for which there is no correct drawing function.

The vertices of the elements are defined in a unit space [0,1] in each axis direction.

The elements guarantee a fixed local numbering scheme of the vertices. One should however not rely on a specific numbering scheme of edges, faces or elements. For solid elements, it is guaranteed that the vertices of all faces are numbered in a consecutive order spinning positively around the outward normal on the face.

The list of available element types can be found by:

>>> printElementTypes()
Available Element Types:
  0-dimensional elements: ['point']
  1-dimensional elements: ['line2', 'line3']
  2-dimensional elements: ['tri3', 'tri6', 'quad4', 'quad6', 'quad8', 'quad9']
  3-dimensional elements: ['tet4', 'tet10', 'tet14', 'tet15', 'wedge6', 'hex8', 'hex16', 'hex20', 'icosa']

Optional attributes:

  • conversions: Defines possible strategies for conversion of the element to other element types. It is a dictionary with the target element name as key, and a list of actions as value. Each action in the list consists of a tuple ( action, data ), where action is one of the action identifier characters defined below, and data are the data needed for this action.

Conversion actions:

‘m’: add new nodes to the element by taking the mean values of existing
nodes. data is a list of tuples containing the nodes numbers whose coorrdinates have to be averaged.
‘s’: select nodes from the existing ones. data is a list of the node numbers
to retain in the new element. This can be used to reduce the plexitude but also just to reorder the existing nodes.
‘v’: perform a conversion via an intermediate type. data is the name of the
intermediate element type. The current element will first be converted to the intermediate type, and then conversion from that type to the target will be attempted.
‘r’: randomly choose one of the possible conversions. data is a list of
element names. This can e.g. be used to select randomly between different but equivalent conversion paths.
getEntities(level, reduce=False)

Return the type and connectivity table of some element entities.

The full list of entities with increasing dimensionality 0,1,2,3 is:

['points', 'edges', 'faces', 'cells' ]

If level is negative, the dimensionality returned is relative to the highest dimensionality (.i.e., that of the element). If it is positive, it is taken absolute.

Thus, for a 3D element type, getEntities(-1) returns the faces, while for a 2D element type, it returns the edges. For both types however, getLowerEntities(+1) returns the edges.

The return value is a dict where the keys are element types and the values are connectivity tables. If reduce == False: there will be only one connectivity table and it may include degenerate elements. If reduce == True, an attempt is made to reduce the degenerate elements. The returned dict may then have multiple entries.

If the requested entity level is outside the range 0..ndim, the return value is None.

Functions defined in module elements

elements.elementType(name=None, nplex=-1)

Return the requested element type

Parameters:

  • name: a string (case ignored) with the name of an element. If not specified, or the named element does not exist, the default element for the specified plexitude is returned.
  • nplex: plexitude of the element. If specified and no element name was given, the default element type for this plexitude is returned.

Returns: a subclass of Element

Errors: if neither name nor nplex can resolve into an element type,
an error is raised.

Example:

>>> elementType('tri3').name()
'tri3'
>>> elementType(nplex=2).name()
'line2'
elements.elementTypes(ndim=None)

Return the names of available elements.

If a value is specified for ndim, only the elements with the matching dimensionality are returned.

elements.printElementTypes()

Print all available element types.

Prints a list of the names of all availabale element types, grouped by their dimensionality.

pyformex-0.8.6/pyformex/doc/html/ref/viewport.html0000644000211500021150000016425211705104256022150 0ustar benebene00000000000000 22. viewport — Interactive OpenGL Canvas embedded in a Qt4 widget. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

21. canvas — This implements an OpenGL drawing widget for painting 3D scenes.

Next topic

23. camera — OpenGL camera handling

[FSF Associate Member]

Valid XHTML 1.0 Transitional

22. viewport — Interactive OpenGL Canvas embedded in a Qt4 widget.

This module implements user interaction with the OpenGL canvas defined in module canvas. QtCanvas is a single interactive OpenGL canvas, while MultiCanvas implements a dynamic array of multiple canvases.

Classes defined in module viewport

class viewport.CursorShapeHandler(widget)

A class for handling the mouse cursor shape on the Canvas.

setCursorShape(shape)

Set the cursor shape to shape

setCursorShapeFromFunc(func)

Set the cursor shape to shape

class viewport.CanvasMouseHandler

A class for handling the mouse events on the Canvas.

getMouseFunc()

Return the mouse function bound to self.button and self.mod

class viewport.QtCanvas(*args)

A canvas for OpenGL rendering.

This class provides interactive functionality for the OpenGL canvas provided by the canvas.Canvas class.

Interactivity is highly dependent on Qt4. Putting the interactive functions in a separate class makes it esier to use the Canvas class in non-interactive situations or combining it with other GUI toolsets.

resetOptions()

Reset the Drawing options to some defaults

setOptions(d)

Set the Drawing options to some values

setCursorShape(shape)

Set the cursor shape to shape

setCursorShapeFromFunc(func)

Set the cursor shape to shape

getMouseFunc()

Return the mouse function bound to self.button and self.mod

mouse_rectangle_zoom(x, y, action)

Process mouse events during interactive rectangle zooming.

On PRESS, record the mouse position. On MOVE, create a rectangular zoom window. On RELEASE, zoom to the picked rectangle.

setPickable(nrs=None)

Set the list of pickable actors

start_selection(mode, filter)

Start an interactive picking mode.

If selection mode was already started, mode is disregarded and this can be used to change the filter method.

wait_selection()

Wait for the user to interactively make a selection.

finish_selection()

End an interactive picking mode.

accept_selection(clear=False)

Accept or cancel an interactive picking mode.

If clear == True, the current selection is cleared.

cancel_selection()

Cancel an interactive picking mode and clear the selection.

pick(mode='actor', oneshot=False, func=None, filter=None)

Interactively pick objects from the viewport.

  • mode: defines what to pick : one of ['actor','element','point','number','edge']

  • oneshot: if True, the function returns as soon as the user ends a picking operation. The default is to let the user modify his selection and only to return after an explicit cancel (ESC or right mouse button).

  • func: if specified, this function will be called after each atomic pick operation. The Collection with the currently selected objects is passed as an argument. This can e.g. be used to highlight the selected objects during picking.

  • filter: defines what elements to retain from the selection: one of [None,'single','closest,'connected'].

    • None (default) will return the complete selection.

    • ‘closest’ will only keep the element closest to the user.

    • ‘connected’ will only keep elements connected to - the closest element (set picked) - what is already in the selection (add picked).

      Currently this only works when picking mode is ‘element’ and for Actors having a partitionByConnection method.

When the picking operation is finished, the selection is returned. The return value is always a Collection object.

pickNumbers(*args, **kargs)

Go into number picking mode and return the selection.

idraw(mode='point', npoints=-1, zplane=0.0, func=None, coords=None, preview=False)

Interactively draw on the canvas.

This function allows the user to interactively create points in 3D space and collects the subsequent points in a Coords object. The interpretation of these points is left to the caller.

  • mode: one of the drawing modes, specifying the kind of objects you want to draw. This is passed to the specified func.
  • npoints: If -1, the user can create any number of points. When >=0, the function will return when the total number of points in the collection reaches the specified value.
  • zplane: the depth of the z-plane on which the 2D drawing is done.
  • func: a function that is called after each atomic drawing operation. It is typically used to draw a preview using the current set of points. The function is passed the current Coords and the mode as arguments.
  • coords: an initial set of coordinates to which the newly created points should be added. If specified, npoints also counts these initial points.
  • preview: Experimental If True, the preview funcion will also be called during mouse movement with a pressed button, allowing to preview the result before a point is created.

The drawing operation is finished when the number of requested points has been reached, or when the user clicks the right mouse button or hits ‘ENTER’. The return value is a (n,3) shaped Coords array.

start_draw(mode, zplane, coords)

Start an interactive drawing mode.

finish_draw()

End an interactive drawing mode.

accept_draw(clear=False)

Cancel an interactive drawing mode.

If clear == True, the current drawing is cleared.

cancel_draw()

Cancel an interactive drawing mode and clear the drawing.

mouse_draw(x, y, action)

Process mouse events during interactive drawing.

On PRESS, do nothing. On MOVE, do nothing. On RELEASE, add the point to the point list.

start_drawing(mode)

Start an interactive line drawing mode.

wait_drawing()

Wait for the user to interactively draw a line.

finish_drawing()

End an interactive drawing mode.

accept_drawing(clear=False)

Cancel an interactive drawing mode.

If clear == True, the current drawing is cleared.

cancel_drawing()

Cancel an interactive drawing mode and clear the drawing.

edit_drawing(mode)

Edit an interactive drawing.

drawLinesInter(mode='line', oneshot=False, func=None)

Interactively draw lines on the canvas.

  • oneshot: if True, the function returns as soon as the user ends a drawing operation. The default is to let the user draw multiple lines and only to return after an explicit cancel (ESC or right mouse button).
  • func: if specified, this function will be called after each atomic drawing operation. The current drawing is passed as an argument. This can e.g. be used to show the drawing.

When the drawing operation is finished, the drawing is returned. The return value is a (n,2,2) shaped array.

dynarot(x, y, action)

Perform dynamic rotation operation.

This function processes mouse button events controlling a dynamic rotation operation. The action is one of PRESS, MOVE or RELEASE.

dynapan(x, y, action)

Perform dynamic pan operation.

This function processes mouse button events controlling a dynamic pan operation. The action is one of PRESS, MOVE or RELEASE.

dynazoom(x, y, action)

Perform dynamic zoom operation.

This function processes mouse button events controlling a dynamic zoom operation. The action is one of PRESS, MOVE or RELEASE.

wheel_zoom(delta)

Zoom by rotating a wheel over an angle delta

emit_done(x, y, action)

Emit a DONE event by clicking the mouse.

This is equivalent to pressing the ENTER button.

emit_cancel(x, y, action)

Emit a CANCEL event by clicking the mouse.

This is equivalent to pressing the ESC button.

draw_state_rect(x, y)

Store the pos and draw a rectangle to it.

mouse_pick(x, y, action)

Process mouse events during interactive picking.

On PRESS, record the mouse position. On MOVE, create a rectangular picking window. On RELEASE, pick the objects inside the rectangle.

pick_actors()

Set the list of actors inside the pick_window.

pick_parts(obj_type, max_objects, store_closest=False)

Set the list of actor parts inside the pick_window.

obj_type can be ‘element’, ‘edge’ or ‘point’ ‘edge’ is only available for mesh type geometry max_objects specifies the maximum number of objects

The picked object numbers are stored in self.picked. If store_closest==True, the closest picked object is stored in as a tuple ( [actor,object] ,distance) in self.picked_closest

A list of actors from which can be picked may be given. If so, the resulting keys are indices in this list. By default, the full actor list is used.

pick_elements()

Set the list of actor elements inside the pick_window.

pick_points()

Set the list of actor points inside the pick_window.

pick_edges()

Set the list of actor edges inside the pick_window.

pick_numbers()

Return the numbers inside the pick_window.

draw_state_line(x, y)

Store the pos and draw a line to it.

mouse_draw_line(x, y, action)

Process mouse events during interactive drawing.

On PRESS, record the mouse position. On MOVE, draw a line. On RELEASE, add the line to the drawing.

mousePressEvent(e)

Process a mouse press event.

mouseMoveEvent(e)

Process a mouse move event.

mouseReleaseEvent(e)

Process a mouse release event.

wheelEvent(e)

Process a wheel event.

do_lighting(onoff)

Toggle lights on/off.

has_lighting()

Return the status of the lighting.

resetDefaults(dict={})

Return all the settings to their default values.

setAmbient(ambient)

Set the global ambient lighting for the canvas

setMaterial(matname)

Set the default material light properties for the canvas

resetLighting()

Change the light parameters

setRenderMode(mode, lighting=None)

Set the rendering mode.

This sets or changes the rendermode and lighting attributes. If lighting is not specified, it is set depending on the rendermode.

If the canvas has not been initialized, this merely sets the attributes self.rendermode and self.lighting. If the canvas was already initialized (it has a camera), and one of the specified settings is fdifferent from the existing, the new mode is set, the canvas is re-initialized according to the newly set mode, and everything is redrawn with the new mode.

setLineWidth(lw)

Set the linewidth for line rendering.

setLineStipple(repeat, pattern)

Set the linestipple for line rendering.

setPointSize(sz)

Set the size for point drawing.

setBgColor(color1, color2=None, mode='solid')

Set the background color.

If one color is specified, a solid background is set. If two colors are specified, a graded background is set and an object is created to display the background.

createBackground()

Create the background object.

setFgColor(color)

Set the default foreground color.

setSlColor(color)

Set the highlight color.

setTriade(on=None, pos='lb', siz=100)

Toggle the display of the global axes on or off.

If on is True, a triade of global axes is displayed, if False it is removed. The default (None) toggles between on and off.

glinit()

Initialize the rendering machine.

The rendering machine is initialized according to: - self.rendermode: one of - self.lighting

glupdate()

Flush all OpenGL commands, making sure the display is updated.

clear()

Clear the canvas to the background color.

setDefaults()

Activate the canvas settings in the GL machine.

display()

(Re)display all the actors in the scene.

This should e.g. be used when actors are added to the scene, or after changing camera position/orientation or lens.

begin_2D_drawing()

Set up the canvas for 2D drawing on top of 3D canvas.

The 2D drawing operation should be ended by calling end_2D_drawing. It is assumed that you will not try to change/refresh the normal 3D drawing cycle during this operation.

end_2D_drawing()

Cancel the 2D drawing mode initiated by begin_2D_drawing.

setBbox(bbox=None)

Set the bounding box of the scene you want to be visible.

addActor(actor)

Add a 3D actor to the 3D scene.

removeActor(actor)

Remove a 3D actor from the 3D scene.

addHighlight(actor)

Add a 3D actor highlight to the 3D scene.

removeHighlight(actor)

Remove a 3D actor highlight from the 3D scene.

addAnnotation(actor)

Add an annotation to the 3D scene.

removeAnnotation(actor)

Remove an annotation from the 3D scene.

addDecoration(actor)

Add a 2D decoration to the canvas.

removeDecoration(actor)

Remove a 2D decoration from the canvas.

remove(itemlist)

Remove a list of any actor/highlights/annotation/decoration items.

This will remove the items from any of the canvas lists in which the item appears. itemlist can also be a single item instead of a list.

removeActors(actorlist=None)

Remove all actors in actorlist (default = all) from the scene.

removeHighlights(actorlist=None)

Remove all highlights in actorlist (default = all) from the scene.

removeAnnotations(actorlist=None)

Remove all annotations in actorlist (default = all) from the scene.

removeDecorations(actorlist=None)

Remove all decorations in actorlist (default = all) from the scene.

removeAll()

Remove all actors and decorations

redrawAll()

Redraw all actors in the scene.

setCamera(bbox=None, angles=None)

Sets the camera looking under angles at bbox.

This function sets the camera angles and adjusts the zooming. The camera distance remains unchanged.

If a bbox is specified, the camera will be zoomed to make the whole bbox visible. If no bbox is specified, the current scene bbox will be used. If no current bbox has been set, it will be calculated as the bbox of the whole scene.

If no camera angles are given, the camera orientation is kept. angles can be a set of 3 angles, or a string

project(x, y, z, locked=False)

Map the object coordinates (x,y,z) to window coordinates.

unProject(x, y, z, locked=False)

Map the window coordinates (x,y,z) to object coordinates.

zoom(f, dolly=True)

Dolly zooming.

Zooms in with a factor f by moving the camera closer to the scene. This does noet change the camera’s FOV setting. It will change the perspective view though.

zoomRectangle(x0, y0, x1, y1)

Rectangle zooming.

Zooms in/out by changing the area and position of the visible part of the lens. Unlike zoom(), this does not change the perspective view.

x0,y0,x1,y1 are pixel coordinates of the lower left and upper right corners of the area of the lens that will be mapped on the canvas viewport. Specifying values that lead to smaller width/height will zoom in.

zoomCentered(w, h, x=None, y=None)

Rectangle zooming with specified center.

This is like zoomRectangle, but the zoom rectangle is specified by its center and size, which may be more appropriate when using off-center zooming.

zoomAll()

Rectangle zoom to make full scene visible.

saveBuffer()

Save the current OpenGL buffer

showBuffer()

Show the saved buffer

draw_focus_rectangle(width=2)

Draw the focus rectangle.

The specified width is HALF of the line width

draw_cursor(x, y)

draw the cursor

class viewport.NewMultiCanvas(parent=None)

An OpenGL canvas with multiple viewports and QT interaction.

The MultiCanvas implements a central QT widget containing one or more QtCanvas widgets.

changeLayout(nvps=None, ncols=None, nrows=None, pos=None, rstretch=None, cstretch=None)

Change the lay-out of the viewports on the OpenGL widget.

nvps: number of viewports ncols: number of columns nrows: number of rows pos: list holding the position and span of each viewport [[row,col,rowspan,colspan],...] rstretch: list holding the stretch factor for each row cstretch: list holding the stretch factor for each column (rows/columns with a higher stretch factor take more of the available space) Each of this parameters is optional.

If pos is given, it specifies all viewports and nvps, nrows and ncols are disregarded.

Else:

If nvps is given, it specifies the number of viewports in the layout. Else, nvps will be set to the current number of viewports.

If ncols is an int, viewports are laid out rowwise over ncols columns and nrows is ignored. If ncols is None and nrows is an int, viewports are laid out columnwise over nrows rows.

If nvps is not equal to the current number of viewports, viewports will be added or removed to match the requested number.

By default they are laid out rowwise over two columns.

createView(shared=None)

Create a new viewport

If another QtCanvas instance is passed, both will share the same display lists and textures.

addView(view, row, col, rowspan=1, colspan=1)

Add a new viewport and make it visible

removeView(view=None)

Remove a view from the canvas

If view is None, the last one is removed. You can not remove a view when there is only one left.

setCurrent(view)

Make the specified viewport the current one.

view can be either a viewport or viewport number.

setStretch(rowstretch, colstretch)

Set the row and column stretch factors.

rowstretch and colstretch are lists of stretch factors to be applied on the subsequent rows/columns. If the lists are shorter than the number of rows/columns, the

Link viewport vp to to

class viewport.FramedGridLayout(parent=None)

A QtGui.QGridLayout where each added widget is framed.

class viewport.MultiCanvas(parent=None)

An OpenGL canvas with multiple viewports and QT interaction.

The MultiCanvas implements a central QT widget containing one or more QtCanvas widgets.

newView(shared=None)

Create a new viewport

If another QtCanvas instance is passed, both will share the same display lists and textures.

addView()

Add a new viewport to the widget

setCurrent(canv)

Make the specified viewport the current one.

canv can be either a viewport or viewport number.

showWidget(w)

Show the view w.

changeLayout(nvps=None, ncols=None, nrows=None, pos=None, rstretch=None, cstretch=None)

Change the lay-out of the viewports on the OpenGL widget.

nvps: number of viewports ncols: number of columns nrows: number of rows pos: list holding the position and span of each viewport [[row,col,rowspan,colspan],...] rstretch: list holding the stretch factor for each row cstretch: list holding the stretch factor for each column (rows/columns with a higher stretch factor take more of the available space) Each of this parameters is optional.

If a number of viewports is given, viewports will be added or removed to match the requested number. By default they are laid out rowwise over two columns.

If ncols is an int, viewports are laid out rowwise over ncols columns and nrows is ignored. If ncols is None and nrows is an int, viewports are laid out columnwise over nrows rows. Alternatively, the pos argument can be used to specify the layout of the viewports.

Link viewport vp to to

Functions defined in module viewport

viewport.dotpr(v, w)

Return the dot product of vectors v and w

viewport.length(v)

Return the length of the vector v

viewport.projection(v, w)

Return the (signed) length of the projection of vector v on vector w.

viewport.setOpenGLFormat()

Set the correct OpenGL format.

On a correctly installed system, the default should do well. The default OpenGL format can be changed by command line options:

--dri   : use the Direct Rendering Infrastructure
--nodri : do not use the DRI
--alpha : enable the alpha buffer 
viewport.OpenGLFormat(fmt=None)

Some information about the OpenGL format.

pyformex-0.8.6/pyformex/doc/html/ref/export.html0000644000211500021150000001756111705104252021606 0ustar benebene00000000000000 49. export — Classes and functions for exporting geometry in various formats. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

48. dxf — Read/write geometry in DXF format.

Next topic

50. tetgen — Interface with tetgen

[FSF Associate Member]

Valid XHTML 1.0 Transitional

49. export — Classes and functions for exporting geometry in various formats.

Classes defined in module export

Functions defined in module export

pyformex-0.8.6/pyformex/doc/html/ref/lima.html0000644000211500021150000002352711705104253021207 0ustar benebene00000000000000 46. lima — Lindenmayer Systems — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

45. flavia

Next topic

47. turtle — Turtle graphics for pyFormex

[FSF Associate Member]

Valid XHTML 1.0 Transitional

46. lima — Lindenmayer Systems

Classes defined in module lima

class lima.Lima(axiom='', rules={})

A class for operations on Lindenmayer Systems.

status()

Print the status of the Lima

addRule(atom, product)

Add a new rule (or overwrite an existing)

translate(rule, keep=False)

Translate the product by the specified rule set.

If keep=True is specified, atoms that do not have a translation in the rule set, will be kept unchanged. The default (keep=False) is to remove those atoms.

Functions defined in module lima

lima.lima(axiom, rules, level, turtlecmds, glob=None)

Create a list of connected points using a Lindenmayer system.

axiom is the initial string, rules are translation rules for the characters in the string, level is the number of generations to produce, turtlecmds are the translation rules of the final string to turtle cmds, glob is an optional list of globals to pass to the turtle script player.

This is a convenience function for quickly creating a drawing of a single generation member. If you intend to draw multiple generations of the same Lima, it is better to use the grow() and translate() methods directly.

pyformex-0.8.6/pyformex/doc/html/ref/actors.html0000644000211500021150000007406611705104247021567 0ustar benebene00000000000000 17. actors — OpenGL actors for populating the 3D scene. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

16. colorscale — Color mapping of a range of values.

Next topic

18. decors — 2D decorations for the OpenGL canvas.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

17. actors — OpenGL actors for populating the 3D scene.

Classes defined in module actors

class actors.Actor(**kargs)

An Actor is anything that can be drawn in an OpenGL 3D Scene.

The visualisation of the Scene Actors is dependent on camera position and angles, clipping planes, rendering mode and lighting.

An Actor subclass should minimally reimplement the following methods:

  • bbox(): return the actors bounding box.
  • drawGL(mode): to draw the actor. Takes a mode argument so the drawing function can act differently depending on the mode. There are currently 5 modes: wireframe, flat, smooth, flatwire, smoothwire. drawGL should only contain OpenGL calls that are allowed inside a display list. This may include calling the display list of another actor but not creating a new display list.

The interactive picking functionality requires the following methods, for which we porvide do-nothing defaults here:

  • npoints():
  • nelems():
  • pickGL():
bbox()

Default implementation for bbox().

drawGL(**kargs)

Perform the OpenGL drawing functions to display the actor.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class actors.TranslatedActor(A, trl=(0.0, 0.0, 0.0), **kargs)

An Actor translated to another position.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class actors.RotatedActor(A, rot=(1.0, 0.0, 0.0), twist=0.0, **kargs)

An Actor rotated to another position.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class actors.CubeActor(size=1.0, color=[(1.0, 0.0, 0.0), (0.0, 1.0, 1.0), (0.0, 1.0, 0.0), (1.0, 0.0, 1.0), (0.0, 0.0, 1.0), (1.0, 1.0, 0.0)], **kargs)

An OpenGL actor with cubic shape and 6 colored sides.

drawGL(**kargs)

Draw the cube.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class actors.SphereActor(size=1.0, color=None, **kargs)

An OpenGL actor representing a sphere.

drawGL(**kargs)

Draw the cube.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class actors.BboxActor(bbox, color=None, linewidth=None, **kargs)

Draws a bbox.

drawGL(**kargs)

Always draws a wireframe model of the bbox.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class actors.AxesActor(cs=None, size=1.0, color=[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)], colored_axes=True, draw_planes=False, linewidth=None, **kargs)

An actor showing the three axes of a CoordinateSystem.

If no CoordinateSystem is specified, the global coordinate system is drawn.

The default actor consists of three colored lines of unit length along the unit vectors of the axes and three colored triangles representing the coordinate planes. This can be modified by the following parameters:

size: scale factor for the unit vectors. color: a set of three colors to use for x,y,z axes. colored_axes = False: draw black axes. draw_planes = False: do not draw the coordinate planes.

drawGL(**kargs)

Draw the axes.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class actors.GridActor(nx=(1, 1, 1), ox=(0.0, 0.0, 0.0), dx=(1.0, 1.0, 1.0), linecolor=(0.0, 0.0, 0.0), linewidth=None, planecolor=(1.0, 1.0, 1.0), alpha=0.2, lines=True, planes=True, **kargs)

Draws a (set of) grid(s) in one of the coordinate planes.

drawGL(**kargs)

Draw the grid.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class actors.CoordPlaneActor(nx=(1, 1, 1), ox=(0.0, 0.0, 0.0), dx=(1.0, 1.0, 1.0), linecolor=(0.0, 0.0, 0.0), linewidth=None, planecolor=(1.0, 1.0, 1.0), alpha=0.5, lines=True, planes=True, **kargs)

Draws a set of 3 coordinate planes.

drawGL(**kargs)

Draw the grid.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class actors.PlaneActor(nx=(2, 2, 2), ox=(0.0, 0.0, 0.0), size=((0.0, 1.0, 1.0), (0.0, 1.0, 1.0)), linecolor=(0.0, 0.0, 0.0), linewidth=None, planecolor=(1.0, 1.0, 1.0), alpha=0.5, lines=True, planes=True, **kargs)

A plane in a 3D scene.

drawGL(**kargs)

Draw the grid.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

setColor(color=None, colormap=None, ncolors=1)

Set the color of the Drawable.

class actors.GeomActor(data, elems=None, eltype=None, color=None, colormap=None, bkcolor=None, bkcolormap=None, alpha=1.0, mode=None, linewidth=None, linestipple=None, marksize=None, **kargs)

An OpenGL actor representing a geometrical model.

The model can either be in Formex or Mesh format.

vertices()

Return the vertives as a 2-dim array.

setColor(color, colormap=None)

Set the color of the Actor.

setBkColor(color, colormap=None)

Set the backside color of the Actor.

setAlpha(alpha)

Set the Actors alpha value.

drawGL(canvas=None, mode=None, color=None, **kargs)

Draw the geometry on the specified canvas.

The drawing parameters not provided by the Actor itself, are derived from the canvas defaults.

mode and color can be overridden for the sole purpose of allowing the recursive use for modes ending on ‘wire’ (‘smoothwire’ or ‘flatwire’). In these cases, two drawing operations are done: one with mode=’wireframe’ and color=black, and one with mode=mode[:-4].

pickGL(mode)

Allow picking of parts of the actor.

mode can be ‘element’, ‘edge’ or ‘point’

select(sel)

Return a GeomActor with a selection of this actor’s elements

Currently, the resulting Actor will not inherit the properties of its parent, but the eltype will be retained.

setLineWidth(linewidth)

Set the linewidth of the Actor.

setLineStipple(linestipple)

Set the linewidth of the Actor.

Functions defined in module actors

pyformex-0.8.6/pyformex/doc/html/ref/fe_post.html0000644000211500021150000002036011705104252021713 0ustar benebene00000000000000 43. fe_post — A postprocessor for ABAQUS output files. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

42. fe_abq — Exporting finite element models in Abaqus™ input file format.

Next topic

44. postproc — Postprocessing functions

[FSF Associate Member]

Valid XHTML 1.0 Transitional

43. fe_post — A postprocessor for ABAQUS output files.

The ABAQUS .fil output file can be scanned and translated into a pyFormex script with the ‘postabq’ command. Use it as follows:

postabq job.fil > job.py

Then execute the created script from inside pyFormex.

Classes defined in module fe_post

Functions defined in module fe_post

pyformex-0.8.6/pyformex/doc/html/ref/sendmail.html0000644000211500021150000002240211705104255022052 0ustar benebene00000000000000 60. sendmail — sendmail.py: a simple program to send an email message — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

59. flatkeydb — Flat Text File Database.

Next topic

61. timer — A timer class.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

60. sendmail — sendmail.py: a simple program to send an email message

(C) 2008 Benedict Verhegghe (benedict.verhegghe@ugent.be) I wrote this software in my free time, for my joy, not as a commissioned task. Any copyright claims made by my employer should therefore be considered void.

Distributed under the GNU General Public License, version 3 or later

Classes defined in module sendmail

Functions defined in module sendmail

sendmail.message(sender='', to='', cc='', subject='', text='')

Create an email message

‘to’ and ‘cc’ can be lists of email addresses.

sendmail.sendmail(message, sender, to, serverURL='localhost')

Send an email message

‘message’ is an email message (e.g. returned by message()) ‘sender’ is a single mail address ‘to’ can be a list of addresses

pyformex-0.8.6/pyformex/doc/html/ref/units.html0000644000211500021150000003325211705104255021425 0ustar benebene00000000000000 38. units — A Python wrapper for unit conversion of physical quantities. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

37. inertia — inertia.py

Next topic

39. datareader — Numerical data reader

[FSF Associate Member]

Valid XHTML 1.0 Transitional

38. units — A Python wrapper for unit conversion of physical quantities.

This module uses the standard UNIX program ‘units’ (available from http://www.gnu.org/software/units/units.html) to do the actual conversions. Obviously, it will only work on systems that have this program available.

If you really insist on running another OS lacking the units command, have a look at http://home.tiscali.be/be052320/Unum.html and make an implementation based on unum. If you GPL it and send it to me, I might include it in this distribution.

Classes defined in module units

class units.UnitsSystem(system='international')

A class for handling and converting units of physical quantities.

The units class provides two built-in consistent units systems: International() and Engineering(). International() returns the standard International Standard units. Engineering() returns a consistent engineering system,which is very practical for use in mechanical engineering. It uses ‘mm’ for length and ‘MPa’ for pressure and stress. To keep it consistent however, the density is rather unpractical: ‘t/mm^3’. If you want to use t/m^3, you can make a custom units system. Beware with non-consistent unit systems though! The better practice is to allow any unit to be specified at input (and eventually requested for output), and to convert everyting internally to a consistent system. Apart from the units for usual physical quantities, Units stores two special purpose values in its units dictionary: ‘model’ : defines the length unit used in the geometrical model ‘problem’ : defines the unit system to be used in the problem. Defaults are: model=’m’, problem=’international’.

Add(un)

Add the units from dictionary un to the units system

Predefined(system)

Returns the predefined units for the specified system

International()

Returns the international units system.

Engineering()

Returns a consistent engineering units system.

Read(filename)

Read units from file with specified name.

The units file is an ascii file where each line contains a couple of words separated by a colon and a blank. The first word is the type of quantity, the second is the unit to be used for this quantity. Lines starting with ‘#’ are ignored. A ‘problem: system’ line sets all units to the corresponding value of the specified units system.

Get(ent)

Get units list for the specified entitities.

If ent is a single entity, returns the corresponding unit if an entry ent exists in the current system or else returns ent unchanged. If ent is a list of entities, returns a list of corresponding units. Example: with the default units system:

Un = UnitsSystem()
Un.Get(['length','mass','float'])

returns: ['m', 'kg', 'float']

Functions defined in module units

units.convertUnits(From, To)

Converts between conformable units.

This function converts the units ‘From’ to units ‘To’. The units should be conformable. The ‘From’ argument can (and usually does) include a value. The return value is the converted value without units. Thus: convertUnits(‘3.45 kg’,’g’) will return ‘3450’. This function is merely a wrapper around the GNU ‘units’ command, which should be installed for this function to work.

pyformex-0.8.6/pyformex/doc/html/ref/inertia.html0000644000211500021150000002442111705104253021712 0ustar benebene00000000000000 37. inertia — inertia.py — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

36. section2d — Some functions operating on 2D structures.

Next topic

38. units — A Python wrapper for unit conversion of physical quantities.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

37. inertia — inertia.py

Compute inertia related quantities of a Formex. This comprises: center of gravity, inertia tensor, principal axes

Currently, these functions work on arrays of nodes, not on Formices! Use func(F,f) to operate on a Formex F.

Classes defined in module inertia

Functions defined in module inertia

inertia.centroids(X)

Compute the centroids of the points of a set of elements.

X (nelems,nplex,3)

inertia.center(X, mass=None)

Compute the center of gravity of an array of points.

mass is an optional array of masses to be atributed to the points. The default is to attribute a mass=1 to all points.

If you also need the inertia tensor, it is more efficient to use the inertia() function.

inertia.inertia(X, mass=None)

Compute the inertia tensor of an array of points.

mass is an optional array of masses to be atributed to the points. The default is to attribute a mass=1 to all points.

The result is a tuple of two float arrays:
  • the center of gravity: shape (3,)
  • the inertia tensor: shape (6,) with the following values (in order): Ixx, Iyy, Izz, Ixy, Ixz, Iyz
inertia.principal(inertia, sort=False, right_handed=False)

Returns the principal values and axes of the inertia tensor.

If sort is True, they are sorted (maximum comes first). If right_handed is True, the axes define a right-handed coordinate system.

pyformex-0.8.6/pyformex/doc/html/ref/config.html0000644000211500021150000004455311705104247021537 0ustar benebene00000000000000 58. config — A general yet simple configuration class. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

57. collection — Tools for handling collections of elements belonging to multiple parts.

Next topic

59. flatkeydb — Flat Text File Database.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

58. config — A general yet simple configuration class.

(C) 2005 Benedict Verhegghe
Distributed under the GNU GPL version 3 or later
Why
I wrote this simple class because I wanted to use Python expressions in my configuration files. This is so much more fun than using .INI style config files. While there are some other Python config modules available on the web, I couldn’t find one that suited my needs and my taste: either they are intended for more complex configuration needs than mine, or they do not work with the simple Python syntax I expected.
What
Our Config class is just a normal Python dictionary which can hold anything. Fields can be accessed either as dictionary lookup (config[‘foo’]) or as object attributes (config.foo). The class provides a function for reading the dictionary from a flat text (multiline string or file). I will always use the word ‘file’ hereafter, because that is what you usually will read the configuration from. Your configuration file can have named sections. Sections are stored as other Python dicts inside the top Config dictionary. The current version is limited to one level of sectioning.

Classes defined in module config

class config.Config(data={}, default=None)

A configuration class allowing Python expressions in the input.

The configuration settings are stored in the __dict__ of a Python object. An item ‘foo’ in the configuration ‘config’ can be accessed either as dictionary lookup (config['foo']) or as object attribute (config.foo).

The configuration object can be initialized from a multiline string or a text file (or any other object that allows iterating over strings).

The format of the config file/text is described hereafter.

All config lines should have the format: key = value, where key is a string and value is a Python expression The first ‘=’ character on the line is the delimiter between key and value. Blanks around both the key and the value are stripped. The value is then evaluated as a Python expression and stored in a variable with name specified by the key. This variable is available for use in subsequent configuration lines. It is an error to use a variable before it is defined. The key,value pair is also stored in the config dictionary, unless the key starts with an underscore (‘_’): this provides for local variables.

Lines starting with ‘#’ are comments and are ignored, as are empty and blank lines. Lines ending with ‘’ are continued on the next line. A line starting with ‘[‘ starts a new section. A section is nothing more than a Python dictionary inside the config dictionary. The section name is delimited by ‘[‘and ‘]’. All subsequent lines will be stored in the section dictionary instead of the toplevel dictionary.

All other lines are executed as python statements. This allows e.g. for importing modules.

Whole dictionaries can be inserted at once in the config with the update() function.

All defined variables while reading config files remain available for use in the config file statements, even over multiple calls to the read() function. Variables inserted with addSection() will not be available as individual variables though, but can be access as self['name'].

As an example, if your config file looks like:

aa = 'bb'
bb = aa
[cc]
aa = 'aa'
_n = 3
rng = range(_n)

the resulting configuration dictionary is {'aa': 'bb', 'bb': 'bb', 'cc': {'aa': 'aa', 'rng': [0, 1, 2]}}

As far as the resulting Config contents is concerned, the following are equivalent:

C.update({'key':'value'})
C.read("key='value'\n")

There is an important difference though: the second line will make a variable key (with value ‘value’) available in subsequent Config read() method calls.

update(data={}, name=None, removeLocals=False)

Add a dictionary to the Config object.

The data, if specified, should be a valid Python dict. If no name is specified, the data are added to the top dictionary and will become attributes. If a name is specified, the data are added to the named attribute, which should be a dictionary. If the name does not specify a dictionary, an empty one is created, deleting the existing attribute.

If a name is specified, but no data, the effect is to add a new empty dictionary (section) with that name.

If removeLocals is set, keys starting with ‘_’ are removed from the data before updating the dictionary and not included in the config. This behaviour can be changed by setting removeLocals to false.

read(fil, debug=False)

Read a configuration from a file or text

fil is a sequence of strings. Any type that allows a loop like for line in fil: to iterate over its text lines will do. This could be a file type, or a multiline text after splitting on ‘n’.

The function will try to react intelligently if a string is passed as argument. If the string contains at least one ‘n’, it will be interpreted as a multiline string and be splitted on ‘n’. Else, the string will be considered and a file with that name will be opened. It is an error if the file does not exist or can not be opened.

The function returns self, so that you can write: cfg = Config().

write(filename, header='# Config written by pyFormex -*- PYTHON -*-nn', trailer='n# End of confign')

Write the config to the given file

The configuration data will be written to the file with the given name in a text format that is both readable by humans and by the Config.read() method.

The header and trailer arguments are strings that will be added at the start and end of the outputfile. Make sure they are valid Python statements (or comments) and that they contain the needed line separators, if you want to be able to read it back.

keys(descend=True)

Return the keys in the config.

By default this descends one level of Dicts.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

Functions defined in module config

config.formatDict(d)

Format a dict in Python source representation.

Each (key,value) pair is formatted on a line of the form:

key = value

The resulting text is a legal Python script to define the items in the dict.

pyformex-0.8.6/pyformex/doc/html/ref/script.html0000644000211500021150000006340111705104254021565 0ustar benebene00000000000000 4. script — Basic pyFormex script functions — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

3. arraytools — A collection of numerical array utilities.

Next topic

5. draw — Create 3D graphical representations.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

4. script — Basic pyFormex script functions

The script module provides the basic functions available in all pyFormex scripts. These functions are available in GUI and NONGUI applications, without the need to explicitely importing the script module.

Classes defined in module script

Functions defined in module script

script.Globals()

Return the globals that are passed to the scripts on execution.

This basically contains the globals defined in draw.py, colors.py, and formex.py, as well as the globals from numpy. It also contains the definitions put into the pyformex.PF, by preference using the export() function. During execution of the script, the global variable __name__ will be set to either ‘draw’ or ‘script’ depending on whether the script was executed in the ‘draw’ module (–gui option) or the ‘script’ module (–nogui option).

script.export(dic)

Export the variables in the given dictionary.

script.export2(names, values)

Export a list of names and values.

script.forget(names)

Remove the global variables specified in list.

script.forgetAll()

Delete all the global variables.

script.rename(oldnames, newnames)

Rename the global variables in oldnames to newnames.

script.listAll(clas=None, like=None, filter=None, dic=None)

Return a list of all objects in dictionay that match criteria.

  • dic: a dictionary object, defaults to pyformex.PF
  • clas: a class name: if specified, only instances of this class will be returned
  • like: a string: if given, only object names starting with this string will be returned
  • filter: a function taking an object name as parameter and returning True or False. If specified, only objects passing the test will be returned.

The return value is a list of keys from dic.

script.named(name)

Returns the global object named name.

script.getcfg(name)

Return a value from the configuration.

script.ask(question, choices=None, default='')

Ask a question and present possible answers.

If no choices are presented, anything will be accepted. Else, the question is repeated until one of the choices is selected. If a default is given and the value entered is empty, the default is substituted. Case is not significant, but choices are presented unchanged. If no choices are presented, the string typed by the user is returned. Else the return value is the lowest matching index of the users answer in the choices list. Thus, ask(‘Do you agree’,[‘Y’,’n’]) will return 0 on either ‘y’ or ‘Y’ and 1 on either ‘n’ or ‘N’.

script.ack(question)

Show a Yes/No question and return True/False depending on answer.

script.error(message)

Show an error message and wait for user acknowlegement.

script.system(cmdline, result='output')

Run a command and return its output.

If result == ‘status’, the exit status of the command is returned. If result == ‘output’, the output of the command is returned. If result == ‘both’, a tuple of status and output is returned.

script.executeScript(scr, glob)

Execute a Python script in specified globals.

script.playScript(scr, name=None, filename=None, argv=[], pye=False)

Play a pyformex script scr. scr should be a valid Python text.

There is a lock to prevent multiple scripts from being executed at the same time. This implies that pyFormex scripts can currently not be recurrent. If a name is specified, set the global variable pyformex.scriptName to it when the script is started. If a filename is specified, set the global variable __file__ to it.

If step==True, an indefinite pause will be started after each line of the script that starts with ‘draw’. Also (in this case), each line (including comments) is echoed to the message board.

script.breakpt(msg=None)

Set a breakpoint where the script can be halted on a signal.

If an argument is specified, it will be written to the message board.

The exitrequested signal is usually emitted by pressing a button in the GUI.

script.stopatbreakpt()

Set the exitrequested flag.

script.playFile(fn, argv=[])

Play a formex script from file fn.

fn is the name of a file holding a pyFormex script. A list of arguments can be passed. They will be available under the name argv. This variable can be changed by the script and the resulting argv is returned to the caller.

script.play(fn=None, argv=[], step=False)

Play the pyFormex script with filename fn or the current script file.

This function does nothing if no filename is passed or no current scriptfile was set. If arguments are given, they are passed to the script. If step is True, the script is executed in step mode.

script.exit(all=False)

Exit from the current script or from pyformex if no script running.

script.quit()

Quit the pyFormex program

This is a hard exit from pyFormex. It is normally not called directly, but results from an exit(True) call.

script.processArgs(args)

Run the application without gui.

Arguments are interpreted as names of script files, possibly interspersed with arguments for the scripts. Each running script should pop the required arguments from the list.

script.setPrefs(res, save=False)

Update the current settings (store) with the values in res.

res is a dictionary with configuration values. The current settings will be update with the values in res.

If save is True, the changes will be stored to the user’s configuration file.

script.printall()

Print all Formices in globals()

script.writable(path)

Returns True if the specified path is writeable

script.chdir(fn)

Change the current working directory.

If fn is a directory name, the current directory is set to fn. If fn is a file name, the current directory is set to the directory holding fn. In either case, the current directory is stored in the user’s preferences for persistence between pyFormex invocations.

If fn does not exist, nothing is done.

script.runtime()

Return the time elapsed since start of execution of the script.

script.startGui(args=[])

Start the gui

script.isWritable(path)

Check that the specified path is writeable.

BEWARE: this only works if the path exists!

script.checkRevision(rev, comp='>=')

Check the pyFormex revision number.

  • rev: a positive integer.
  • comp: a string specifying a comparison operator.

By default, this function returns True if the pyFormex revision number is equal or larger than the specified number.

The comp argument may specify another comparison operator.

If pyFormex is unable to find its revision number (this is the case on very old versions) the test returns False.

script.requireRevision(rev, comp='>=')

Require a specified pyFormex revision number.

The arguments are like checkRevision. Ho9wever, this function will raise an error if the requirement fails.

script.grepSource(pattern, options='', quiet=False)

Finds pattern in the pyFormex source .py files.

Uses the grep program to find all occurrences of some specified pattern text in the pyFormex source .py files (including the examples). Extra options can be passed to the grep command. See man grep for more info.

Returns the output of the grep command.

script.writeGeomFile(filename, objects, sep=' ', mode='w', shortlines=False)

Save geometric objects to a pyFormex Geometry File.

A pyFormex Geometry File can store multiple geometrical objects in a native format that can be efficiently read back into pyformex. The format is portable over different pyFormex versions and even to other software.

  • filename: the name of the file to be written
  • objects: a list or a dictionary. If it is a dictionary, the objects will be saved with the key values as there names. Objects that can not be exported to a Geometry File will be silently ignored.
  • mode: can be set to ‘a’ to append to an existing file.
  • sep: the string used to separate data. If set to an empty string, the data will be written in binary format and the resulting file will be smaller but less portable.

Returns the number of objects written to the file.

script.readGeomFile(filename)

Read a pyFormex Geometry File.

A pyFormex Geometry File can store multiple geometrical objects in a native format that can be efficiently read back into pyformex. The format is portable over different pyFormex versions and even to other software.

  • filename: the name of an exisiting pyFormex Geometry File.

Returns a dictionary with the geometric objects read from the file. If object names were stored in the file, they will be used as the keys. Else, default names will be provided.

pyformex-0.8.6/pyformex/doc/html/ref/mesh.html0000644000211500021150000023344311705104254021222 0ustar benebene00000000000000 13. mesh — Finite element meshes in pyFormex. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

12. elements — Definition of elements.

Next topic

14. widgets — A collection of custom widgets used in the pyFormex GUI

[FSF Associate Member]

Valid XHTML 1.0 Transitional

13. mesh — Finite element meshes in pyFormex.

This module defines the Mesh class, which can be used to describe discrete geometrical models like those used in Finite Element models. It also contains some useful functions to create such models.

Classes defined in module mesh

class mesh.Mesh(coords=None, elems=None, prop=None, eltype=None)

A Mesh is a discrete geometrical model defined by nodes and elements.

In the Mesh geometrical data model, the coordinates of all the points are gathered in a single twodimensional array with shape (ncoords,3). The individual geometrical elements are then described by indices into the coordinates array.

This model has some advantages over the Formex data model (which stores all the points of all the elements by their coordinates):

  • a more compact storage, because coordinates of coinciding points are not repeated,
  • faster connectivity related algorithms.

The downside is that geometry generating algorithms are far more complex and possibly slower.

In pyFormex we therefore mostly use the Formex data model when creating geometry, but when we come to the point of exporting the geometry to file (and to other programs), a Mesh data model may be more adequate.

The Mesh data model has at least the following attributes:

  • coords: (ncoords,3) shaped Coords object, holding the coordinates of all points in the Mesh;
  • elems: (nelems,nplex) shaped Connectivity object, defining the elements by indices into the Coords array. All values in elems should be in the range 0 <= value < ncoords.
  • prop: an array of element property numbers, default None.
  • eltype: an Element subclass or a string designing the element type, default None.

If eltype is None, the eltype of the elems Connectivity table is used, and if that is missing, a default eltype is derived from the plexitude, by a call to the elements.elementType function. In most cases the eltype can be set automatically. The user can override the default value, but an error will occur if the element type does not exist or does not match the plexitude.

A Mesh can be initialized by its attributes (coords,elems,prop,eltype) or by a single geometric object that provides a toMesh() method.

setType(eltype=None)

Set the eltype from a character string.

This function allows the user to change the element type of the Mesh. The input is a character string with the name of one of the element defined in elements.py. The function will only allow to set a type matching the plexitude of the Mesh.

This method is seldom needed, because the applications should normally set the element type at creation time.

setProp(prop=None)

Create or destroy the property array for the Mesh.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Mesh. You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Mesh.

getProp()

Return the properties as a numpy array (ndarray)

maxProp()

Return the highest property value used, or None

propSet()

Return a list with unique property values.

copy()

Return a copy using the same data arrays

toFormex()

Convert a Mesh to a Formex.

The Formex inherits the element property numbers and eltype from the Mesh. Node property numbers however can not be translated to the Formex data model.

toSurface()

Convert a Mesh to a Surface.

If the plexitude of the mesh is 3, returns a TriSurface equivalent with the Mesh. Else, an error is raised.

nedges()

Return the number of edges.

This returns the number of rows that would be in getEdges(), without actually constructing the edges. The edges are not fused!

centroids()

Return the centroids of all elements of the Mesh.

The centroid of an element is the point whose coordinates are the mean values of all points of the element. The return value is a Coords object with nelems points.

getCoords()

Get the coords data.

Returns the full array of coordinates stored in the Mesh object. Note that this may contain points that are not used in the mesh. compact() will remove the unused points.

getElems()

Get the elems data.

Returns the element connectivity data as stored in the object.

getLowerEntities(level=-1, unique=False)

Get the entities of a lower dimensionality.

If the element type is defined in the elements module, this returns a Connectivity table with the entities of a lower dimensionality. The full list of entities with increasing dimensionality 0,1,2,3 is:

['points', 'edges', 'faces', 'cells' ]

If level is negative, the dimensionality returned is relative to that of the caller. If it is positive, it is taken absolute. Thus, for a Mesh with a 3D element type, getLowerEntities(-1) returns the faces, while for a 2D element type, it returns the edges. For both meshes however, getLowerEntities(+1) returns the edges.

By default, all entities for all elements are returned and common entities will appear multiple times. Specifying unique=True will return only the unique ones.

The return value may be an empty table, if the element type does not have the requested entities (e.g. the ‘point’ type). If the eltype is not defined, or the requested entity level is outside the range 0..3, the return value is None.

getNodes()

Return the set of unique node numbers in the Mesh.

This returns only the node numbers that are effectively used in the connectivity table. For a compacted Mesh, it is equal to `arange(self.nelems)`. This function also stores the result internally so that future requests can return it without the need for computing it again.

getPoints()

Return the nodal coordinates of the Mesh.

This returns only those points that are effectively used in the connectivity table. For a compacted Mesh, it is equal to the coords attribute.

getEdges()

Return the unique edges of all the elements in the Mesh.

This is a convenient function to create a table with the element edges. It is equivalent to `self.getLowerEntities(1,unique=True)`, but this also stores the result internally so that future requests can return it without the need for computing it again.

getFaces()

Return the unique faces of all the elements in the Mesh.

This is a convenient function to create a table with the element faces. It is equivalent to `self.getLowerEntities(2,unique=True)`, but this also stores the result internally so that future requests can return it without the need for computing it again.

getCells()

Return the cells of the elements.

This is a convenient function to create a table with the element cells. It is equivalent to `self.getLowerEntities(3,unique=True)`, but this also stores the result internally so that future requests can return it without the need for computing it again.

getElemEdges()

Defines the elements in function of its edges.

This returns a Connectivity table with the elements defined in function of the edges. It is equivalent to `self.elems.insertLevel(self.eltype.getEntities(1))` but it also stores the definition of the edges and the returned element to edge connectivity.

getFreeEntities(level=-1, return_indices=False)

Return the border of the Mesh.

Returns a Connectivity table with the free entities of the specified level of the Mesh. Free entities are entities that are only connected with a single element.

If return_indices==True, also returns an (nentities,2) index for inverse lookup of the higher entity (column 0) and its local lower entity number (column 1).

getFreeEntitiesMesh(level=-1, compact=True)

Return a Mesh with lower entities.

Returns a Mesh representing the lower entities of the specified level. If the Mesh has property numbers, the lower entities inherit the property of the element to which they belong.

By default, the resulting Mesh is compacted. Compaction can be switched off by setting compact=False.

getBorder(return_indices=False)

Return the border of the Mesh.

This returns a Connectivity table with the border of the Mesh. The border entities are of a lower hierarchical level than the mesh itself. These entities become part of the border if they are connected to only one element.

If return_indices==True, it returns also an (nborder,2) index for inverse lookup of the higher entity (column 0) and its local border part number (column 1).

This is a convenient shorthand for

self.getFreeEntities(level=-1,return_indices=return_indices)
getBorderMesh(compact=True)

Return a Mesh with the border elements.

The returned Mesh is of the next lower hierarchical level and contains all the free entitites of that level. If the Mesh has property numbers, the border elements inherit the property of the element to which they belong.

By default, the resulting Mesh is compacted. Compaction can be switched off by setting compact=False.

This is a convenient shorthand for

self.getFreeEntitiesMesh(level=-1,compact=compact)
getFreeEdgesMesh(compact=True)

Return a Mesh with the free edge elements.

The returned Mesh is of the hierarchical level 1 (no mather what the level of the parent Mesh is) and contains all the free entitites of that level. If the Mesh has property numbers, the border elements inherit the property of the element to which they belong.

By default, the resulting Mesh is compacted. Compaction can be switched off by setting compact=False.

This is a convenient shorthand for

self.getFreeEntitiesMesh(level=1,compact=compact)
reverse()

Return a Mesh where all elements have been reversed.

Reversing an element has the following meaning:

  • for 1D elements: reverse the traversal direction,
  • for 2D elements: reverse the direction of the positive normal,
  • for 3D elements: reverse inside and outside directions of the element’s border surface

The reflect() method by default calls this method to undo the element reversal caused by the reflection operation.

reflect(dir=0, pos=0.0, reverse=True, **kargs)

Reflect the coordinates in one of the coordinate directions.

Parameters:

  • dir: int: direction of the reflection (default 0)
  • pos: float: offset of the mirror plane from origin (default 0.0)
  • reverse: boolean: if True, the Mesh.reverse() method is called after the reflection to undo the element reversal caused by the reflection of its coordinates. This will in most cases have the desired effect. If not however, the user can set this to False to skip the element reversal.
nodeConnections()

Find and store the elems connected to nodes.

nNodeConnected()

Find the number of elems connected to nodes.

edgeConnections()

Find and store the elems connected to edges.

nEdgeConnected()

Find the number of elems connected to edges.

nodeAdjacency()

Find the elems adjacent to each elem via one or more nodes.

nNodeAdjacent()

Find the number of elems which are adjacent by node to each elem.

edgeAdjacency()

Find the elems adjacent to elems via an edge.

nEdgeAdjacent()

Find the number of adjacent elems.

report()

Create a report on the Mesh shape and size.

The report contains the number of nodes, number of elements, plexitude, element type, bbox and size.

fuse(**kargs)

Fuse the nodes of a Meshes.

All nodes that are within the tolerance limits of each other are merged into a single node.

The merging operation can be tuned by specifying extra arguments that will be passed to Coords:fuse().

matchCoords(mesh, **kargs)

Match nodes of Mesh with nodes of self.

This is a convenience function equivalent to:

self.coords.match(mesh.coords,**kargs)

See also Coords.match()

matchCentroids(mesh, **kargs)

Match elems of Mesh with elems of self.

self and Mesh are same eltype meshes and are both without duplicates.

Elems are matched by their centroids.

matchFaces(mesh)

Match faces of mesh with faces of self.

self and Mesh can be same eltype meshes or different eltype but of the same hierarchical type (i.e. hex8-quad4 or tet4 - tri3) and are both without duplicates.

Returns the indices array of the elems of self that matches the faces of mesh

compact()

Remove unconnected nodes and renumber the mesh.

Returns a mesh where all nodes that are not used in any element have been removed, and the nodes are renumbered to a compacter scheme.

select(selected, compact=True)

Return a Mesh only holding the selected elements.

  • selected: an object that can be used as an index in the elems array, e.g. a list of (integer) element numbers, or a boolean array with the same length as the elems array.
  • compact: boolean. If True (default), the returned Mesh will be compacted, i.e. the unused nodes are removed and the nodes are renumbered from zero. If False, returns the node set and numbers unchanged.

Returns a Mesh (or subclass) with only the selected elements.

See cselect for the complementary operation.

cselect(selected, compact=True)

Return a mesh without the selected elements.

  • selected: an object that can be used as an index in the elems array, e.g. a list of (integer) element numbers, or a boolean array with the same length as the elems array.
  • compact: boolean. If True (default), the returned Mesh will be compacted, i.e. the unused nodes are removed and the nodes are renumbered from zero. If False, returns the node set and numbers unchanged.

Returns a Mesh with all but the selected elements.

This is the complimentary operation of select.

avgNodes(nodsel, wts=None)

Create average nodes from the existing nodes of a mesh.

nodsel is a local node selector as in selectNodes() Returns the (weighted) average coordinates of the points in the selector as (nelems*nnod,3) array of coordinates, where nnod is the length of the node selector. wts is a 1-D array of weights to be attributed to the points. Its length should be equal to that of nodsel.

meanNodes(nodsel)

Create nodes from the existing nodes of a mesh.

nodsel is a local node selector as in selectNodes() Returns the mean coordinates of the points in the selector as (nelems*nnod,3) array of coordinates, where nnod is the length of the node selector.

addNodes(newcoords, eltype=None)

Add new nodes to elements.

newcoords is an (nelems,nnod,3) or`(nelems*nnod,3)` array of coordinates. Each element gets exactly nnod extra nodes from this array. The result is a Mesh with plexitude self.nplex() + nnod.

addMeanNodes(nodsel, eltype=None)

Add new nodes to elements by averaging existing ones.

nodsel is a local node selector as in selectNodes() Returns a Mesh where the mean coordinates of the points in the selector are added to each element, thus increasing the plexitude by the length of the items in the selector. The new element type should be set to correct value.

selectNodes(nodsel, eltype=None)

Return a mesh with subsets of the original nodes.

nodsel is an object that can be converted to a 1-dim or 2-dim array. Examples are a tuple of local node numbers, or a list of such tuples all having the same length. Each row of nodsel holds a list of local node numbers that should be retained in the new connectivity table.

withProp(val)

Return a Mesh which holds only the elements with property val.

val is either a single integer, or a list/array of integers. The return value is a Mesh holding all the elements that have the property val, resp. one of the values in val. The returned Mesh inherits the matching properties.

If the Mesh has no properties, a copy with all elements is returned.

withoutProp(val)

Return a Mesh without the elements with property val.

This is the complementary method of Mesh.withProp(). val is either a single integer, or a list/array of integers. The return value is a Mesh holding all the elements that do not have the property val, resp. one of the values in val. The returned Mesh inherits the matching properties.

If the Mesh has no properties, a copy with all elements is returned.

splitProp()

Partition a Mesh according to its propery values.

Returns a dict with the property values as keys and the corresponding partitions as values. Each value is a Mesh instance. It the Mesh has no props, an empty dict is returned.

splitRandom(n, compact=True)

Split a mesh in n parts, distributing the elements randomly.

Returns a list of n Mesh objects, constituting together the same Mesh as the original. The elements are randomly distributed over the subMeshes.

By default, the Meshes are compacted. Compaction may be switched off for efficiency reasons.

convert(totype, fuse=False)

Convert a Mesh to another element type.

Converting a Mesh from one element type to another can only be done if both element types are of the same dimensionality. Thus, 3D elements can only be converted to 3D elements.

The conversion is done by splitting the elements in smaller parts and/or by adding new nodes to the elements.

Not all conversions between elements of the same dimensionality are possible. The possible conversion strategies are implemented in a table. New strategies may be added however.

The return value is a Mesh of the requested element type, representing the same geometry (possibly approximatively) as the original mesh.

If the requested conversion is not implemented, an error is raised.

..warning: Conversion strategies that add new nodes may produce
double nodes at the common border of elements. The fuse() method can be used to merge such coincident nodes. Specifying fuse=True will also enforce the fusing. This option become the default in future.
convertRandom(choices)

Convert choosing randomly between choices

reduceDegenerate(eltype=None)

Reduce degenerate elements to lower plexitude elements.

This will try to reduce the degenerate elements of the mesh to elements of a lower plexitude. If a target element type is given, only the matching reduce scheme is tried. Else, all the target element types for which a reduce scheme from the Mesh eltype is available, will be tried.

The result is a list of Meshes of which the last one contains the elements that could not be reduced and may be empty. Property numbers propagate to the children.

splitDegenerate(autofix=True)

Split a Mesh in degenerate and non-degenerate elements.

If autofix is True, the degenerate elements will be tested against known degeneration patterns, and the matching elements will be transformed to non-degenerate elements of a lower plexitude.

The return value is a list of Meshes. The first holds the non-degenerate elements of the original Mesh. The last holds the remaining degenerate elements. The intermediate Meshes, if any, hold elements of a lower plexitude than the original. These may still contain degenerate elements.

removeDegenerate(eltype=None)

Remove the degenerate elements from a Mesh.

Returns a Mesh with all degenerate elements removed.

removeDuplicate(permutations=True)

Remove the duplicate elements from a Mesh.

Duplicate elements are elements that consist of the same nodes, by default in no particular order. Setting permutations=False will only consider elements with the same nodes in the same order as duplicates.

Returns a Mesh with all duplicate elements removed.

renumber(order='elems')

Renumber the nodes of a Mesh in the specified order.

order is an index with length equal to the number of nodes. The index specifies the node number that should come at this position. Thus, the order values are the old node numbers on the new node number positions.

order can also be a predefined value that will generate the node index automatically:

  • ‘elems’: the nodes are number in order of their appearance in the Mesh connectivity.
renumberElems(order='nodes')

Renumber the elements of a Mesh.

Parameters:

  • order: either a 1-D integer array with a permutation of arange(self.nelems()), specifying the requested order, or one of the following predefined strings:
    • ‘nodes’: order the elements in increasing node number order.
    • ‘random’: number the elements in a random order.
    • ‘reverse’: number the elements in.
Returns:
A Mesh equivalent with self but with the elements ordered as specified.

See also: Connectivity.reorder()

connect(coordslist, div=1, degree=1, loop=False, eltype=None)

Connect a sequence of toplogically congruent Meshes into a hypermesh.

Parameters:

  • coordslist: either a list of Coords instances, all having the same shape as self.coords, or a single Mesh instance whose coords attribute has the same shape.

    If it is a list of Coords, consider a list of Meshes obtained by combining each Coords object with the connectivity table, element type and property numbers of the current Mesh. The return value then is the hypermesh obtained by connecting each consecutive slice of (degree+1) of these meshes. The hypermesh has a dimensionality that is one higher than the original Mesh (i.e. points become lines, lines become surfaces, surfaces become volumes). The resulting elements will be of the given degree in the direction of the connection. Notice that the coords of the current Mesh are not used, unless these coords are explicitely included into the specified coordslist. In many cases self.coords will be the first item in coordslist, but it could occur anywhere in the list or even not at all. The number of Coords items in the list should be a multiple of degree plus 1.

    Specifying a single Mesh instead of a list of Coords is just a convenience for the often occurring situation of connecting a Mesh (self) with another one (mesh) having the same connectivity: in this case the list of Coords will automatically be set to [ self.coords, mesh.coords ]. The degree should be 1 in this case.

  • degree: degree of the connection. Currently only degree 1 and 2 are supported.

    • If degree is 1, every Coords from the coordslist is connected with hyperelements of a linear degree in the connection direction.
    • If degree is 2, quadratic hyperelements are created from one Coords item and the next two in the list. Note that all Coords items should contain the same number of nodes, even for higher order elements where the intermediate planes contain less nodes.
  • loop: if True, the connections with loop around the list and connect back to the first. This is accomplished by adding the first Coords item back at the end of the list.

  • div: Either an integer, or a sequence of float numbers (usually in the range ]0.0..1.0]). With this parameter the generated elements can be further subdivided along the connection direction. If an int is given, the connected elements will be divided into this number of elements along the connection direction. If a sequence of float numbers is given, the numbers specify the relative distance along the connection direction where the elements should end. If the last value in the sequence is not equal to 1.0, there will be a gap between the consecutive connections.

  • eltype: the element type of the constructed hypermesh. Normally, this is set automatically from the base element type and the connection degree. If a different element type is specified, a final conversion to the requested element type is attempted.

extrude(n, step=1.0, dir=0, degree=1, eltype=None)

Extrude a Mesh in one of the axes directions.

Returns a new Mesh obtained by extruding the given Mesh over n steps of length step in direction of axis dir.

revolve(n, axis=0, angle=360.0, around=None, loop=False, eltype=None)

Revolve a Mesh around an axis.

Returns a new Mesh obtained by revolving the given Mesh over an angle around an axis in n steps, while extruding the mesh from one step to the next. This extrudes points into lines, lines into surfaces and surfaces into volumes.

sweep(path, eltype=None, **kargs)

Sweep a mesh along a path, creating an extrusion

Returns a new Mesh obtained by sweeping the given Mesh over a path. The returned Mesh has double plexitude of the original. The operation is similar to the extrude() method, but the path can be any 3D curve.

This function is usually used to extrude points into lines, lines into surfaces and surfaces into volumes. By default it will try to fix the connectivity ordering where appropriate. If autofix is switched off, the connectivities are merely stacked, and the user may have to fix it himself.

Currently, this function produces the correct element type, but the geometry .

smooth(iterations=1, lamb=0.5, k=0.1, edg=True)

Return a smoothened mesh.

Smoothing algorithm based on lowpass filters.

If edg is True, the algorithm tries to smooth the outer border of the mesh seperately to reduce mesh shrinkage.

Higher values of k can reduce shrinkage even more (up to a point where the mesh expands), but will result in less smoothing per iteration.

classmethod concatenate(clas, meshes, **kargs)

Concatenate a list of meshes of the same plexitude and eltype

Merging of the nodes can be tuned by specifying extra arguments that will be passed to Coords:fuse().

This is a class method, and should be invoked as follows:

Mesh.concatenate([mesh0,mesh1,mesh2])
test(nodes='all', dir=0, min=None, max=None, atol=0.0)

Flag elements having nodal coordinates between min and max.

This function is very convenient in clipping a Mesh in a specified direction. It returns a 1D integer array flagging (with a value 1 or True) the elements having nodal coordinates in the required range. Use where(result) to get a list of element numbers passing the test. Or directly use clip() or cclip() to create the clipped Mesh

The test plane can be defined in two ways, depending on the value of dir. If dir == 0, 1 or 2, it specifies a global axis and min and max are the minimum and maximum values for the coordinates along that axis. Default is the 0 (or x) direction.

Else, dir should be compaitble with a (3,) shaped array and specifies the direction of the normal on the planes. In this case, min and max are points and should also evaluate to (3,) shaped arrays.

nodes specifies which nodes are taken into account in the comparisons. It should be one of the following:

  • a single (integer) point number (< the number of points in the Formex)
  • a list of point numbers
  • one of the special strings: ‘all’, ‘any’, ‘none’

The default (‘all’) will flag all the elements that have all their nodes between the planes x=min and x=max, i.e. the elements that fall completely between these planes. One of the two clipping planes may be left unspecified.

clip(t, compact=False)

Return a Mesh with all the elements where t>0.

t should be a 1-D integer array with length equal to the number of elements of the Mesh. The resulting Mesh will contain all elements where t > 0.

cclip(t, compact=False)

This is the complement of clip, returning a Mesh where t<=0.

clipAtPlane(p, n, nodes='any', side='+')

Return the Mesh clipped at plane (p,n).

This is a convenience function returning the part of the Mesh at one side of the plane (p,n)

volumes()

Return the signed volume of all the mesh elements

For a ‘tet4’ tetraeder Mesh, the volume of the elements is calculated as 1/3 * surface of base * height.

For other Mesh types the volumes are calculated by first splitting the elements into tetraeder elements.

The return value is an array of float values with length equal to the number of elements. If the Mesh conversion to tetraeder does not succeed, the return value is None.

volume()

Return the total volume of a Mesh.

If the Mesh can not be converted to tet4 type, 0 is returned

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

Functions defined in module mesh

mesh.mergeNodes(nodes, fuse=True, **kargs)

Merge all the nodes of a list of node sets.

Merging the nodes creates a single Coords object containing all nodes, and the indices to find the points of the original node sets in the merged set.

Parameters:

  • nodes: a list of Coords objects, all having the same shape, except possibly for their first dimension
  • fuse: if True (default), coincident (or very close) points will be fused to a single point
  • **kargs: keyword arguments that are passed to the fuse operation

Returns:

  • a Coords with the coordinates of all (unique) nodes,
  • a list of indices translating the old node numbers to the new. These numbers refer to the serialized Coords.

The merging operation can be tuned by specifying extra arguments that will be passed to Coords.fuse().

mesh.mergeMeshes(meshes, fuse=True, **kargs)

Merge all the nodes of a list of Meshes.

Each item in meshes is a Mesh instance. The return value is a tuple with:

  • the coordinates of all unique nodes,
  • a list of elems corresponding to the input list, but with numbers referring to the new coordinates.

The merging operation can be tuned by specifying extra arguments that will be passed to Coords:fuse(). Setting fuse=False will merely concatenate all the mesh.coords, but not fuse them.

pyformex-0.8.6/pyformex/doc/html/ref/curve.html0000644000211500021150000112552511705104251021411 0ustar benebene00000000000000 30. curve — Definition of curves in pyFormex. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

29. toolbar — Toolbars for the pyFormex GUI.

Next topic

31. mesh_ext — Extended functionality of the Mesh class.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

30. curve — Definition of curves in pyFormex.

This module defines classes and functions specialized for handling one-dimensional geometry in pyFormex. These may be straight lines, polylines, higher order curves and collections thereof. In general, the curves are 3D, but special cases may be created for handling plane curves.

Classes defined in module curve

class curve.Curve

Base class for curve type classes.

This is a virtual class intended to be subclassed. It defines the common definitions for all curve types. The subclasses should at least define the following:

sub_points(t,j)
endPoints()

Return start and end points of the curve.

Returns a Coords with two points, or None if the curve is closed.

sub_points(t, j)

Return the points at values t in part j

t can be an array of parameter values, j is a single segment number.

sub_points_2(t, j)

Return the points at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

sub_directions(t, j)

Return the directions at values t in part j

t can be an array of parameter values, j is a single segment number.

sub_directions_2(t, j)

Return the directions at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

pointsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

directionsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

subPoints(div=10, extend=[0.0, 0.0])

Return a sequence of points on the Curve.

  • div: int or a list of floats (usually in the range [0.,1.]) If div is an integer, a list of floats is constructed by dividing the range [0.,1.] into div equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts.

The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded.

length()

Return the total length of the curve.

This is only available for curves that implement the ‘lengths’ method.

approx(ndiv=None, ntot=None)

Return a PolyLine approximation of the curve

If no ntot is given, the curve is approximated by ndiv straight segments over each part of the curve. If ntot is given, the curve is approximated by ntot straight segments over the total curve. This is based on a first approximation with ndiv segments over each part.

toFormex(*args, **kargs)

Convert a curve to a Formex.

This creates a polyline approximation as a plex-2 Formex. This is mainly used for drawing curves that do not implement their own drawing routines.

The method can be passed the same arguments as the approx method.

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

copy()

Return a deep copy of the object.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

class curve.PolyLine(coords=[], control=None, closed=False)

A class representing a series of straight line segments.

coords is a (npts,3) shaped array of coordinates of the subsequent vertices of the polyline (or a compatible data object). If closed == True, the polyline is closed by connecting the last point to the first. This does not change the vertex data.

The control parameter has the same meaning as coords and is added for symmetry with other Curve classes. If specified, it will override the coords argument.

close()

Close a PolyLine.

If the PolyLine is already closed, it is returned unchanged. Else it is closed by adding a segment from the last to the first point (even if these are coincident).

..warning :: This method changes the PolyLine inplace.

open()

Open a closed PolyLine.

If the PolyLine is closed, it is opened by removing the last segment. Else, it is returned unchanged.

..warning :: This method changes the PolyLine inplace.

Use split() if you want to open the PolyLine without losing a segment.

toFormex()

Return the polyline as a Formex.

toMesh()

Convert the polyLine to a plex-2 Mesh.

The returned Mesh is equivalent with the PolyLine.

sub_points(t, j)

Return the points at values t in part j

sub_points2(t, j)

Return the points at value,part pairs (t,j)

sub_directions(t, j)

Return the unit direction vectors at values t in part j.

vectors()

Return the vectors of each point to the next one.

The vectors are returned as a Coords object. If the curve is not closed, the number of vectors returned is one less than the number of points.

directions(return_doubles=False)

Returns unit vectors in the direction of the next point.

This directions are returned as a Coords object with the same number of elements as the point set.

If two subsequent points are identical, the first one gets the direction of the previous segment. If more than two subsequent points are equal, an invalid direction (NaN) will result.

If the curve is not closed, the last direction is set equal to the penultimate.

If return_doubles is True, the return value is a tuple of the direction and an index of the points that are identical with their follower.

avgDirections(return_doubles=False)

Returns the average directions at points.

For each point the returned direction is the average of the direction from the preceding point to the current, and the direction from the current to the next point.

If the curve is open, the first and last direction are equal to the direction of the first, resp. last segment.

Where two subsequent points are identical, the average directions are set equal to those of the segment ending in the first and the segment starting from the last.

lengths()

Return the length of the parts of the curve.

atLength(div)

Returns the parameter values for relative curve lengths div.

div is a list of relative curve lengths (from 0.0 to 1.0). As a convenience, a single integer value may be specified, in which case the relative curve lengths are found by dividing the interval [0.0,1.0] in the specified number of subintervals.

The function returns a list with the parameter values for the points at the specified relative lengths.

reverse()

Return the same curve with the parameter direction reversed.

split(i)

Split the curve at the point i.

Returns a list of open PolyLines: one, if the PolyLine is closed or i is one of the endpoints of an open PolyLine, two in other cases.

cutWithPlane(p, n, side='')

Return the parts of the polyline at one or both sides of a plane.

If side is ‘+’ or ‘-‘, return a list of PolyLines with the parts at the positive or negative side of the plane.

For any other value, returns a tuple of two lists of PolyLines, the first one being the parts at the positive side.

p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components.

append(PL, fuse=True, **kargs)

Append another PolyLine to this one.

Returns the concatenation of two open PolyLines. Closed PolyLines cannot be concatenated.

endPoints()

Return start and end points of the curve.

Returns a Coords with two points, or None if the curve is closed.

sub_points_2(t, j)

Return the points at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

sub_directions_2(t, j)

Return the directions at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

pointsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

directionsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

subPoints(div=10, extend=[0.0, 0.0])

Return a sequence of points on the Curve.

  • div: int or a list of floats (usually in the range [0.,1.]) If div is an integer, a list of floats is constructed by dividing the range [0.,1.] into div equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts.

The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded.

copy()

Return a deep copy of the object.

length()

Return the total length of the curve.

This is only available for curves that implement the ‘lengths’ method.

approx(ndiv=None, ntot=None)

Return a PolyLine approximation of the curve

If no ntot is given, the curve is approximated by ndiv straight segments over each part of the curve. If ntot is given, the curve is approximated by ntot straight segments over the total curve. This is based on a first approximation with ndiv segments over each part.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

class curve.Line(coords)

A Line is a PolyLine with exactly two points.

Parameters:

  • coords: compatible with (2,3) shaped float array
endPoints()

Return start and end points of the curve.

Returns a Coords with two points, or None if the curve is closed.

sub_points_2(t, j)

Return the points at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

sub_directions_2(t, j)

Return the directions at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

pointsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

directionsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

subPoints(div=10, extend=[0.0, 0.0])

Return a sequence of points on the Curve.

  • div: int or a list of floats (usually in the range [0.,1.]) If div is an integer, a list of floats is constructed by dividing the range [0.,1.] into div equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts.

The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded.

copy()

Return a deep copy of the object.

length()

Return the total length of the curve.

This is only available for curves that implement the ‘lengths’ method.

approx(ndiv=None, ntot=None)

Return a PolyLine approximation of the curve

If no ntot is given, the curve is approximated by ndiv straight segments over each part of the curve. If ntot is given, the curve is approximated by ntot straight segments over the total curve. This is based on a first approximation with ndiv segments over each part.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

close()

Close a PolyLine.

If the PolyLine is already closed, it is returned unchanged. Else it is closed by adding a segment from the last to the first point (even if these are coincident).

..warning :: This method changes the PolyLine inplace.

open()

Open a closed PolyLine.

If the PolyLine is closed, it is opened by removing the last segment. Else, it is returned unchanged.

..warning :: This method changes the PolyLine inplace.

Use split() if you want to open the PolyLine without losing a segment.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

toFormex()

Return the polyline as a Formex.

toMesh()

Convert the polyLine to a plex-2 Mesh.

The returned Mesh is equivalent with the PolyLine.

sub_points(t, j)

Return the points at values t in part j

sub_points2(t, j)

Return the points at value,part pairs (t,j)

sub_directions(t, j)

Return the unit direction vectors at values t in part j.

vectors()

Return the vectors of each point to the next one.

The vectors are returned as a Coords object. If the curve is not closed, the number of vectors returned is one less than the number of points.

directions(return_doubles=False)

Returns unit vectors in the direction of the next point.

This directions are returned as a Coords object with the same number of elements as the point set.

If two subsequent points are identical, the first one gets the direction of the previous segment. If more than two subsequent points are equal, an invalid direction (NaN) will result.

If the curve is not closed, the last direction is set equal to the penultimate.

If return_doubles is True, the return value is a tuple of the direction and an index of the points that are identical with their follower.

avgDirections(return_doubles=False)

Returns the average directions at points.

For each point the returned direction is the average of the direction from the preceding point to the current, and the direction from the current to the next point.

If the curve is open, the first and last direction are equal to the direction of the first, resp. last segment.

Where two subsequent points are identical, the average directions are set equal to those of the segment ending in the first and the segment starting from the last.

lengths()

Return the length of the parts of the curve.

atLength(div)

Returns the parameter values for relative curve lengths div.

div is a list of relative curve lengths (from 0.0 to 1.0). As a convenience, a single integer value may be specified, in which case the relative curve lengths are found by dividing the interval [0.0,1.0] in the specified number of subintervals.

The function returns a list with the parameter values for the points at the specified relative lengths.

reverse()

Return the same curve with the parameter direction reversed.

split(i)

Split the curve at the point i.

Returns a list of open PolyLines: one, if the PolyLine is closed or i is one of the endpoints of an open PolyLine, two in other cases.

cutWithPlane(p, n, side='')

Return the parts of the polyline at one or both sides of a plane.

If side is ‘+’ or ‘-‘, return a list of PolyLines with the parts at the positive or negative side of the plane.

For any other value, returns a tuple of two lists of PolyLines, the first one being the parts at the positive side.

p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components.

append(PL, fuse=True, **kargs)

Append another PolyLine to this one.

Returns the concatenation of two open PolyLines. Closed PolyLines cannot be concatenated.

class curve.BezierSpline(coords=None, deriv=None, curl=0.3333333333333333, control=None, closed=False, degree=3, endzerocurv=False)

A class representing a Bezier spline curve of degree 1, 2 or 3.

A Bezier spline of degree d is a continuous curve consisting of nparts successive parts, where each part is a Bezier curve of the same degree. Currently pyFormex can model linear, quadratic and cubic BezierSplines. A linear BezierSpline is equivalent to a PolyLine, which has more specialized methods than the BezierSpline, so it might be more sensible to use a PolyLine instead of the linear BezierSpline.

A Bezier curve of degree d is determined by d+1 control points, of which the first and the last are on the curve, while the intermediate d-1 points are not. Since the end point of one part is the begin point of the next part, a BezierSpline is described by ncontrol=d*nparts+1 control points if the curve is open, or ncontrol=d*nparts if the curve is closed.

The constructor provides different ways to initialize the full set of control points. In many cases the off-curve control points can be generated automatically.

Parameters:

  • coords : array_like (npoints,3) The points that are on the curve. For an open curve, npoints=nparts+1, for a closed curve, npoints = nparts. If not specified, the on-curve points should be included in the control argument.

  • deriv : array_like (npoints,3) or (2,3) If specified, it gives the direction of the curve at all points or at the endpoints only for a shape (2,3) array. For points where the direction is left unspecified or where the specified direction contains a NaN value, the direction is calculated as the average direction of the two line segments ending in the point. This will also be used for points where the specified direction contains a value NaN. In the two endpoints of an open curve however, this average direction can not be calculated: the two control points in these parts are set coincident.

  • curl : float The curl parameter can be set to influence the curliness of the curve in between two subsequent points. A value curl=0.0 results in straight segments. The higher the value, the more the curve becomes curled.

  • control : array(nparts,2,3) or array(ncontrol,3) If coords was specified, this should be a (nparts,2,3) array with the intermediate control points, two for each part.

    If coords was not specified, this should be the full array of ncontrol control points for the curve. The number of points should be a multiple of 3 plus 1. If the curve is closed, the last point is equal to the first and does not need to a multiple of 3 is also allowed, in which case the first point will be appended as last.

    If not specified, the control points are generated automatically from the coords, deriv and curl arguments. If specified, they override these parameters.

  • closed : boolean If True, the curve will be continued from the last point back to the first to create a closed curve.

  • degree: int (1, 2 or 3) Specfies the degree of the curve. Default is 3.

  • endzerocurv : boolean or tuple of two booleans. Specifies the end conditions for an open curve. If True, the end curvature will be forced to zero. The default is to use maximal continuity of the curvature. The value may be set to a tuple of two values to specify different conditions for both ends. This argument is ignored for a closed curve.

pointsOn()

Return the points on the curve.

This returns a Coords object of shape [nparts+1]. For a closed curve, the last point will be equal to the first.

pointsOff()

Return the points off the curve (the control points)

This returns a Coords object of shape [nparts,ndegree-1], or an empty Coords if degree <= 1.

part(j)

Returns the points defining part j of the curve.

sub_points(t, j)

Return the points at values t in part j.

sub_directions(t, j)

Return the unit direction vectors at values t in part j.

sub_curvature(t, j)

Return the curvature at values t in part j.

length_intgrnd(t, j)

Return the arc length integrand at value t in part j.

lengths()

Return the length of the parts of the curve.

toMesh()

Convert the BezierSpline to a Mesh.

For degrees 1 or 2, the returned Mesh is equivalent with the BezierSpline, and will have element type ‘line1’, resp. ‘line2’.

For degree 3, the returned Mesh will currently be a quadratic approximation with element type ‘line2’.

approx_by_subdivision(tol=0.001)

Return a PolyLine approximation of the curve.

tol is a tolerance value for the flatness of the curve. The flatness of each part is calculated as the maximum orthogonal distance of its intermediate control points from the straight segment through its end points.

Parts for which the distance is larger than tol are subdivided using de Casteljau’s algorithm. The subdivision stops if all parts are sufficiently flat. The return value is a PolyLine connecting the end points of all parts.

extend(extend=[1.0, 1.0])

Extend the curve beyond its endpoints.

This function will add a Bezier curve before the first part and/or after the last part by applying de Casteljau’s algorithm on this part.

reverse()

Return the same curve with the parameter direction reversed.

endPoints()

Return start and end points of the curve.

Returns a Coords with two points, or None if the curve is closed.

sub_points_2(t, j)

Return the points at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

sub_directions_2(t, j)

Return the directions at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

pointsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

directionsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

subPoints(div=10, extend=[0.0, 0.0])

Return a sequence of points on the Curve.

  • div: int or a list of floats (usually in the range [0.,1.]) If div is an integer, a list of floats is constructed by dividing the range [0.,1.] into div equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts.

The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded.

copy()

Return a deep copy of the object.

length()

Return the total length of the curve.

This is only available for curves that implement the ‘lengths’ method.

approx(ndiv=None, ntot=None)

Return a PolyLine approximation of the curve

If no ntot is given, the curve is approximated by ndiv straight segments over each part of the curve. If ntot is given, the curve is approximated by ntot straight segments over the total curve. This is based on a first approximation with ndiv segments over each part.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

toFormex(*args, **kargs)

Convert a curve to a Formex.

This creates a polyline approximation as a plex-2 Formex. This is mainly used for drawing curves that do not implement their own drawing routines.

The method can be passed the same arguments as the approx method.

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

class curve.CardinalSpline(coords, tension=0.0, closed=False, endzerocurv=False)

A class representing a cardinal spline.

Create a natural spline through the given points.

The Cardinal Spline with given tension is a Bezier Spline with curl :math: curl = ( 1 - tension) / 3 The separate class name is retained for compatibility and convenience. See CardinalSpline2 for a direct implementation (it misses the end intervals of the point set).

endPoints()

Return start and end points of the curve.

Returns a Coords with two points, or None if the curve is closed.

sub_points_2(t, j)

Return the points at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

sub_directions_2(t, j)

Return the directions at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

pointsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

directionsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

subPoints(div=10, extend=[0.0, 0.0])

Return a sequence of points on the Curve.

  • div: int or a list of floats (usually in the range [0.,1.]) If div is an integer, a list of floats is constructed by dividing the range [0.,1.] into div equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts.

The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded.

copy()

Return a deep copy of the object.

length()

Return the total length of the curve.

This is only available for curves that implement the ‘lengths’ method.

approx(ndiv=None, ntot=None)

Return a PolyLine approximation of the curve

If no ntot is given, the curve is approximated by ndiv straight segments over each part of the curve. If ntot is given, the curve is approximated by ntot straight segments over the total curve. This is based on a first approximation with ndiv segments over each part.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

toFormex(*args, **kargs)

Convert a curve to a Formex.

This creates a polyline approximation as a plex-2 Formex. This is mainly used for drawing curves that do not implement their own drawing routines.

The method can be passed the same arguments as the approx method.

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

pointsOn()

Return the points on the curve.

This returns a Coords object of shape [nparts+1]. For a closed curve, the last point will be equal to the first.

pointsOff()

Return the points off the curve (the control points)

This returns a Coords object of shape [nparts,ndegree-1], or an empty Coords if degree <= 1.

part(j)

Returns the points defining part j of the curve.

sub_points(t, j)

Return the points at values t in part j.

sub_directions(t, j)

Return the unit direction vectors at values t in part j.

sub_curvature(t, j)

Return the curvature at values t in part j.

length_intgrnd(t, j)

Return the arc length integrand at value t in part j.

lengths()

Return the length of the parts of the curve.

toMesh()

Convert the BezierSpline to a Mesh.

For degrees 1 or 2, the returned Mesh is equivalent with the BezierSpline, and will have element type ‘line1’, resp. ‘line2’.

For degree 3, the returned Mesh will currently be a quadratic approximation with element type ‘line2’.

approx_by_subdivision(tol=0.001)

Return a PolyLine approximation of the curve.

tol is a tolerance value for the flatness of the curve. The flatness of each part is calculated as the maximum orthogonal distance of its intermediate control points from the straight segment through its end points.

Parts for which the distance is larger than tol are subdivided using de Casteljau’s algorithm. The subdivision stops if all parts are sufficiently flat. The return value is a PolyLine connecting the end points of all parts.

extend(extend=[1.0, 1.0])

Extend the curve beyond its endpoints.

This function will add a Bezier curve before the first part and/or after the last part by applying de Casteljau’s algorithm on this part.

reverse()

Return the same curve with the parameter direction reversed.

class curve.CardinalSpline2(coords, tension=0.0, closed=False)

A class representing a cardinal spline.

endPoints()

Return start and end points of the curve.

Returns a Coords with two points, or None if the curve is closed.

sub_points_2(t, j)

Return the points at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

sub_directions(t, j)

Return the directions at values t in part j

t can be an array of parameter values, j is a single segment number.

sub_directions_2(t, j)

Return the directions at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

pointsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

directionsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

subPoints(div=10, extend=[0.0, 0.0])

Return a sequence of points on the Curve.

  • div: int or a list of floats (usually in the range [0.,1.]) If div is an integer, a list of floats is constructed by dividing the range [0.,1.] into div equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts.

The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded.

copy()

Return a deep copy of the object.

length()

Return the total length of the curve.

This is only available for curves that implement the ‘lengths’ method.

approx(ndiv=None, ntot=None)

Return a PolyLine approximation of the curve

If no ntot is given, the curve is approximated by ndiv straight segments over each part of the curve. If ntot is given, the curve is approximated by ntot straight segments over the total curve. This is based on a first approximation with ndiv segments over each part.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

toFormex(*args, **kargs)

Convert a curve to a Formex.

This creates a polyline approximation as a plex-2 Formex. This is mainly used for drawing curves that do not implement their own drawing routines.

The method can be passed the same arguments as the approx method.

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

class curve.NaturalSpline(coords, closed=False, endzerocurv=False)

A class representing a natural spline.

endPoints()

Return start and end points of the curve.

Returns a Coords with two points, or None if the curve is closed.

sub_points_2(t, j)

Return the points at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

sub_directions(t, j)

Return the directions at values t in part j

t can be an array of parameter values, j is a single segment number.

sub_directions_2(t, j)

Return the directions at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

pointsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

directionsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

subPoints(div=10, extend=[0.0, 0.0])

Return a sequence of points on the Curve.

  • div: int or a list of floats (usually in the range [0.,1.]) If div is an integer, a list of floats is constructed by dividing the range [0.,1.] into div equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts.

The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded.

copy()

Return a deep copy of the object.

length()

Return the total length of the curve.

This is only available for curves that implement the ‘lengths’ method.

approx(ndiv=None, ntot=None)

Return a PolyLine approximation of the curve

If no ntot is given, the curve is approximated by ndiv straight segments over each part of the curve. If ntot is given, the curve is approximated by ntot straight segments over the total curve. This is based on a first approximation with ndiv segments over each part.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

toFormex(*args, **kargs)

Convert a curve to a Formex.

This creates a polyline approximation as a plex-2 Formex. This is mainly used for drawing curves that do not implement their own drawing routines.

The method can be passed the same arguments as the approx method.

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

class curve.Arc3(coords)

A class representing a circular arc.

endPoints()

Return start and end points of the curve.

Returns a Coords with two points, or None if the curve is closed.

sub_points_2(t, j)

Return the points at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

sub_directions(t, j)

Return the directions at values t in part j

t can be an array of parameter values, j is a single segment number.

sub_directions_2(t, j)

Return the directions at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

pointsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

directionsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

subPoints(div=10, extend=[0.0, 0.0])

Return a sequence of points on the Curve.

  • div: int or a list of floats (usually in the range [0.,1.]) If div is an integer, a list of floats is constructed by dividing the range [0.,1.] into div equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts.

The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded.

copy()

Return a deep copy of the object.

length()

Return the total length of the curve.

This is only available for curves that implement the ‘lengths’ method.

approx(ndiv=None, ntot=None)

Return a PolyLine approximation of the curve

If no ntot is given, the curve is approximated by ndiv straight segments over each part of the curve. If ntot is given, the curve is approximated by ntot straight segments over the total curve. This is based on a first approximation with ndiv segments over each part.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

toFormex(*args, **kargs)

Convert a curve to a Formex.

This creates a polyline approximation as a plex-2 Formex. This is mainly used for drawing curves that do not implement their own drawing routines.

The method can be passed the same arguments as the approx method.

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

class curve.Arc(coords=None, center=None, radius=None, angles=None, angle_spec=0.017453292519943295)

A class representing a circular arc.

endPoints()

Return start and end points of the curve.

Returns a Coords with two points, or None if the curve is closed.

sub_points_2(t, j)

Return the points at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

sub_directions(t, j)

Return the directions at values t in part j

t can be an array of parameter values, j is a single segment number.

sub_directions_2(t, j)

Return the directions at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

pointsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

directionsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

subPoints(div=10, extend=[0.0, 0.0])

Return a sequence of points on the Curve.

  • div: int or a list of floats (usually in the range [0.,1.]) If div is an integer, a list of floats is constructed by dividing the range [0.,1.] into div equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts.

The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded.

copy()

Return a deep copy of the object.

length()

Return the total length of the curve.

This is only available for curves that implement the ‘lengths’ method.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

toFormex(*args, **kargs)

Convert a curve to a Formex.

This creates a polyline approximation as a plex-2 Formex. This is mainly used for drawing curves that do not implement their own drawing routines.

The method can be passed the same arguments as the approx method.

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

approx(chordal=0.01, ndiv=None)

Return a PolyLine approximation of the Arc.

Approximates the Arc by a sequence of inscribed straight line segments.

If ndiv is specified, the arc is divided in pecisely ndiv segments.

If ndiv is not given, the number of segments is determined from the chordal distance tolerance. It will guarantee that the distance of any point of the arc to the chordal approximation is less or equal than chordal times the radius of the arc.

class curve.Spiral(turns=2.0, nparts=100, rfunc=None)

A class representing a spiral curve.

endPoints()

Return start and end points of the curve.

Returns a Coords with two points, or None if the curve is closed.

sub_points(t, j)

Return the points at values t in part j

t can be an array of parameter values, j is a single segment number.

sub_points_2(t, j)

Return the points at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

sub_directions(t, j)

Return the directions at values t in part j

t can be an array of parameter values, j is a single segment number.

sub_directions_2(t, j)

Return the directions at values,parts given by zip(t,j)

t and j can both be arrays, but should have the same length.

pointsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

directionsAt(t)

Return the points at parameter values t.

Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment.

subPoints(div=10, extend=[0.0, 0.0])

Return a sequence of points on the Curve.

  • div: int or a list of floats (usually in the range [0.,1.]) If div is an integer, a list of floats is constructed by dividing the range [0.,1.] into div equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts.

The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded.

copy()

Return a deep copy of the object.

length()

Return the total length of the curve.

This is only available for curves that implement the ‘lengths’ method.

approx(ndiv=None, ntot=None)

Return a PolyLine approximation of the curve

If no ntot is given, the curve is approximated by ndiv straight segments over each part of the curve. If ntot is given, the curve is approximated by ntot straight segments over the total curve. This is based on a first approximation with ndiv segments over each part.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

toFormex(*args, **kargs)

Convert a curve to a Formex.

This creates a polyline approximation as a plex-2 Formex. This is mainly used for drawing curves that do not implement their own drawing routines.

The method can be passed the same arguments as the approx method.

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

Functions defined in module curve

curve.circle()

Create a spline approximation of a circle.

The returned circle lies in the x,y plane, has its center at (0,0,0) and has a radius 1.

In the current implementation it is approximated by a bezier spline with curl 0.375058 through 8 points.

curve.convertFormexToCurve(self, closed=False)

Convert a Formex to a Curve.

The following Formices can be converted to a Curve: - plex 2 : to PolyLine - plex 3 : to BezierSpline with degree=2 - plex 4 : to BezierSpline

pyformex-0.8.6/pyformex/doc/html/ref/timer.html0000644000211500021150000002357411705104255021411 0ustar benebene00000000000000 61. timer — A timer class. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

60. sendmail — sendmail.py: a simple program to send an email message

Next topic

pyFormex FAQ ‘n TRICKS

[FSF Associate Member]

Valid XHTML 1.0 Transitional

61. timer — A timer class.

Classes defined in module timer

class timer.Timer(start=None)

A class for measuring elapsed time.

A Timer object measures elapsed real time since a specified time, which by default is the time of the creation of the Timer.

Parameters:

  • start: a datetime object. If not specified, the time of the creation of the Timer is used.
reset(start=None)

(Re)Start the timer.

Sets the start time of the timer to the specified value, or to the current time by default.

Parameters:

  • start: a datetime object. If not specified, the current time as returned by datetime.now() is used.
read(reset=False)

Read the timer.

Returns the elapsed time since the last reset (or the creation of the timer) as a datetime.timedelta object.

If reset=True, the timer is reset to the time of reading.

seconds(reset=False, rounded=True)

Return the timer readings in seconds.

The default return value is a rounded integer number of seconds. With rounded == False, a floating point value with granularity of 1 microsecond is returned.

If reset=True, the timer is reset at the time of reading.

Functions defined in module timer

pyformex-0.8.6/pyformex/doc/html/ref/nurbs.html0000644000211500021150000010065311705104254021413 0ustar benebene00000000000000 34. nurbs — Using NURBS in pyFormex. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

33. geomtools — Basic geometrical operations.

Next topic

35. isopar — Isoparametric transformations

[FSF Associate Member]

Valid XHTML 1.0 Transitional

34. nurbs — Using NURBS in pyFormex.

The nurbs module defines functions and classes to manipulate NURBS curves and surface in pyFormex.

Classes defined in module nurbs

class nurbs.Coords4

A collection of points represented by their homogeneous coordinates.

While most of the pyFormex implementation is based on the 3D Cartesian coordinates class Coords, some applications may benefit from using homogeneous coordinates. The class Coords4 provides some basic functions and conversion to and from cartesian coordinates. Through the conversion, all other pyFormex functions, such as transformations, are available.

Coords4 is implemented as a float type numpy.ndarray whose last axis has a length equal to 4. Each set of 4 values (x,y,z,w) along the last axis represents a single point in 3D space. The cartesian coordinates of the point are obtained by dividing the first three values by the fourth: (x/w, y/w, z/w). A zero w-value represents a point at infinity. Converting such points to Coords will result in Inf or NaN values in the resulting object.

The float datatype is only checked at creation time. It is the responsibility of the user to keep this consistent throughout the lifetime of the object.

Just like Coords, the class Coords4 is derived from numpy.ndarray.

Parameters

data: array_like
If specified, data should evaluate to an array of floats, with the length of its last axis not larger than 4. When equal to four, each tuple along the last axis represents a ingle point in homogeneous coordinates. If smaller than four, the last axis will be expanded to four by adding values zero in the second and third position and values 1 in the last position. If no data are given, a single point (0.,0.,0.) will be created.
w: array_like
If specified, the w values are used to denormalize the homogeneous
data such that the last component becomes w.
dtyp: data-type
The datatype to be used. It not specified, the datatype of data is used, or the default Float (which is equivalent to numpy.float32).
copy: boolean
If True, the data are copied. By default, the original data are used if possible, e.g. if a correctly shaped and typed numpy.ndarray is specified.
normalize()

Normalize the homogeneous coordinates.

Two sets of homogeneous coordinates that differ only by a multiplicative constant refer to the same points in cartesian space. Normalization of the coordinates is a way to make the representation of a single point unique. Normalization is done so that the last component (w) is equal to 1.

The normalization of the coordinates is done in place.

Warning

Normalizing points at infinity will result in Inf or NaN values.

deNormalize(w)

Denormalizes the homogeneous coordinates.

This multiplies the homogeneous coordinates with the values w. w normally is a constant or an array with shape self.shape[:-1] + (1,). It then multiplies all 4 coordinates of a point with the same value, thus resulting in a denormalization while keeping the position of the point unchanged.

The denormalization of the coordinates is done in place. If the Coords4 object was normalized, it will have precisely w as its 4-th coordinate value after the call.

toCoords()

Convert homogeneous coordinates to cartesian coordinates.

Returns a Coords object with the cartesian coordinates of the points. Points at infinity (w=0) will result in Inf or NaN value. If there are no points at infinity, the resulting Coords point set is equivalent to the Coords4 one.

npoints()

Return the total number of points.

ncoords()

Return the total number of points.

x()

Return the x-plane

y()

Return the y-plane

z()

Return the z-plane

w()

Return the w-plane

bbox()

Return the bounding box of a set of points.

Returns the bounding box of the cartesian coordinates of the object.

actor(**kargs)

Graphical representation

class nurbs.NurbsCurve(control, degree=None, wts=None, knots=None, closed=False, blended=True)

A NURBS curve

The Nurbs curve is defined by nctrl control points, a degree (>= 1) and a knot vector with knots = nctrl+degree+1 parameter values.

The knots vector should hold nknots values in ascending order. The values are only defined upon a multiplicative constant and will be normalized to set the last value to 1. Sensible default values are constructed automatically by calling knotVector().

If no knots are given and no degree is specified, the degree is set to the number of control points - 1 if the curve is blended. If not blended, the degree is not set larger than 3.

bbox()

Return the bounding box of the NURBS curve.

pointsAt(u)

Return the points on the Nurbs curve at given parametric values.

Parameters:

u: (nu,) shaped float array: parametric values at which a point
is to be placed.

Returns: (nu,3) shaped Coords with nu points at the specified parametric values.

derivatives(at, d=1)

Returns the points and derivatives up to d at parameter values at

knotPoints()

Returns the points at the knot values.

The multiplicity of the knots is retained in the points set.

insertKnots(u)

Insert a set of knots in the curve.

u is a vector with knot parameter values to be inserted into the curve. The control points are adapted to keep the curve unchanged.

Returns a Nurbs curve equivalent with the original but with the specified knot values inserted in the knot vector, and the control points adapted.

decompose()

Decomposes a curve in subsequent Bezier curves.

Returns an equivalent unblended Nurbs.

actor(**kargs)

Graphical representation

class nurbs.NurbsSurface(control, degree=(None, None), wts=None, knots=(None, None), closed=(False, False), blended=(True, True))

A NURBS surface

The Nurbs surface is defined as a tensor product of NURBS curves in two parametrical directions u and v. The control points form a grid of (nctrlu,nctrlv) points. The other data are like those for a NURBS curve, but need to be specified as a tuple for the (u,v) directions.

The knot values are only defined upon a multiplicative constant, equal to the largest value. Sensible default values are constructed automatically by a call to the knotVector() function.

If no knots are given and no degree is specified, the degree is set to the number of control points - 1 if the curve is blended. If not blended, the degree is not set larger than 3.

Warning

This is a class under development!

bbox()

Return the bounding box of the NURBS surface.

pointsAt(u)

Return the points on the Nurbs surface at given parametric values.

Parameters:

u: (nu,2) shaped float array: nu parametric values (u,v) at which
a point is to be placed.

Returns: (nu,3) shaped Coords with nu points at the specified parametric values.

actor(**kargs)

Graphical representation

Functions defined in module nurbs

nurbs.globalInterpolationCurve(Q, degree=3, strategy=0.5)

Create a global interpolation NurbsCurve.

Given an ordered set of points Q, the globalInterpolationCurve is a NURBS curve of the given degree, passing through all the points.

Returns a NurbsCurve through the given point set. The number of control points is the same as the number of input points.

..warning: Currently there is the limitation that two consecutive
points should not coincide. If they do, a warning is shown and the double points will be removed.

The procedure works by computing the control points that will produce a NurbsCurve with the given points occurring at predefined parameter values. The strategy to set this values uses a parameter as exponent. Different values produce (slighly) different curves. Typical values are:

0.0: equally spaced (not recommended) 0.5: centripetal (default, recommended) 1.0: chord length (often used)

nurbs.uniformParamValues(n, umin=0.0, umax=1.0)

Create a set of uniformly distributed parameter values in a range.

Parameters:

n: int: number of intervals in which the range should be divided.
The number of values returned is n+1.
umin,`umax`: float: start and end value of the interval. Default
interval is [0.0..1.0].
Returns: a float array with n+1 equidistant values in the range umin..umax.
For n > 0, both of the endpoints are included. For n=0, a single value at the center of the interval will be returned. For n<0, an empty array is returned.

Example:

>>> uniformParamValues(4).tolist()
[0.0, 0.25, 0.5, 0.75, 1.0]
>>> uniformParamValues(0).tolist()
[0.5]
>>> uniformParamValues(-1).tolist()
[]
>>> uniformParamValues(2,1.5,2.5).tolist()
[1.5, 2.0, 2.5]
nurbs.knotVector(nctrl, degree, blended=True, closed=False)

Compute sensible knot vector for a Nurbs curve.

A knot vector is a sequence of non-decreasing parametric values. These values define the knots, i.e. the points where the analytical expression of the Nurbs curve may change. The knot values are only meaningful upon a multiplicative constant, and they are usually normalized to the range [0.0..1.0].

A Nurbs curve with nctrl points and of given degree needs a knot vector with nknots = nctrl+degree+1 values. A degree curve needs at least nctrl = degree+1 control points, and thus at least nknots = 2*(degree+1) knot values.

To make an open curve start and end in its end points, it needs knots with multiplicity degree+1 at its ends. Thus, for an open blended curve, the default policy is to set the knot values at the ends to 0.0, resp. 1.0, both with multiplicity degree+1, and to spread the remaining nctrl - degree - 1 values equally over the interval.

For a closed (blended) curve, the knots are equally spread over the interval, all having a multiplicity 1 for maximum continuity of the curve.

For an open unblended curve, all internal knots get multiplicity degree. This results in a curve that is only one time continuously derivable at the knots, thus the curve is smooth, but the curvature may be discontinuous. There is an extra requirement in this case: nctrl sohuld be a multiple of degree plus 1.

Example:

>>> print knotVector(7,3)
[ 0.    0.    0.    0.    0.25  0.5   0.75  1.    1.    1.    1.  ]
>>> print knotVector(7,3,closed=True)
[ 0.   0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9  1. ]
>>> print knotVector(7,3,blended=False)
[ 0.  0.  0.  0.  1.  1.  1.  2.  2.  2.  2.]
nurbs.toCoords4(x)

Convert cartesian coordinates to homogeneous

x: Coords
Array with cartesian coordinates.

Returns a Coords4 object corresponding to the input cartesian coordinates.

nurbs.pointsOnBezierCurve(P, u)

Compute points on a Bezier curve

Parameters: P is an array with n+1 points defining a Bezier curve of degree n. u is a vector with nu parameter values between 0 and 1.

Returns: An array with the nu points of the Bezier curve corresponding with the specified parametric values. ERROR: currently u is a single paramtric value!

See also: examples BezierCurve, Casteljou

nurbs.deCasteljou(P, u)

Compute points on a Bezier curve using deCasteljou algorithm

Parameters: P is an array with n+1 points defining a Bezier curve of degree n. u is a single parameter value between 0 and 1.

Returns: A list with point sets obtained in the subsequent deCasteljou approximations. The first one is the set of control points, the last one is the point on the Bezier curve.

This function works with Coords as well as Coords4 points.

nurbs.curveToNurbs(B)

Convert a BezierSpline to NurbsCurve

nurbs.frenet(d1, d2, d3=None)

Returns the 3 Frenet vectors and the curvature.

d1,d2 are the first and second derivatives at points of a curve. d3, if given, is the third derivative. Returns 3 normalized vectors along tangent, normal, binormal plus the curvature. if d3 is give, also returns the torsion of the curve.

pyformex-0.8.6/pyformex/doc/html/ref/simple.html0000644000211500021150000005061611705104255021557 0ustar benebene00000000000000 9. simple — Predefined geometries with a simple shape. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

8. connectivity — A class and functions for handling nodal connectivity.

Next topic

10. project — project.py

[FSF Associate Member]

Valid XHTML 1.0 Transitional

9. simple — Predefined geometries with a simple shape.

This module contains some functions, data and classes for generating Formex structures representing simple geometric shapes. You need to import this module in your scripts to have access to its contents.

Classes defined in module simple

Functions defined in module simple

simple.shape(name)

Return a Formex with one of the predefined named shapes.

This is a convenience function returning a plex-2 Formex constructed from one of the patterns defined in the simple.Pattern dictionary. Currently, the following pattern names are defined: ‘line’, ‘angle’, ‘square’, ‘plus’, ‘cross’, ‘diamond’, ‘rtriangle’, ‘cube’, ‘star’, ‘star3d’. See the Pattern example.

simple.regularGrid(x0, x1, nx)

Create a regular grid between points x0 and x1.

x0 and x1 are n-dimensional points (usually 1D, 2D or 3D). The space between x0 and x1 is divided in nx equal parts. nx should have the same dimension as x0 and x1. The result is a rectangular grid of coordinates in an array with shape ( nx[0]+1, nx[1]+1, ..., n ).

simple.point(x=0.0, y=0.0, z=0.0)

Return a Formex which is a point, by default at the origin.

Each of the coordinates can be specified and is zero by default.

simple.line(p1=[0.0, 0.0, 0.0], p2=[1.0, 0.0, 0.0], n=1)

Return a Formex which is a line between two specified points.

p1: first point, p2: second point The line is split up in n segments.

simple.rect(p1=[0.0, 0.0, 0.0], p2=[1.0, 0.0, 0.0], nx=1, ny=1)

Return a Formex which is a the circumference of a rectangle.

p1 and p2 are two opposite corner points of the rectangle. The edges of the rectangle are in planes parallel to the z-axis. There will always be two opposite edges that are parallel with the x-axis. The other two will only be parallel with the y-axis if both points have the same z-value, but in any case they will be parallel with the y-z plane.

The edges parallel with the x-axis are divide in nx parts, the other ones in ny parts.

simple.rectangle(nx, ny, b=None, h=None, bias=0.0, diag=None)

Return a Formex representing a rectangluar surface.

The rectangle has a size(b,h) divided into (nx,ny) cells.

The default b/h values are equal to nx/ny, resulting in a modular grid. The rectangle lies in the (x,y) plane, with one corner at [0,0,0]. By default, the elements are quads. By setting diag=’u’,’d’ of ‘x’, diagonals are added in /, resp. and both directions, to form triangles.

simple.circle(a1=2.0, a2=0.0, a3=360.0, r=None, n=None, c=None, eltype='line2')

A polygonal approximation to a circle or arc.

All points generated by this function lie on a circle with unit radius at the origin in the x-y-plane.

  • a1: the angle enclosed between the start and end points of each line segment (dash angle).
  • a2: the angle enclosed between the start points of two subsequent line segments (module angle). If a2==0.0, a2 will be taken equal to a1.
  • a3: the total angle enclosed between the first point of the first segment and the end point of the last segment (arc angle).

All angles are given in degrees and are measured in the direction from x- to y-axis. The first point of the first segment is always on the x-axis.

The default values produce a full circle (approximately). If $a3 < 360$, the result is an arc. Large values of a1 and a2 result in polygones. Thus circle(120.) is an equilateral triangle and circle(60.) is regular hexagon.

Remark that the default a2 == a1 produces a continuous line, while a2 > a1 results in a dashed line.

Three optional arguments can be added to scale and position the circle in 3D space:

  • r: the radius of the circle
  • n: the normal on the plane of the circle
  • c: the center of the circle
simple.polygon(n)

A regular polygon with n sides.

Creates the circumference of a regular polygon with $n$ sides, inscribed in a circle with radius 1 and center at the origin. The first point lies on the axis 0. All points are in the (0,1) plane. The return value is a plex-2 Formex. This function is equivalent to circle(360./n).

simple.triangle()

An equilateral triangle with base [0,1] on axis 0.

Returns an equilateral triangle with side length 1. The first point is the origin, the second points is on the axis 0. The return value is a plex-3 Formex.

simple.quadraticCurve(x=None, n=8)

Create a collection of curves.

x is a (3,3) shaped array of coordinates, specifying 3 points.

Return an array with 2*n+1 points lying on the quadratic curve through the points x. Each of the intervals [x0,x1] and [x1,x2] will be divided in n segments.

simple.sphere2(nx, ny, r=1, bot=-90, top=90)

Return a sphere consisting of line elements.

A sphere with radius r is modeled by a regular grid of nx longitude circles, ny latitude circles and their diagonals.

The 3 sets of lines can be distinguished by their property number: 1: diagonals, 2: meridionals, 3: horizontals.

The sphere caps can be cut off by specifying top and bottom latitude angles (measured in degrees from 0 at north pole to 180 at south pole.

simple.sphere3(nx, ny, r=1, bot=-90, top=90)

Return a sphere consisting of surface triangles

A sphere with radius r is modeled by the triangles fromed by a regular grid of nx longitude circles, ny latitude circles and their diagonals.

The two sets of triangles can be distinguished by their property number: 1: horizontal at the bottom, 2: horizontal at the top.

The sphere caps can be cut off by specifying top and bottom latitude angles (measured in degrees from 0 at north pole to 180 at south pole.

simple.connectCurves(curve1, curve2, n)

Connect two curves to form a surface.

curve1, curve2 are plex-2 Formices with the same number of elements. The two curves are connected by a surface of quadrilaterals, with n elements in the direction between the curves.

simple.sector(r, t, nr, nt, h=0.0, diag=None)

Constructs a Formex which is a sector of a circle/cone.

A sector with radius r and angle t is modeled by dividing the radius in nr parts and the angle in nt parts and then creating straight line segments. If a nonzero value of h is given, a conical surface results with its top at the origin and the base circle of the cone at z=h. The default is for all points to be in the (x,y) plane.

By default, a plex-4 Formex results. The central quads will collapse into triangles. If diag=’up’ or diag = ‘down’, all quads are divided by an up directed diagonal and a plex-3 Formex results.

simple.cylinder(D, L, nt, nl, D1=None, angle=360.0, bias=0.0, diag=None)

Create a cylindrical, conical or truncated conical surface.

Returns a Formex representing (an approximation of) a cylindrical or (possibly truncated) conical surface with its axis along the z-axis. The resulting surface is actually a prism or pyramid, and only becomes a good approximation of a cylinder or cone for high values of nt.

Parameters:

  • D: base diameter (at z=0) of the cylinder/cone,
  • L: length (along z-axis) of the cylinder/cone,
  • nt: number of elements along the circumference,
  • nl: number of elements along the length,
  • D1: diameter at the top (z=L) of the cylinder/cone: if unspecified, it is taken equal to D and a cylinder results. Setting either D1 or D to zero results in a cone, other values will create a truncated cone.
  • diag: by default, the elements are quads. Setting diag to ‘u’ or ‘d’ will put in an ‘up’ or ‘down’ diagonal to create triangles.
simple.cuboid(xmin=[0.0, 0.0, 0.0], xmax=[1.0, 1.0, 1.0])

Create a rectangular prism

Creates a rectangular prism with faces parallel to the global axes through the points xmin and xmax.

Returns a single element Formex with eltype ‘hex8’.

pyformex-0.8.6/pyformex/doc/html/ref/imageViewer.html0000644000211500021150000001762111705104253022527 0ustar benebene00000000000000 27. imageViewer — A general image viewer — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

26. imagecolor — Using bitmap images as colors.

Next topic

28. scriptMenu — Menu with pyFormex scripts.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

27. imageViewer — A general image viewer

Part of this code was borrowed from the TrollTech Qt examples.

Classes defined in module imageViewer

Functions defined in module imageViewer

pyformex-0.8.6/pyformex/doc/html/ref/olist.html0000644000211500021150000003072511705104254021416 0ustar benebene00000000000000 54. olist — Some convenient shortcuts for common list operations. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

53. plot2d — plot2d.py

Next topic

55. mydict

[FSF Associate Member]

Valid XHTML 1.0 Transitional

54. olist — Some convenient shortcuts for common list operations.

While most of these functions look (and work) like set operations, their result differs from using Python builtin Sets in that they preserve the order of the items in the lists.

Classes defined in module olist

Functions defined in module olist

olist.roll(a, n=1)

Roll the elements of a list n positions forward (backward if n < 0)

olist.union(a, b)

Return a list with all items in a or in b, in the order of a,b.

olist.difference(a, b)

Return a list with all items in a but not in b, in the order of a.

olist.symdifference(a, b)

Return a list with all items in a or b but not in both.

olist.intersection(a, b)

Return a list with all items in a and in b, in the order of a.

olist.concatenate(a)

Concatenate a list of lists

olist.flatten(a, recurse=False)

Flatten a nested list.

By default, lists are flattened one level deep. If recurse=True, flattening recurses through all sublists.

>>> flatten([[[3.,2,],6.5,],[5],6,'hi'])
[[3.0, 2], 6.5, 5, 6, 'hi']
>>> flatten([[[3.,2,],6.5,],[5],6,'hi'],True)
[3.0, 2, 6.5, 5, 6, 'hi']
olist.select(a, b)

Return a subset of items from a list.

Returns a list with the items of a for which the index is in b.

olist.collectOnLength(items, return_indices=False)

Collect items of a list in separate bins according to the item length.

items is a list of items of any type having the len() method. The items are put in separate lists according to their length.

The return value is a dict where the keys are item lengths and the values are lists of items with this length.

If return_indices is True, a second dict is returned, with the same keys, holding the original indices of the items in the lists.

pyformex-0.8.6/pyformex/doc/html/ref/isopar.html0000644000211500021150000002571511705104253021563 0ustar benebene00000000000000 35. isopar — Isoparametric transformations — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

34. nurbs — Using NURBS in pyFormex.

Next topic

36. section2d — Some functions operating on 2D structures.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

35. isopar — Isoparametric transformations

Classes defined in module isopar

class isopar.Isopar(eltype, coords, oldcoords)

A class representing an isoparametric transformation

type is one of the keys in Isopar.isodata coords and oldcoords can be either arrays, Coords or Formex instances, but should be of equal shape, and match the number of atoms in the specified transformation type

The following three formulations are equivalent

trf = Isopar(eltype,coords,oldcoords)
G = F.isopar(trf)

trf = Isopar(eltype,coords,oldcoords)
G = trf.transform(F)

G = isopar(F,eltype,coords,oldcoords)
transform(X)

Apply isoparametric transform to a set of coordinates.

Returns a Coords array with same shape as X

Functions defined in module isopar

isopar.evaluate(atoms, x, y=0, z=0)

Build a matrix of functions of coords.

  • atoms: a list of text strings representing a mathematical function of x, and possibly of y and z.
  • x, y, z: a list of x- (and optionally y-, z-) values at which the atoms will be evaluated. The lists should have the same length.

Returns a matrix with nvalues rows and natoms colums.

pyformex-0.8.6/pyformex/doc/html/ref/objects.html0000644000211500021150000006576711705104254021733 0ustar benebene00000000000000 52. objects — Selection of objects from the global dictionary. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

51. tools — tools.py

Next topic

53. plot2d — plot2d.py

[FSF Associate Member]

Valid XHTML 1.0 Transitional

52. objects — Selection of objects from the global dictionary.

This is a support module for other pyFormex plugins.

Classes defined in module objects

class objects.Objects(clas=None, like=None, filter=None, namelist=[])

A selection of objects from the pyFormex Globals().

The class provides facilities to filter the global objects by their type and select one or more objects by their name(s). The values of these objects can be changed and the changes can be undone.

object_type()

Return the type of objects in this selection.

set(names)

Set the selection to a list of names.

namelist can be a single object name or a list of names. This will also store the current values of the variables.

append(name, value=None)

Add a name,value to a selection.

If no value is given, its current value is used. If a value is given, it is exported.

clear()

Clear the selection.

listAll()

Return a list with all selectable objects.

This lists all the global names in pyformex.PF that match the class and/or filter (if specified).

remember(copy=False)

Remember the current values of the variables in selection.

If copy==True, the values are copied, so that the variables’ current values can be changed inplace without affecting the remembered values.

changeValues(newvalues)

Replace the current values of selection by new ones.

The old values are stored locally, to enable undo operations.

This is only needed to change the values of objects that can not be changed inplace!

undoChanges()

Undo the last changes of the values.

check(single=False, warn=True)

Check that we have a current selection.

Returns the list of Objects corresponding to the current selection. If single==True, the selection should hold exactly one Object name and a single Object instance is returned. If there is no selection, or more than one in case of single==True, an error message is displayed and None is returned

odict()

Return the currently selected items as a dictionary.

Returns an ODict with the currently selected objects in the order of the selection.names.

ask(mode='multi')

Show the names of known objects and let the user select one or more.

mode can be set to’single’ to select a single item. Return a list with the selected names, possibly empty (if nothing was selected by the user), or None if there is nothing to choose from. This also sets the current selection to the selected names, unless the return value is None, in which case the selection remains unchanged.

ask1()

Select a single object from the list.

Returns the object, not its name!

forget()

Remove the selection from the globals.

keep()

Remove everything except the selection from the globals.

printval()

Print the selection.

printbbox()

Print the bbox of the current selection.

writeToFile(filename)

Write objects to a geometry file.

readFromFile(filename)

Read objects from a geometry file.

class objects.DrawableObjects(*args, **kargs)

A selection of drawable objects from the globals().

This is a subclass of Objects. The constructor has the same arguments as the Objects class, plus the following:

  • annotations: a set of functions that draw annotations of the objects. Each function should take an object name as argument, and draw the requested annotation for the named object. If the object does not have the annotation, it should be silently ignored. Default annotation functions available are:

    • draw_object_name
    • draw_elem_numbers
    • draw_nodes
    • draw_node_numbers
    • draw_bbox

    No annotation functions are activated by default.

ask(mode='multi')

Interactively sets the current selection.

drawChanges()

Draws old and new version of a Formex with different colors.

old and new can be a either Formex instances or names or lists thereof. old are drawn in yellow, new in the current color.

undoChanges()

Undo the last changes of the values.

toggleAnnotation(f, onoff=None)

Toggle the display of an annotation On or Off.

If given, onoff is True or False. If no onoff is given, this works as a toggle.

drawAnnotation(f)

Draw some annotation for the current selection.

removeAnnotation(f)

Remove the annotation f.

hasAnnotation(f)

Return the status of annotation f

setProp(prop=None)

Set the property of the current selection.

prop should be a single integer value or None. If None is given, a value will be asked from the user. If a negative value is given, the property is removed. If a selected object does not have a setProp method, it is ignored.

delProp()

Delete the property of the current selection.

This well reset the prop attribute of all selected objects to None.

object_type()

Return the type of objects in this selection.

set(names)

Set the selection to a list of names.

namelist can be a single object name or a list of names. This will also store the current values of the variables.

append(name, value=None)

Add a name,value to a selection.

If no value is given, its current value is used. If a value is given, it is exported.

clear()

Clear the selection.

listAll()

Return a list with all selectable objects.

This lists all the global names in pyformex.PF that match the class and/or filter (if specified).

remember(copy=False)

Remember the current values of the variables in selection.

If copy==True, the values are copied, so that the variables’ current values can be changed inplace without affecting the remembered values.

changeValues(newvalues)

Replace the current values of selection by new ones.

The old values are stored locally, to enable undo operations.

This is only needed to change the values of objects that can not be changed inplace!

check(single=False, warn=True)

Check that we have a current selection.

Returns the list of Objects corresponding to the current selection. If single==True, the selection should hold exactly one Object name and a single Object instance is returned. If there is no selection, or more than one in case of single==True, an error message is displayed and None is returned

odict()

Return the currently selected items as a dictionary.

Returns an ODict with the currently selected objects in the order of the selection.names.

ask1()

Select a single object from the list.

Returns the object, not its name!

forget()

Remove the selection from the globals.

keep()

Remove everything except the selection from the globals.

printval()

Print the selection.

printbbox()

Print the bbox of the current selection.

writeToFile(filename)

Write objects to a geometry file.

readFromFile(filename)

Read objects from a geometry file.

Functions defined in module objects

objects.draw_object_name(n)

Draw the name of an object at its center.

objects.draw_elem_numbers(n)

Draw the numbers of an object’s elements.

objects.draw_nodes(n)

Draw the nodes of an object.

objects.draw_node_numbers(n)

Draw the numbers of an object’s nodes.

objects.draw_free_edges(n)

Draw the feature edges of an object.

objects.draw_bbox(n)

Draw the bbox of an object.

pyformex-0.8.6/pyformex/doc/html/ref/canvas.html0000644000211500021150000010144311705104247021535 0ustar benebene00000000000000 21. canvas — This implements an OpenGL drawing widget for painting 3D scenes. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

20. gluttext — 2D text decorations using GLUT fonts

Next topic

22. viewport — Interactive OpenGL Canvas embedded in a Qt4 widget.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

21. canvas — This implements an OpenGL drawing widget for painting 3D scenes.

Classes defined in module canvas

class canvas.CanvasSettings(**kargs)

A collection of settings for an OpenGL Canvas.

The canvas settings are a collection of settings and default values affecting the rendering in an individual viewport. There are two type of settings:

  • mode settings are set during the initialization of the canvas and can/should not be changed during the drawing of actors and decorations;
  • default settings can be used as default values but may be changed during the drawing of actors/decorations: they are reset before each individual draw instruction.

Currently the following mode settings are defined:

  • bgmode: the viewport background mode. Should be one of ‘solid’, ‘vertical gradient’, ‘horizontal gradient’
  • bgcolor: the viewport background color (left/top color for graded modes)
  • bgcolor2: right/bottom color for a grade background
  • slcolor: the highlight color
  • rendermode: the rendering mode
  • shading: True is smooth, False is flat
  • lighting: True or False

The list of default settings includes:

  • fgcolor: the default drawing color
  • bkcolor: the default backface color
  • colormap: the default color map to be used if color is an index
  • bklormap: the default color map to be used if bkcolor is an index
  • pointsize: the default size for drawing points
  • marksize: the default size for drawing markers
  • linewidth: the default width for drawing lines

Any of these values can be set in the constructor using a keyword argument. All items that are not set, will get their value from the configuration file(s).

reset(d={})

Reset the CanvasSettings to its defaults.

The default values are taken from the configuration files. An optional dictionary may be specified to override (some of) these defaults.

update(d, strict=True)

Update current values with the specified settings

Returns the sanitized update values.

classmethod checkDict(clas, dict, strict=True)

Transform a dict to acceptable settings.

setMode()

Activate the mode canvas settings in the GL machine.

setDefault()

Activate the default canvas settings in the GL machine.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class canvas.Canvas(settings={})

A canvas for OpenGL rendering.

The Canvas is a class holding all global data of an OpenGL scene rendering. This includes colors, line types, rendering mode. It also keeps lists of all the actors and decorations in the scene. It always has a Camera object holding import viewing parameters. And finally it stores the lighting information.

It does not however contain the viewport size and position.

do_lighting(onoff)

Toggle lights on/off.

has_lighting()

Return the status of the lighting.

resetDefaults(dict={})

Return all the settings to their default values.

setAmbient(ambient)

Set the global ambient lighting for the canvas

setMaterial(matname)

Set the default material light properties for the canvas

resetLighting()

Change the light parameters

setRenderMode(mode, lighting=None)

Set the rendering mode.

This sets or changes the rendermode and lighting attributes. If lighting is not specified, it is set depending on the rendermode.

If the canvas has not been initialized, this merely sets the attributes self.rendermode and self.lighting. If the canvas was already initialized (it has a camera), and one of the specified settings is fdifferent from the existing, the new mode is set, the canvas is re-initialized according to the newly set mode, and everything is redrawn with the new mode.

setLineWidth(lw)

Set the linewidth for line rendering.

setLineStipple(repeat, pattern)

Set the linestipple for line rendering.

setPointSize(sz)

Set the size for point drawing.

setBgColor(color1, color2=None, mode='solid')

Set the background color.

If one color is specified, a solid background is set. If two colors are specified, a graded background is set and an object is created to display the background.

createBackground()

Create the background object.

setFgColor(color)

Set the default foreground color.

setSlColor(color)

Set the highlight color.

setTriade(on=None, pos='lb', siz=100)

Toggle the display of the global axes on or off.

If on is True, a triade of global axes is displayed, if False it is removed. The default (None) toggles between on and off.

glinit()

Initialize the rendering machine.

The rendering machine is initialized according to: - self.rendermode: one of - self.lighting

glupdate()

Flush all OpenGL commands, making sure the display is updated.

clear()

Clear the canvas to the background color.

setDefaults()

Activate the canvas settings in the GL machine.

display()

(Re)display all the actors in the scene.

This should e.g. be used when actors are added to the scene, or after changing camera position/orientation or lens.

begin_2D_drawing()

Set up the canvas for 2D drawing on top of 3D canvas.

The 2D drawing operation should be ended by calling end_2D_drawing. It is assumed that you will not try to change/refresh the normal 3D drawing cycle during this operation.

end_2D_drawing()

Cancel the 2D drawing mode initiated by begin_2D_drawing.

setBbox(bbox=None)

Set the bounding box of the scene you want to be visible.

addActor(actor)

Add a 3D actor to the 3D scene.

removeActor(actor)

Remove a 3D actor from the 3D scene.

addHighlight(actor)

Add a 3D actor highlight to the 3D scene.

removeHighlight(actor)

Remove a 3D actor highlight from the 3D scene.

addAnnotation(actor)

Add an annotation to the 3D scene.

removeAnnotation(actor)

Remove an annotation from the 3D scene.

addDecoration(actor)

Add a 2D decoration to the canvas.

removeDecoration(actor)

Remove a 2D decoration from the canvas.

remove(itemlist)

Remove a list of any actor/highlights/annotation/decoration items.

This will remove the items from any of the canvas lists in which the item appears. itemlist can also be a single item instead of a list.

removeActors(actorlist=None)

Remove all actors in actorlist (default = all) from the scene.

removeHighlights(actorlist=None)

Remove all highlights in actorlist (default = all) from the scene.

removeAnnotations(actorlist=None)

Remove all annotations in actorlist (default = all) from the scene.

removeDecorations(actorlist=None)

Remove all decorations in actorlist (default = all) from the scene.

removeAll()

Remove all actors and decorations

redrawAll()

Redraw all actors in the scene.

setCamera(bbox=None, angles=None)

Sets the camera looking under angles at bbox.

This function sets the camera angles and adjusts the zooming. The camera distance remains unchanged.

If a bbox is specified, the camera will be zoomed to make the whole bbox visible. If no bbox is specified, the current scene bbox will be used. If no current bbox has been set, it will be calculated as the bbox of the whole scene.

If no camera angles are given, the camera orientation is kept. angles can be a set of 3 angles, or a string

project(x, y, z, locked=False)

Map the object coordinates (x,y,z) to window coordinates.

unProject(x, y, z, locked=False)

Map the window coordinates (x,y,z) to object coordinates.

zoom(f, dolly=True)

Dolly zooming.

Zooms in with a factor f by moving the camera closer to the scene. This does noet change the camera’s FOV setting. It will change the perspective view though.

zoomRectangle(x0, y0, x1, y1)

Rectangle zooming.

Zooms in/out by changing the area and position of the visible part of the lens. Unlike zoom(), this does not change the perspective view.

x0,y0,x1,y1 are pixel coordinates of the lower left and upper right corners of the area of the lens that will be mapped on the canvas viewport. Specifying values that lead to smaller width/height will zoom in.

zoomCentered(w, h, x=None, y=None)

Rectangle zooming with specified center.

This is like zoomRectangle, but the zoom rectangle is specified by its center and size, which may be more appropriate when using off-center zooming.

zoomAll()

Rectangle zoom to make full scene visible.

saveBuffer()

Save the current OpenGL buffer

showBuffer()

Show the saved buffer

draw_focus_rectangle(width=2)

Draw the focus rectangle.

The specified width is HALF of the line width

draw_cursor(x, y)

draw the cursor

Functions defined in module canvas

canvas.gl_pickbuffer()

Return a list of the 2nd numbers in the openGL pick buffer.

canvas.glLineStipple(factor, pattern)

Set the line stipple pattern.

When drawing lines, OpenGl can use a stipple pattern. The stipple is defined by two values: a pattern (on/off) of maximum 16 bits, used on the pixel level, and a multiplier factor for each bit.

If factor <= 0, the stippling is disabled.

canvas.glSmooth()

Enable smooth shading

canvas.glFlat()

Disable smooth shading

canvas.onOff(onoff)

Convert On/Off strings to a boolean

canvas.glEnable(facility, onoff)

Enable/Disable an OpenGL facility, depending on onoff value

facility is an OpenGL facility. onoff can be True or False to enable, resp. disable the facility, or None to leave it unchanged.

pyformex-0.8.6/pyformex/doc/html/ref/colorscale.html0000644000211500021150000003102611705104247022407 0ustar benebene00000000000000 16. colorscale — Color mapping of a range of values. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

15. menu — Menus for the pyFormex GUI.

Next topic

17. actors — OpenGL actors for populating the 3D scene.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

16. colorscale — Color mapping of a range of values.

Classes defined in module colorscale

class colorscale.ColorScale(palet='RAINBOW', minval=0.0, maxval=1.0, midval=None, exp=1.0, exp2=None)

Mapping floating point values into colors.

A colorscale maps floating point values within a certain range into colors and can be used to provide visual representation of numerical values. This is e.g. quite useful in Finite Element postprocessing (see the postproc plugin).

The ColorLegend class provides a way to make the ColorScale visible on the canvas.

scale(val)

Scale a value to the range -1...1.

If the ColorScale has only one exponent, values in the range mival..maxval are scaled to the range -1..+1.

If two exponents were specified, scaling is done independently in the intervals minval..midval and midval..maxval, mapped resp. using exp2 and exp onto the intevals -1..0 and 0..1.

color(val)

Return the color representing a value val.

The returned color is a tuple of three RGB values in the range 0-1. The color is obtained by first scaling the value to the -1..1 range using the ‘scale’ method, and then using that result to pick a color value from the palet. A palet specifies the three colors corresponding to the -1, 0 and 1 values.

class colorscale.ColorLegend(colorscale, n)

A colorlegend divides a in a number of subranges.

Parameters:

  • colorscale: a ColorScale instance
  • n: a positive integer

For a ColorScale without midval, the full range is divided in n subranges; for a scale with midval, each of the two ranges is divided in n/2 subranges. In each case the legend has n subranges limited by n+1 values. The n colors of the legend correspond to the middle value of each subrange.

overflow(oflow=None)

Raise a runtime error if oflow == None, else return oflow.

color(val)

Return the color representing a value val.

The color is that of the subrange holding the value. If the value matches a subrange limit, the lower range color is returned. If the value falls outside the colorscale range, a runtime error is raised, unless the corresponding underflowcolor or overflowcolor attribute has been set, in which case this attirbute is returned. Though these attributes can be set to any not None value, it will usually be set to some color value, that will be used to show overflow values. The returned color is a tuple of three RGB values in the range 0-1.

Functions defined in module colorscale

pyformex-0.8.6/pyformex/doc/html/ref/project.html0000644000211500021150000004276211705104254021736 0ustar benebene00000000000000 10. project — project.py — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

9. simple — Predefined geometries with a simple shape.

Next topic

11. utils — A collection of miscellaneous utility functions.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

10. project — project.py

Functions for managing a project in pyFormex.

Classes defined in module project

class project.Project(filename=None, access='wr', convert=True, signature='pyFormex 0.8.6', compression=5, binary=True, data={}, **kargs)

Project: a persistent storage of pyFormex data.

A pyFormex Project is a regular Python dict that can contain named data of any kind, and can be saved to a file to create persistence over different pyFormex sessions.

The Project class is used by pyFormex for the pyformex.PF global variable that collects variables exported from pyFormex scripts. While projects are mostly handled through the pyFormex GUI, notably the File menu, the user may also create and handle his own Project objects from a script.

Because of the way pyFormex Projects are written to file, there may be problems when trying to read a project file that was created with another pyFormex version. Problems may occur if the project contains data of a class whose implementation has changed, or whose definition has been relocated. Our policy is to provide backwards compatibility: newer versions of pyFormex will normally read the older project formats. Saving is always done in the newest format, and these can generally not be read back by older program versions (unless you are prepared to do some hacking).

Warning

Compatibility issues.

Occasionally you may run into problems when reading back an old project file, especially when it was created by an unreleased (development) version of pyFormex. Because pyFormex is evolving fast, we can not test the full compatibility with every revision You can file a support request on the pyFormex support tracker. and we will try to add the required conversion code to pyFormex.

The project files are mainly intended as a means to easily save lots of data of any kind and to restore them in the same session or a later session, to pass them to another user (with the same or later pyFormex version), to store them over a medium time period. Occasionally opening and saving back your project files with newer pyFormex versions may help to avoid read-back problems over longer time.

For a problemless long time storage of Geometry type objects you may consider to write them to a pyFormex Geometry file (.pgf) instead, since this uses a stable ascii based format. It can (currently) not deal with other data types however.

Parameters:

  • filename: the name of the file where the Project data will be saved. If the file exists (and access is not w), it should be a previously saved Project and an attempt will be made to load the data from this file into the Project. If this fails, an error is raised.

    If the file exists and access is w, it will be overwritten, destroying any previous contents.

    If no filename is specified, a temporary file will be created when the Project is saved for the first time. The file with not be automatically deleted. The generated name can be retrieved from the filename attribute.

  • access: One of ‘wr’ (default), ‘rw’, ‘w’ or ‘r’. If the string contains an ‘r’ the data from an existing file will be read into the dict. If the string starts with an ‘r’, the file should exist. If the string contains a ‘w’, the data can be written back to the file. The ‘r’ access mode is a read-only mode.

    access

    File must exist

    File is read

    File can be written

    r

    yes

    yes

    no

    rw

    yes

    yes

    yes

    wr

    no

    if it exists

    yes

    w

    no

    no

    yes

  • convert: if True (default), and the file is opened for reading, an attempt is made to open old projects in a compatibility mode, doing the necessary conversions to new data formats. If convert is set False, only the latest format can be read and older formats will generate an error.

  • signature: A text that will be written in the header record of the file. This can e.g. be used to record format version info.

  • compression: An integer from 0 to 9: compression level. For large data sets, compression leads to much smaller files. 0 is no compression, 9 is maximal compression. The default is 4.

  • binary: if False and no compression is used, storage is done in an ASCII format, allowing to edit the file. Otherwise, storage uses a binary format. Using binary=False is deprecated.

  • data: a dict-like object to initialize the Project contents. These data may override values read from the file.

header_data()

Construct the data to be saved in the header.

save()

Save the project to file.

readHeader()

Read the header from a project file.

Tries to read the header from different legacy formats, and if succesfull, adjusts the project attributes according to the values in the header. Returns the open file if succesfull.

load(try_resolve=False)

Load a project from file.

The loaded definitions will update the current project.

convert(filename=None)

Convert an old format project file.

The project file is read, and if successfull, is immediately saved. By default, this will overwrite the original file. If a filename is specified, the converted data are saved to that file. In both cases, access is set to ‘wr’, so the tha saved data can be read back immediately.

uncompress()

Uncompress a compressed project file.

The project file is read, and if successfull, is written back in uncompressed format. This allows to make conversions of the data inside.

delete()

Unrecoverably delete the project file.

Functions defined in module project

project.find_global(module, name)

Override the import path of some classes

project.pickle_load(f, try_resolve=True)

Load data from pickle file f.

pyformex-0.8.6/pyformex/doc/html/ref/trisurface.html0000644000211500021150000037134411705104255022441 0ustar benebene00000000000000 32. trisurface — Operations on triangulated surfaces. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

31. mesh_ext — Extended functionality of the Mesh class.

Next topic

33. geomtools — Basic geometrical operations.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

32. trisurface — Operations on triangulated surfaces.

A triangulated surface is a surface consisting solely of triangles. Any surface in space, no matter how complex, can be approximated with a triangulated surface.

Classes defined in module trisurface

class trisurface.TriSurface(*args, **kargs)

A class representing a triangulated 3D surface.

The surface contains ntri triangles, each having 3 vertices with 3 coordinates. The surface can be initialized from one of the following:

  • a (ntri,3,3) shaped array of floats
  • a Formex with plexitude 3
  • a Mesh with plexitude 3
  • an (ncoords,3) float array of vertex coordinates and an (ntri,3) integer array of vertex numbers
  • an (ncoords,3) float array of vertex coordinates, an (nedges,2) integer array of vertex numbers, an (ntri,3) integer array of edges numbers.

Additionally, a keyword argument prop= may be specified to set property values.

areas()

area of elements

For surface element the faces’ area is returned. For volume elements the sum of the faces’areas is returned.

nedges()

Return the number of edges of the TriSurface.

nfaces()

Return the number of faces of the TriSurface.

vertices()

Return the coordinates of the nodes of the TriSurface.

shape()

Return the number of points, edges, faces of the TriSurface.

getElemEdges()

Get the faces’ edge numbers.

setCoords(coords)

Change the coords.

setElems(elems)

Change the elems.

setEdgesAndFaces(edges, faces)

Change the edges and faces.

append(S)

Merge another surface with self.

This just merges the data sets, and does not check whether the surfaces intersect or are connected! This is intended mostly for use inside higher level functions.

classmethod read(clas, fn, ftype=None)

Read a surface from file.

If no file type is specified, it is derived from the filename extension. Currently supported file types:

  • .stl (ASCII or BINARY)
  • .gts
  • .off
  • .neu (Gambit Neutral)
  • .smesh (Tetgen)
write(fname, ftype=None)

Write the surface to file.

If no filetype is given, it is deduced from the filename extension. If the filename has no extension, the ‘gts’ file type is used.

avgVertexNormals()

Compute the average normals at the vertices.

areaNormals()

Compute the area and normal vectors of the surface triangles.

The normal vectors are normalized. The area is always positive.

The values are returned and saved in the object.

facetArea()

Return the area of the surface triangles.

area()

Return the area of the surface

volume()

Return the enclosed volume of the surface.

This will only be correct if the surface is a closed manifold.

curvature(neighbours=1)

Return the curvature parameters at the nodes.

This uses the nodes that are connected to the node via a shortest path of ‘neighbours’ edges. Eight values are returned: the Gaussian and mean curvature, the shape index, the curvedness, the principal curvatures and the principal directions.

inertia()

Return inertia related quantities of the surface.

This returns the center of gravity, the principal axes of inertia, the principal moments of inertia and the inertia tensor.

surfaceType()

Check whether the TriSurface is a manifold and if it’s closed.

borderEdges()

Detect the border elements of TriSurface.

The border elements are the edges having less than 2 connected elements. Returns True where edge is on the border.

borderEdgeNrs()

Returns the numbers of the border edges.

borderNodeNrs()

Detect the border nodes of TriSurface.

The border nodes are the vertices belonging to the border edges. Returns a list of vertex numbers.

isManifold()

Check whether the TriSurface is a manifold.

A surface is a manifold if a small sphere exists that cuts the surface to a surface that can continously be deformed to an open disk.

nonManifoldEdges()

Finds edges and faces that are not Manifold.

Returns a tuple of:

  • the edges that connect 3 or more faces,
  • the faces connected to these edges.
isClosedManifold()

Check whether the TriSurface is a closed manifold.

checkBorder()

Return the border of TriSurface as a set of segments.

border()

Return the border(s) of TriSurface.

The complete border of the surface is returned as a list of plex-2 Meshes. Each Mesh constitutes a continuous part of the border.

fillBorder(method='radial')

Fill the border areas of a surface to make it closed.

Returns a list of surfaces, each of which fills a singly connected part of the border of the input surface. Adding these surfaces to the original will create a closed surface. The surfaces will have property values set above those used in the parent surface. If the surface is already closed, an empty list is returned.

There are two methods, ‘radial’ and ‘border’ corresponding with the methods of the surfaceInsideBorder.

edgeCosAngles()

Return the cos of the angles over all edges.

The surface should be a manifold (max. 2 elements per edge). Edges adjacent to only one element get cosangles = 1.0.

edgeAngles()

Return the angles over all edges (in degrees). It is the angle (0 to 180) between 2 face normals.

aspectRatio()

Return the apect ratio of the triangles of the surface.

The aspect ratio of a triangle is the ratio of the longest edge over the smallest altitude of the triangle.

Equilateral triangles have the smallest edge ratio (2 over square root 3).

smallestAltitude()

Return the smallest altitude of the triangles of the surface.

longestEdge()

Return the longest edge of the triangles of the surface.

shortestEdge()

Return the shortest edge of the triangles of the surface.

stats()

Return a text with full statistics.

distanceOfPoints(X, return_points=False)

Find the distances of points X to the TriSurface.

The distance of a point is either: - the closest perpendicular distance to the facets; - the closest perpendicular distance to the edges; - the closest distance to the vertices.

X is a (nX,3) shaped array of points. If return_points = True, a second value is returned: an array with the closest (foot)points matching X.

offset(distance=1.0)

Offset a surface with a certain distance.

All the nodes of the surface are translated over a specified distance along their normal vector.

edgeFront(startat=0, okedges=None, front_increment=1)

Generator function returning the frontal elements.

startat is an element number or list of numbers of the starting front. On first call, this function returns the starting front. Each next() call returns the next front. front_increment determines how the property increases at each frontal step. There is an extra increment +1 at each start of a new part. Thus, the start of a new part can always be detected by a front not having the property of the previous plus front_increment.

walkEdgeFront(startat=0, nsteps=-1, okedges=None, front_increment=1)

Grow a selection using a frontal method.

Starting from element startat, grow a selection nsteps times following the common edges of the triangles.

The property of each new front is augmented by front_increment.

growSelection(sel, mode='node', nsteps=1)

Grow a selection of a surface.

p is a single element number or a list of numbers. The return value is a list of element numbers obtained by growing the front nsteps times. The mode argument specifies how a single frontal step is done:

  • ‘node’ : include all elements that have a node in common,
  • ‘edge’ : include all elements that have an edge in common.
partitionByEdgeFront(okedges, firstprop=0, startat=0)

Detect different parts of the surface using a frontal method.

okedges flags the edges where the two adjacent triangles are to be in the same part of the surface. startat is a list of elements that are in the first part. The partitioning is returned as a property type array having a value corresponding to the part number. The lowest property number will be firstprop.

partitionByAngle(angle=60.0, firstprop=0, sort='number', sortedbyarea=None)

Partition the surface by splitting it at sharp edges.

The surface is partitioned in parts in which all elements can be reach without ever crossing a sharp edge angle. More precisely, any two elements that can be connected by a line not crossing an edge between two elements having their normals differ more than angle (in degrees), will belong to the same part.

The partitioning is returned as a property type array having a value corresponding to the part number. The lowest property number will be firstprop.

By default the parts are assigned property numbers in decreasing order of the number of triangles in the part. Setting the sort argument to ‘area’ will sort the parts according to decreasing area. Any other value will return the parts unsorted.

cutWithPlane(*args, **kargs)

Cut a surface with a plane or a set of planes.

Cuts the surface with one or more plane and returns either one side or both.

Parameters:

  • p,`n`: a point and normal vector defining the cutting plane. p and n can be sequences of points and vector, allowing to cut with multiple planes. Both p and n have shape (3) or (npoints,3).

The parameters are the same as in Formex.CutWithPlane(). The returned surface will have its normals fixed wherever possible.

connectedElements(target, elemlist=None)

Return the elements from list connected with target

intersectionWithPlane(p, n)

Return the intersection lines with plane (p,n).

Returns a plex-2 mesh with the line segments obtained by cutting all triangles of the surface with the plane (p,n) p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components.

The return value is a plex-2 Mesh where the line segments defining the intersection are sorted to form continuous lines. The Mesh has property numbers such that all segments forming a single continuous part have the same property value. The splitProp() method can be used to get a list of Meshes.

slice(dir=0, nplanes=20)

Intersect a surface with a sequence of planes.

A sequence of nplanes planes with normal dir is constructed at equal distances spread over the bbox of the surface.

The return value is a list of intersectionWithPlane() return values, i.e. a list of Meshes, one for every cutting plane. In each Mesh the simply connected parts are identified by property number.

smooth(method='lowpass', iterations=1, lambda_value=0.5, neighbourhood=1, alpha=0.0, beta=0.2, verbose=False)

Smooth the surface.

Returns a TriSurface which is a smoothed version of the original. Three smoothing methods are available: ‘lowpass’, ‘laplace’, and ‘gts’. The first two are built-in, the latter uses the external command gtssmooth.

Parameters:

  • method: ‘lowpass’, ‘laplace’, or ‘gts’
  • iterations: int: number of iterations
  • lambda_value: float: lambda value used in the filters

Extra parameters for ‘lowpass’ and ‘laplace’:

  • neighbourhood: int: maximum number of edges to follow to define node neighbourhood

Extra parameters for ‘laplace’:

  • alpha, beta: float: parameters for the laplace method.

Extra parameters for ‘gts’:

  • verbose: boolean: requests more verbose output of the gtssmooth command

Returns: the smoothed TriSurface

smoothLowPass(iterations=2, lambda_value=0.5, neighbours=1)

Apply a low pass smoothing to the surface.

smoothLaplaceHC(iterations=2, lambda_value=0.5, alpha=0.0, beta=0.2, neighbours=1)

Apply Laplace smoothing with shrinkage compensation to the surface.

fixNormals()

Fix the orientation of the normals.

Some surface operations may result in improperly oriented normals. This tries to reverse improperly oriented normals so that a single oriented surface is achieved. It only works on a closed surface.

In the current version, this uses the external program admesh, so this should be installed on the machine.

If the surface was a (possibly non-orientable) manifold, the result will be an orientable manifold. This is a necessary condition for the gts methods to be applicable.

check(matched=True)

Check the surface using gtscheck.

Uses gtscheck to check whether the surface is an orientable, non self-intersecting manifold.

This is a necessary condition the gts methods: split, coarsen, refine, boolean. (Additionally, the surface should be closed, wich can be checked with isClosedManifold()).

Returns a tuple of:

  • an integer return code with the value:
    • 0: the surface is an orientable, non self-intersecting manifold.
    • 2: the surface is not an orientable manifold. This may be due to misoriented normals. The fixNormals() and reverse() methods may be used to help fixing the problem in such case.
    • 3: the surface is an orientable manifold but is self-intersecting. The self intersecting triangles are returned as the second return value.
  • the intersecting triangles in the case of a return code 3, else None. If matched==True, intersecting triangles are returned as element indices of self, otherwise as a separate TriSurface object.
checkSelfIntersectionsWithTetgen(verbose=False)

check self intersections using tetgen

Returns couples of intersecting triangles

split(base, verbose=False)

Split the surface using gtssplit.

Splits the surface into connected and manifold components. This uses the external program gtssplit. The surface should be a closed orientable non-intersecting manifold. Use the check() method to find out.

This method creates a series of files with given base name, each file contains a single connected manifold.

coarsen(min_edges=None, max_cost=None, mid_vertex=False, length_cost=False, max_fold=1.0, volume_weight=0.5, boundary_weight=0.5, shape_weight=0.0, progressive=False, log=False, verbose=False)

Coarsen the surface using gtscoarsen.

Construct a coarsened version of the surface. This uses the external program gtscoarsen. The surface should be a closed orientable non-intersecting manifold. Use the check() method to find out.

Parameters:

  • min_edges: int: stops the coarsening process if the number of edges was to fall below it
  • max_cost: float: stops the coarsening process if the cost of collapsing an edge is larger
  • mid_vertex: boolean: use midvertex as replacement vertex instead of the default, which is a volume optimized point
  • length_cost: boolean: use length^2 as cost function instead of the default optimized point cost
  • max_fold: float: maximum fold angle in degrees
  • volume_weight: float: weight used for volume optimization
  • boundary_weight: float: weight used for boundary optimization
  • shape_weight: float: weight used for shape optimization
  • progressive: boolean: write progressive surface file
  • log: boolean: log the evolution of the cost
  • verbose: boolean: print statistics about the surface
refine(max_edges=None, min_cost=None, log=False, verbose=False)

Refine the surface using gtsrefine.

Construct a refined version of the surface. This uses the external program gtsrefine. The surface should be a closed orientable non-intersecting manifold. Use the check() method to find out.

Parameters:

  • max_edges: int: stop the refining process if the number of edges exceeds this value
  • min_cost: float: stop the refining process if the cost of refining an edge is smaller
  • log: boolean: log the evolution of the cost
  • verbose: boolean: print statistics about the surface
boolean(surf, op, intersection_curve=False, check=False, verbose=False)

Perform a boolean operation with another surface.

Boolean operations between surfaces are a basic operation in free surface modeling. Both surfaces should be closed orientable non-intersecting manifolds. Use the check() method to find out.

The boolean operations are set operations on the enclosed volumes: union(‘+’), difference(‘-‘) or intersection(‘*’).

Parameters:

  • intersection_curve: boolean: output an OOGL (Geomview) representation of the curve intersection of the surfaces
  • check: boolean: check that the surfaces are not self-intersecting; if one of them is, the set of self-intersecting faces is written (as a GtsSurface) on standard output
  • verbose: boolean: print statistics about the surface
report()

Create a report on the Mesh shape and size.

The report contains the number of nodes, number of elements, plexitude, element type, bbox and size.

nodeFront(startat=0, front_increment=1)

Generator function returning the frontal elements.

startat is an element number or list of numbers of the starting front. On first call, this function returns the starting front. Each next() call returns the next front.

partitionByNodeFront(firstprop=0, startat=0)

Detects different parts of the Mesh using a frontal method.

okedges flags the edges where the two adjacent elems are to be in the same part of the Mesh. startat is a list of elements that are in the first part. The partitioning is returned as a property type array having a value corresponding to the part number. The lowest property number will be firstprop.

partitionByConnection()

Detect the connected parts of a Mesh.

The Mesh is partitioned in parts in which all elements are connected. Two elements are connected if it is possible to draw a continuous (poly)line from a point in one element to a point in the other element without leaving the Mesh. The partitioning is returned as a property type array having a value corresponding to the part number. The lowest property number will be firstprop.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

splitByConnection()

Split the Mesh into connected parts.

Returns a list of Meshes that each form a connected part.

setType(eltype=None)

Set the eltype from a character string.

This function allows the user to change the element type of the Mesh. The input is a character string with the name of one of the element defined in elements.py. The function will only allow to set a type matching the plexitude of the Mesh.

This method is seldom needed, because the applications should normally set the element type at creation time.

largestByConnection()

Return the largest connected part of the Mesh.

setProp(prop=None)

Create or destroy the property array for the Mesh.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Mesh. You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Mesh.

rings(sources, nrings)

It finds the rings of elems connected to sources by node.

Sources is a list of elem indices. A list of rings is returned, from zero (equal to sources) to step. If step is -1, all rings are returned.

correctNegativeVolumes()

Modify the connectivity of negative-volume elements to make positive-volume elements.

Negative-volume elements (hex or tet with inconsistent face orientation) may appear by error during geometrical trnasformations (e.g. reflect, sweep, extrude, revolve). This function fixes those elements. Currently it only works with linear tet and hex.

scaledJacobian(scaled=True)

Returns a quality measure for volume meshes.

If scaled if False, it returns the Jacobian at the corners of each element. If scaled is True, it returns a quality metrics, being the minumum value of the scaled Jacobian in each element (at one corner, the Jacobian divided by the volume of a perfect brick). Each tet or hex element gives a value between -1 and 1. Acceptable elements have a positive scaled Jacobian. However, good quality requires a minimum of 0.2. Quadratic meshes are first converted to linear. If the mesh contain mainly negative Jacobians, it probably has negative volumes and can be fixed with the correctNegativeVolumes.

getProp()

Return the properties as a numpy array (ndarray)

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

maxProp()

Return the highest property value used, or None

propSet()

Return a list with unique property values.

copy()

Return a copy using the same data arrays

toFormex()

Convert a Mesh to a Formex.

The Formex inherits the element property numbers and eltype from the Mesh. Node property numbers however can not be translated to the Formex data model.

toSurface()

Convert a Mesh to a Surface.

If the plexitude of the mesh is 3, returns a TriSurface equivalent with the Mesh. Else, an error is raised.

centroids()

Return the centroids of all elements of the Mesh.

The centroid of an element is the point whose coordinates are the mean values of all points of the element. The return value is a Coords object with nelems points.

getCoords()

Get the coords data.

Returns the full array of coordinates stored in the Mesh object. Note that this may contain points that are not used in the mesh. compact() will remove the unused points.

getElems()

Get the elems data.

Returns the element connectivity data as stored in the object.

getLowerEntities(level=-1, unique=False)

Get the entities of a lower dimensionality.

If the element type is defined in the elements module, this returns a Connectivity table with the entities of a lower dimensionality. The full list of entities with increasing dimensionality 0,1,2,3 is:

['points', 'edges', 'faces', 'cells' ]

If level is negative, the dimensionality returned is relative to that of the caller. If it is positive, it is taken absolute. Thus, for a Mesh with a 3D element type, getLowerEntities(-1) returns the faces, while for a 2D element type, it returns the edges. For both meshes however, getLowerEntities(+1) returns the edges.

By default, all entities for all elements are returned and common entities will appear multiple times. Specifying unique=True will return only the unique ones.

The return value may be an empty table, if the element type does not have the requested entities (e.g. the ‘point’ type). If the eltype is not defined, or the requested entity level is outside the range 0..3, the return value is None.

getNodes()

Return the set of unique node numbers in the Mesh.

This returns only the node numbers that are effectively used in the connectivity table. For a compacted Mesh, it is equal to `arange(self.nelems)`. This function also stores the result internally so that future requests can return it without the need for computing it again.

getPoints()

Return the nodal coordinates of the Mesh.

This returns only those points that are effectively used in the connectivity table. For a compacted Mesh, it is equal to the coords attribute.

getEdges()

Return the unique edges of all the elements in the Mesh.

This is a convenient function to create a table with the element edges. It is equivalent to `self.getLowerEntities(1,unique=True)`, but this also stores the result internally so that future requests can return it without the need for computing it again.

getFaces()

Return the unique faces of all the elements in the Mesh.

This is a convenient function to create a table with the element faces. It is equivalent to `self.getLowerEntities(2,unique=True)`, but this also stores the result internally so that future requests can return it without the need for computing it again.

getCells()

Return the cells of the elements.

This is a convenient function to create a table with the element cells. It is equivalent to `self.getLowerEntities(3,unique=True)`, but this also stores the result internally so that future requests can return it without the need for computing it again.

getFreeEntities(level=-1, return_indices=False)

Return the border of the Mesh.

Returns a Connectivity table with the free entities of the specified level of the Mesh. Free entities are entities that are only connected with a single element.

If return_indices==True, also returns an (nentities,2) index for inverse lookup of the higher entity (column 0) and its local lower entity number (column 1).

getFreeEntitiesMesh(level=-1, compact=True)

Return a Mesh with lower entities.

Returns a Mesh representing the lower entities of the specified level. If the Mesh has property numbers, the lower entities inherit the property of the element to which they belong.

By default, the resulting Mesh is compacted. Compaction can be switched off by setting compact=False.

getBorder(return_indices=False)

Return the border of the Mesh.

This returns a Connectivity table with the border of the Mesh. The border entities are of a lower hierarchical level than the mesh itself. These entities become part of the border if they are connected to only one element.

If return_indices==True, it returns also an (nborder,2) index for inverse lookup of the higher entity (column 0) and its local border part number (column 1).

This is a convenient shorthand for

self.getFreeEntities(level=-1,return_indices=return_indices)
getBorderMesh(compact=True)

Return a Mesh with the border elements.

The returned Mesh is of the next lower hierarchical level and contains all the free entitites of that level. If the Mesh has property numbers, the border elements inherit the property of the element to which they belong.

By default, the resulting Mesh is compacted. Compaction can be switched off by setting compact=False.

This is a convenient shorthand for

self.getFreeEntitiesMesh(level=-1,compact=compact)
getFreeEdgesMesh(compact=True)

Return a Mesh with the free edge elements.

The returned Mesh is of the hierarchical level 1 (no mather what the level of the parent Mesh is) and contains all the free entitites of that level. If the Mesh has property numbers, the border elements inherit the property of the element to which they belong.

By default, the resulting Mesh is compacted. Compaction can be switched off by setting compact=False.

This is a convenient shorthand for

self.getFreeEntitiesMesh(level=1,compact=compact)
reverse()

Return a Mesh where all elements have been reversed.

Reversing an element has the following meaning:

  • for 1D elements: reverse the traversal direction,
  • for 2D elements: reverse the direction of the positive normal,
  • for 3D elements: reverse inside and outside directions of the element’s border surface

The reflect() method by default calls this method to undo the element reversal caused by the reflection operation.

reflect(dir=0, pos=0.0, reverse=True, **kargs)

Reflect the coordinates in one of the coordinate directions.

Parameters:

  • dir: int: direction of the reflection (default 0)
  • pos: float: offset of the mirror plane from origin (default 0.0)
  • reverse: boolean: if True, the Mesh.reverse() method is called after the reflection to undo the element reversal caused by the reflection of its coordinates. This will in most cases have the desired effect. If not however, the user can set this to False to skip the element reversal.
nodeConnections()

Find and store the elems connected to nodes.

nNodeConnected()

Find the number of elems connected to nodes.

edgeConnections()

Find and store the elems connected to edges.

nEdgeConnected()

Find the number of elems connected to edges.

nodeAdjacency()

Find the elems adjacent to each elem via one or more nodes.

nNodeAdjacent()

Find the number of elems which are adjacent by node to each elem.

edgeAdjacency()

Find the elems adjacent to elems via an edge.

nEdgeAdjacent()

Find the number of adjacent elems.

fuse(**kargs)

Fuse the nodes of a Meshes.

All nodes that are within the tolerance limits of each other are merged into a single node.

The merging operation can be tuned by specifying extra arguments that will be passed to Coords:fuse().

matchCoords(mesh, **kargs)

Match nodes of Mesh with nodes of self.

This is a convenience function equivalent to:

self.coords.match(mesh.coords,**kargs)

See also Coords.match()

matchCentroids(mesh, **kargs)

Match elems of Mesh with elems of self.

self and Mesh are same eltype meshes and are both without duplicates.

Elems are matched by their centroids.

matchFaces(mesh)

Match faces of mesh with faces of self.

self and Mesh can be same eltype meshes or different eltype but of the same hierarchical type (i.e. hex8-quad4 or tet4 - tri3) and are both without duplicates.

Returns the indices array of the elems of self that matches the faces of mesh

compact()

Remove unconnected nodes and renumber the mesh.

Returns a mesh where all nodes that are not used in any element have been removed, and the nodes are renumbered to a compacter scheme.

select(selected, compact=True)

Return a Mesh only holding the selected elements.

  • selected: an object that can be used as an index in the elems array, e.g. a list of (integer) element numbers, or a boolean array with the same length as the elems array.
  • compact: boolean. If True (default), the returned Mesh will be compacted, i.e. the unused nodes are removed and the nodes are renumbered from zero. If False, returns the node set and numbers unchanged.

Returns a Mesh (or subclass) with only the selected elements.

See cselect for the complementary operation.

cselect(selected, compact=True)

Return a mesh without the selected elements.

  • selected: an object that can be used as an index in the elems array, e.g. a list of (integer) element numbers, or a boolean array with the same length as the elems array.
  • compact: boolean. If True (default), the returned Mesh will be compacted, i.e. the unused nodes are removed and the nodes are renumbered from zero. If False, returns the node set and numbers unchanged.

Returns a Mesh with all but the selected elements.

This is the complimentary operation of select.

avgNodes(nodsel, wts=None)

Create average nodes from the existing nodes of a mesh.

nodsel is a local node selector as in selectNodes() Returns the (weighted) average coordinates of the points in the selector as (nelems*nnod,3) array of coordinates, where nnod is the length of the node selector. wts is a 1-D array of weights to be attributed to the points. Its length should be equal to that of nodsel.

meanNodes(nodsel)

Create nodes from the existing nodes of a mesh.

nodsel is a local node selector as in selectNodes() Returns the mean coordinates of the points in the selector as (nelems*nnod,3) array of coordinates, where nnod is the length of the node selector.

addNodes(newcoords, eltype=None)

Add new nodes to elements.

newcoords is an (nelems,nnod,3) or`(nelems*nnod,3)` array of coordinates. Each element gets exactly nnod extra nodes from this array. The result is a Mesh with plexitude self.nplex() + nnod.

addMeanNodes(nodsel, eltype=None)

Add new nodes to elements by averaging existing ones.

nodsel is a local node selector as in selectNodes() Returns a Mesh where the mean coordinates of the points in the selector are added to each element, thus increasing the plexitude by the length of the items in the selector. The new element type should be set to correct value.

selectNodes(nodsel, eltype=None)

Return a mesh with subsets of the original nodes.

nodsel is an object that can be converted to a 1-dim or 2-dim array. Examples are a tuple of local node numbers, or a list of such tuples all having the same length. Each row of nodsel holds a list of local node numbers that should be retained in the new connectivity table.

withProp(val)

Return a Mesh which holds only the elements with property val.

val is either a single integer, or a list/array of integers. The return value is a Mesh holding all the elements that have the property val, resp. one of the values in val. The returned Mesh inherits the matching properties.

If the Mesh has no properties, a copy with all elements is returned.

withoutProp(val)

Return a Mesh without the elements with property val.

This is the complementary method of Mesh.withProp(). val is either a single integer, or a list/array of integers. The return value is a Mesh holding all the elements that do not have the property val, resp. one of the values in val. The returned Mesh inherits the matching properties.

If the Mesh has no properties, a copy with all elements is returned.

splitProp()

Partition a Mesh according to its propery values.

Returns a dict with the property values as keys and the corresponding partitions as values. Each value is a Mesh instance. It the Mesh has no props, an empty dict is returned.

splitRandom(n, compact=True)

Split a mesh in n parts, distributing the elements randomly.

Returns a list of n Mesh objects, constituting together the same Mesh as the original. The elements are randomly distributed over the subMeshes.

By default, the Meshes are compacted. Compaction may be switched off for efficiency reasons.

convert(totype, fuse=False)

Convert a Mesh to another element type.

Converting a Mesh from one element type to another can only be done if both element types are of the same dimensionality. Thus, 3D elements can only be converted to 3D elements.

The conversion is done by splitting the elements in smaller parts and/or by adding new nodes to the elements.

Not all conversions between elements of the same dimensionality are possible. The possible conversion strategies are implemented in a table. New strategies may be added however.

The return value is a Mesh of the requested element type, representing the same geometry (possibly approximatively) as the original mesh.

If the requested conversion is not implemented, an error is raised.

..warning: Conversion strategies that add new nodes may produce
double nodes at the common border of elements. The fuse() method can be used to merge such coincident nodes. Specifying fuse=True will also enforce the fusing. This option become the default in future.
convertRandom(choices)

Convert choosing randomly between choices

reduceDegenerate(eltype=None)

Reduce degenerate elements to lower plexitude elements.

This will try to reduce the degenerate elements of the mesh to elements of a lower plexitude. If a target element type is given, only the matching reduce scheme is tried. Else, all the target element types for which a reduce scheme from the Mesh eltype is available, will be tried.

The result is a list of Meshes of which the last one contains the elements that could not be reduced and may be empty. Property numbers propagate to the children.

splitDegenerate(autofix=True)

Split a Mesh in degenerate and non-degenerate elements.

If autofix is True, the degenerate elements will be tested against known degeneration patterns, and the matching elements will be transformed to non-degenerate elements of a lower plexitude.

The return value is a list of Meshes. The first holds the non-degenerate elements of the original Mesh. The last holds the remaining degenerate elements. The intermediate Meshes, if any, hold elements of a lower plexitude than the original. These may still contain degenerate elements.

removeDegenerate(eltype=None)

Remove the degenerate elements from a Mesh.

Returns a Mesh with all degenerate elements removed.

removeDuplicate(permutations=True)

Remove the duplicate elements from a Mesh.

Duplicate elements are elements that consist of the same nodes, by default in no particular order. Setting permutations=False will only consider elements with the same nodes in the same order as duplicates.

Returns a Mesh with all duplicate elements removed.

renumber(order='elems')

Renumber the nodes of a Mesh in the specified order.

order is an index with length equal to the number of nodes. The index specifies the node number that should come at this position. Thus, the order values are the old node numbers on the new node number positions.

order can also be a predefined value that will generate the node index automatically:

  • ‘elems’: the nodes are number in order of their appearance in the Mesh connectivity.
renumberElems(order='nodes')

Renumber the elements of a Mesh.

Parameters:

  • order: either a 1-D integer array with a permutation of arange(self.nelems()), specifying the requested order, or one of the following predefined strings:
    • ‘nodes’: order the elements in increasing node number order.
    • ‘random’: number the elements in a random order.
    • ‘reverse’: number the elements in.
Returns:
A Mesh equivalent with self but with the elements ordered as specified.

See also: Connectivity.reorder()

connect(coordslist, div=1, degree=1, loop=False, eltype=None)

Connect a sequence of toplogically congruent Meshes into a hypermesh.

Parameters:

  • coordslist: either a list of Coords instances, all having the same shape as self.coords, or a single Mesh instance whose coords attribute has the same shape.

    If it is a list of Coords, consider a list of Meshes obtained by combining each Coords object with the connectivity table, element type and property numbers of the current Mesh. The return value then is the hypermesh obtained by connecting each consecutive slice of (degree+1) of these meshes. The hypermesh has a dimensionality that is one higher than the original Mesh (i.e. points become lines, lines become surfaces, surfaces become volumes). The resulting elements will be of the given degree in the direction of the connection. Notice that the coords of the current Mesh are not used, unless these coords are explicitely included into the specified coordslist. In many cases self.coords will be the first item in coordslist, but it could occur anywhere in the list or even not at all. The number of Coords items in the list should be a multiple of degree plus 1.

    Specifying a single Mesh instead of a list of Coords is just a convenience for the often occurring situation of connecting a Mesh (self) with another one (mesh) having the same connectivity: in this case the list of Coords will automatically be set to [ self.coords, mesh.coords ]. The degree should be 1 in this case.

  • degree: degree of the connection. Currently only degree 1 and 2 are supported.

    • If degree is 1, every Coords from the coordslist is connected with hyperelements of a linear degree in the connection direction.
    • If degree is 2, quadratic hyperelements are created from one Coords item and the next two in the list. Note that all Coords items should contain the same number of nodes, even for higher order elements where the intermediate planes contain less nodes.
  • loop: if True, the connections with loop around the list and connect back to the first. This is accomplished by adding the first Coords item back at the end of the list.

  • div: Either an integer, or a sequence of float numbers (usually in the range ]0.0..1.0]). With this parameter the generated elements can be further subdivided along the connection direction. If an int is given, the connected elements will be divided into this number of elements along the connection direction. If a sequence of float numbers is given, the numbers specify the relative distance along the connection direction where the elements should end. If the last value in the sequence is not equal to 1.0, there will be a gap between the consecutive connections.

  • eltype: the element type of the constructed hypermesh. Normally, this is set automatically from the base element type and the connection degree. If a different element type is specified, a final conversion to the requested element type is attempted.

extrude(n, step=1.0, dir=0, degree=1, eltype=None)

Extrude a Mesh in one of the axes directions.

Returns a new Mesh obtained by extruding the given Mesh over n steps of length step in direction of axis dir.

revolve(n, axis=0, angle=360.0, around=None, loop=False, eltype=None)

Revolve a Mesh around an axis.

Returns a new Mesh obtained by revolving the given Mesh over an angle around an axis in n steps, while extruding the mesh from one step to the next. This extrudes points into lines, lines into surfaces and surfaces into volumes.

sweep(path, eltype=None, **kargs)

Sweep a mesh along a path, creating an extrusion

Returns a new Mesh obtained by sweeping the given Mesh over a path. The returned Mesh has double plexitude of the original. The operation is similar to the extrude() method, but the path can be any 3D curve.

This function is usually used to extrude points into lines, lines into surfaces and surfaces into volumes. By default it will try to fix the connectivity ordering where appropriate. If autofix is switched off, the connectivities are merely stacked, and the user may have to fix it himself.

Currently, this function produces the correct element type, but the geometry .

classmethod concatenate(clas, meshes, **kargs)

Concatenate a list of meshes of the same plexitude and eltype

Merging of the nodes can be tuned by specifying extra arguments that will be passed to Coords:fuse().

This is a class method, and should be invoked as follows:

Mesh.concatenate([mesh0,mesh1,mesh2])
test(nodes='all', dir=0, min=None, max=None, atol=0.0)

Flag elements having nodal coordinates between min and max.

This function is very convenient in clipping a Mesh in a specified direction. It returns a 1D integer array flagging (with a value 1 or True) the elements having nodal coordinates in the required range. Use where(result) to get a list of element numbers passing the test. Or directly use clip() or cclip() to create the clipped Mesh

The test plane can be defined in two ways, depending on the value of dir. If dir == 0, 1 or 2, it specifies a global axis and min and max are the minimum and maximum values for the coordinates along that axis. Default is the 0 (or x) direction.

Else, dir should be compaitble with a (3,) shaped array and specifies the direction of the normal on the planes. In this case, min and max are points and should also evaluate to (3,) shaped arrays.

nodes specifies which nodes are taken into account in the comparisons. It should be one of the following:

  • a single (integer) point number (< the number of points in the Formex)
  • a list of point numbers
  • one of the special strings: ‘all’, ‘any’, ‘none’

The default (‘all’) will flag all the elements that have all their nodes between the planes x=min and x=max, i.e. the elements that fall completely between these planes. One of the two clipping planes may be left unspecified.

clip(t, compact=False)

Return a Mesh with all the elements where t>0.

t should be a 1-D integer array with length equal to the number of elements of the Mesh. The resulting Mesh will contain all elements where t > 0.

cclip(t, compact=False)

This is the complement of clip, returning a Mesh where t<=0.

clipAtPlane(p, n, nodes='any', side='+')

Return the Mesh clipped at plane (p,n).

This is a convenience function returning the part of the Mesh at one side of the plane (p,n)

volumes()

Return the signed volume of all the mesh elements

For a ‘tet4’ tetraeder Mesh, the volume of the elements is calculated as 1/3 * surface of base * height.

For other Mesh types the volumes are calculated by first splitting the elements into tetraeder elements.

The return value is an array of float values with length equal to the number of elements. If the Mesh conversion to tetraeder does not succeed, the return value is None.

Functions defined in module trisurface

trisurface.stlConvert(stlname, outname=None, options='-d')

Transform an .stl file to .off or .gts format.

If outname is given, it is either ‘.off’ or ‘.gts’ or a filename ending on one of these extensions. If it is only an extension, the stlname will be used with extension changed.

If the outname file exists and its mtime is more recent than the stlname, the outname file is considered uptodate and the conversion programwill not be run.

The conversion program will be choosen depending on the extension. This uses the external commands ‘admesh’ or ‘stl2gts’.

The return value is a tuple of the output file name, the conversion program exit code (0 if succesful) and the stdout of the conversion program (or a ‘file is already uptodate’ message).

trisurface.read_gts(fn)

Read a GTS surface mesh.

Return a coords,edges,faces tuple.

trisurface.read_off(fn)

Read an OFF surface mesh.

The mesh should consist of only triangles! Returns a nodes,elems tuple.

trisurface.read_stl(fn, intermediate=None)

Read a surface from .stl file.

This is done by first coverting the .stl to .gts or .off format. The name of the intermediate file may be specified. If not, it will be generated by changing the extension of fn to ‘.gts’ or ‘.off’ depending on the setting of the ‘surface/stlread’ config setting.

Return a coords,edges,faces or a coords,elems tuple, depending on the intermediate format.

trisurface.read_gambit_neutral(fn)

Read a triangular surface mesh in Gambit neutral format.

The .neu file nodes are numbered from 1! Returns a nodes,elems tuple.

trisurface.write_stla(f, x)

Export an x[n,3,3] float array as an ascii .stl file.

trisurface.write_smesh(fn, nodes, elems)

Write a tetgen surface model to .node and .smesh files.

The provided file name is the .node or the .smesh filename.

trisurface.surface_volume(x, pt=None)

Return the volume inside a 3-plex Formex.

  • x: an (ntri,3,3) shaped float array, representing ntri triangles.
  • pt: a point in space. If unspecified, it is taken equal to the center() of the coordinates x.

Returns an (ntri) shaped array with the volume of the tetraeders formed by the triangles and the point pt. If x represents a closed surface, the sum of this array will represent the volume inside the surface.

trisurface.curvature(coords, elems, edges, neighbours=1)

Calculate curvature parameters at the nodes.

Algorithms based on Dong and Wang 2005; Koenderink and Van Doorn 1992. This uses the nodes that are connected to the node via a shortest path of ‘neighbours’ edges. Eight values are returned: the Gaussian and mean curvature, the shape index, the curvedness, the principal curvatures and the principal directions.

trisurface.fillBorder(border, method='radial')

Create a surface inside a given closed border line.

The border line is a closed polygonal line and can be specified as one of the following:

  • a closed PolyLine,
  • a 2-plex Mesh, with a Connectivity table such that the elements in order form a closed polyline,
  • a simple Coords specifying the subsequent vertices of the polygonal border line.

The return value is a TriSurface filling the hole inside the border.

There are currently two fill methods available:

  • ‘radial’: this method adds a central point and connects all border segments with the center to create triangles.
  • ‘border’: this method creates subsequent triangles by connecting the endpoints of two consecutive border segments and thus works its way inwards until the hole is closed. Triangles are created at the line segments that form the smallest angle.

The ‘radial’ method produces nice results if the border is relative smooth, nearly convex and nearly planar. It adds an extra point though, which may be unwanted. On irregular 3D borders there is a high change that the result contains intersecting triangles.

This ‘border’ method is slower on large borders, does not introduce any new point and has a better chance of avoiding intersecting triangles on irregular 3D borders.

The resulting surface can be checked for intersecting triangles by the check() method.

..note :: Because the ‘border’ does not create any new points, the
returned surface will use the same point coordinate array as the input object.
trisurface.read_error(cnt, line)

Raise an error on reading the stl file.

trisurface.degenerate(area, norm)

Return a list of the degenerate faces according to area and normals.

A face is degenerate if its surface is less or equal to zero or the normal has a nan.

trisurface.read_stla(fn, dtype=<type 'numpy.float32'>, large=False, guess=True)

Read an ascii .stl file into an [n,3,3] float array.

If the .stl is large, read_ascii_large() is recommended, as it is a lot faster.

trisurface.read_ascii_large(fn, dtype=<type 'numpy.float32'>)

Read an ascii .stl file into an [n,3,3] float array.

This is an alternative for read_ascii, which is a lot faster on large STL models. It requires the ‘awk’ command though, so is probably only useful on Linux/UNIX. It works by first transforming the input file to a .nodes file and then reading it through numpy’s fromfile() function.

trisurface.off_to_tet(fn)

Transform an .off model to tetgen (.node/.smesh) format.

trisurface.find_row(mat, row, nmatch=None)

Find all rows in matrix matching given row.

trisurface.find_nodes(nodes, coords)

Find nodes with given coordinates in a node set.

nodes is a (nnodes,3) float array of coordinates. coords is a (npts,3) float array of coordinates.

Returns a (n,) integer array with ALL the node numbers matching EXACTLY ALL the coordinates of ANY of the given points.

trisurface.find_first_nodes(nodes, coords)

Find nodes with given coordinates in a node set.

nodes is a (nnodes,3) float array of coordinates. coords is a (npts,3) float array of coordinates.

Returns a (n,) integer array with THE FIRST node number matching EXACTLY ALL the coordinates of EACH of the given points.

trisurface.find_triangles(elems, triangles)

Find triangles with given node numbers in a surface mesh.

elems is a (nelems,3) integer array of triangles. triangles is a (ntri,3) integer array of triangles to find.

Returns a (ntri,) integer array with the triangles numbers.

trisurface.remove_triangles(elems, remove)

Remove triangles from a surface mesh.

elems is a (nelems,3) integer array of triangles. remove is a (nremove,3) integer array of triangles to remove.

Returns a (nelems-nremove,3) integer array with the triangles of nelems where the triangles of remove have been removed.

trisurface.Rectangle(nx, ny)

Create a plane rectangular surface consisting of a nx,ny grid.

trisurface.Cube()

Create the surface of a cube

Returns a TriSurface representing the surface of a unit cube. Each face of the cube is represented by two triangles.

trisurface.Sphere(level=4, verbose=False, filename=None)

Create a spherical surface by calling the gtssphere command.

If a filename is given, it is stored under that name, else a temporary file is created. Beware: this may take a lot of time if level is 8 or higher.

pyformex-0.8.6/pyformex/doc/html/ref/turtle.html0000644000211500021150000003347511705104255021611 0ustar benebene00000000000000 47. turtle — Turtle graphics for pyFormex — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

46. lima — Lindenmayer Systems

Next topic

48. dxf — Read/write geometry in DXF format.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

47. turtle — Turtle graphics for pyFormex

This module was mainly aimed at the drawing of Lindenmayer products (see plugins.lima and the Lima example).

The idea is that a turtle can be moved in 2D from one position to another, thereby creating a line between start and endpoint or not.

The current state of the turtle is defined by

  • pos: the position as a 2D coordinate pair (x,y),
  • angle: the moving direction as an angle (in degrees) with the x-axis,
  • step: the speed, as a discrete step size.

The start conditions are: pos=(0,0), step=1., angle=0.

The followin example turtle script creates a unit square:

fd();ro(90);fd();ro(90);fd();ro(90);fd()

Classes defined in module turtle

Functions defined in module turtle

turtle.sind(arg)

Return the sine of an angle in degrees.

turtle.cosd(arg)

Return the cosine of an angle in degrees.

turtle.reset()

Reset the turtle graphics engine to start conditions.

This resets the turtle’s state to the starting conditions pos=(0,0), step=1., angle=0., removes everything from the state save stack and empties the resulting path.

turtle.push()

Save the current state of the turtle.

The turtle state includes its position, step and angle.

turtle.pop()

Restore the turtle state to the last saved state.

turtle.fd(d=None, connect=True)

Move forward over a step d, with or without drawing.

The direction is the current direction. If d is not given, the step size is the current step.

By default, the new position is connected to the previous with a straight line segment.

turtle.mv(d=None)

Move over step d without drawing.

turtle.ro(a)

Rotate over angle a. The new direction is incremented with a

turtle.go(p)

Go to position p (without drawing).

While the mv method performs a relative move, this is an absolute move. p is a tuple of (x,y) values.

turtle.st(d)

Set the step size.

turtle.an(a)

Set the angle

turtle.play(scr, glob=None)

Play all the commands in the script scr

The script is a string of turtle commands, where each command is ended with a semicolon (‘;’).

If a dict glob is specified, it will be update with the turtle module’s globals() after each turtle command.

pyformex-0.8.6/pyformex/doc/html/ref/geomtools.html0000644000211500021150000012233011705104253022265 0ustar benebene00000000000000 33. geomtools — Basic geometrical operations. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

32. trisurface — Operations on triangulated surfaces.

Next topic

34. nurbs — Using NURBS in pyFormex.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

33. geomtools — Basic geometrical operations.

This module defines some basic operations on simple geometrical entities such as lines, triangles, circles, planes.

Classes defined in module geomtools

Functions defined in module geomtools

geomtools.areaNormals(x)

Compute the area and normal vectors of a collection of triangles.

x is an (ntri,3,3) array of coordinates.

Returns a tuple of areas,normals. The normal vectors are normalized. The area is always positive.

geomtools.polygonArea(x, project=None)

Compute area inside a polygon.

Parameters:

  • x: (nplex,3) Coords array representing the vertices of a (possibly nonplanar) polygon.
  • project: (3,) Coords array representing a unit direction vector.

Returns: a single float value with the area inside the polygon. If a direction vector is given, the area projected in that direction is returned.

Note that if the polygon is nonplanar and no direction is given, the area inside the polygon is not well defined.

geomtools.polygonNormals(x)

Compute normals in all points of polygons in x.

x is an (nel,nplex,3) coordinate array representing nel (possibly nonplanar) polygons.

The return value is an (nel,nplex,3) array with the unit normals on the two edges ending in each point.

geomtools.triangleInCircle(x)

Compute the incircles of the triangles x

The incircle of a triangle is the largest circle that can be inscribed in the triangle.

x is a Coords array with shape (ntri,3,3) representing ntri triangles.

Returns a tuple r,C,n with the radii, Center and unit normals of the incircles.

geomtools.triangleCircumCircle(x, bounding=False)

Compute the circumcircles of the triangles x

x is a Coords array with shape (ntri,3,3) representing ntri triangles.

Returns a tuple r,C,n with the radii, Center and unit normals of the circles going through the vertices of each triangle.

If bounding=True, this returns the triangle bounding circle.

geomtools.triangleBoundingCircle(x)

Compute the bounding circles of the triangles x

The bounding circle is the smallest circle in the plane of the triangle such that all vertices of the triangle are on or inside the circle. If the triangle is acute, this is equivalent to the triangle’s circumcircle. It the triangle is obtuse, the longest edge is the diameter of the bounding circle.

x is a Coords array with shape (ntri,3,3) representing ntri triangles.

Returns a tuple r,C,n with the radii, Center and unit normals of the bounding circles.

geomtools.triangleObtuse(x)

Checks for obtuse triangles

x is a Coords array with shape (ntri,3,3) representing ntri triangles.

Returns an (ntri) array of True/False values indicating whether the triangles are obtuse.

geomtools.lineIntersection(P1, D1, P2, D2)

Finds the intersection of 2 coplanar lines.

The lines (P1,D1) and (P2,D2) are defined by a point and a direction vector. Let a and b be unit vectors along the lines, and c = P2-P1, let ld and d be the length and the unit vector of the cross product a*b, the intersection point X is then given by X = 0.5(P1+P2+sa*a+sb*b) where sa = det([c,b,d])/ld and sb = det([c,a,d])/ld

geomtools.displaceLines(A, N, C, d)

Move all lines (A,N) over a distance a in the direction of point C.

A,N are arrays with points and directions defining the lines. C is a point. d is a scalar or a list of scalars. All line elements of F are translated in the plane (line,C) over a distance d in the direction of the point C. Returns a new set of lines (A,N).

geomtools.segmentOrientation(vertices, vertices2=None, point=None)

Determine the orientation of a set of line segments.

vertices and vertices2 are matching sets of points. point is a single point. All arguments are Coords objects.

Line segments run between corresponding points of vertices and vertices2. If vertices2 is None, it is obtained by rolling the vertices one position foreward, thus corresponding to a closed polygon through the vertices). If point is None, it is taken as the center of vertices.

The orientation algorithm checks whether the line segments turn positively around the point.

Returns an array with +1/-1 for positive/negative oriented segments.

geomtools.rotationAngle(A, B, m=None, angle_spec=0.017453292519943295)

Return rotation angles and vectors for rotations of A to B.

A and B are (n,3) shaped arrays where each line represents a vector. This function computes the rotation from each vector of A to the corresponding vector of B. If m is None, the return value is a tuple of an (n,) shaped array with rotation angles (by default in degrees) and an (n,3) shaped array with unit vectors along the rotation axis. If m is a (n,3) shaped array with vectors along the rotation axis, the return value is a (n,) shaped array with rotation angles. Specify angle_spec=Rad to get the angles in radians.

geomtools.anyPerpendicularVector(A)

Return arbitrary vectors perpendicular to vectors of A.

A is a (n,3) shaped array of vectors. The return value is a (n,3) shaped array of perpendicular vectors.

The returned vector is always a vector in the x,y plane. If the original is the z-axis, the result is the x-axis.

geomtools.perpendicularVector(A, B)

Return vectors perpendicular on both A and B.

geomtools.projectionVOV(A, B)

Return the projection of vector of A on vector of B.

geomtools.projectionVOP(A, n)

Return the projection of vector of A on plane of B.

geomtools.pointsAtLines(q, m, t)

Return the points of lines (q,m) at parameter values t.

Parameters:

  • q,`m`: (...,3) shaped arrays of points and vectors, defining a single line or a set of lines.
  • t: array of parameter values, broadcast compatible with q and m.

Returns: An array with the points at parameter values t.

geomtools.pointsAtSegments(S, t)

Return the points of line segments S at parameter values t.

Parameters:

  • S: (...,2,3) shaped array, defining a single line segment or a set of line segments.
  • t: array of parameter values, broadcast compatible with S.

Returns: An array with the points at parameter values t.

geomtools.intersectionTimesLWL(q1, m1, q2, m2, mode='all')

Return the intersection of lines (q1,m1) and lines (q2,m2)

with the perpendiculars between them.

Parameters:

  • qi,`mi` (i=1...2): (nqi,3) shaped arrays of points and vectors (mode=all) or broadcast compatible arrays (mode=pair), defining a single line or a set of lines.
  • mode: all to calculate the intersection of each line (q1,m1) with all lines (q2,m2) or pair for pairwise intersections.

Returns: A tuple of (nq1,nq2) shaped (mode=all) arrays of parameter values t1 and t2, such that the intersection points are given by q1+t1*m1 and q2+t2*m2.

geomtools.intersectionPointsLWL(q1, m1, q2, m2, mode='all')

Return the intersection points of lines (q1,m1) and lines (q2,m2)

with the perpendiculars between them.

This is like intersectionTimesLWL but returns a tuple of (nq1,nq2,3) shaped (mode=all) arrays of intersection points instead of the parameter values.

geomtools.intersectionTimesLWP(q, m, p, n, mode='all')

Return the intersection of lines (q,m) with planes (p,n).

Parameters:

  • q,`m`: (nq,3) shaped arrays of points and vectors (mode=all) or broadcast compatible arrays (mode=pair), defining a single line or a set of lines.
  • p,`n`: (np,3) shaped arrays of points and normals (mode=all) or broadcast compatible arrays (mode=pair), defining a single plane or a set of planes.
  • mode: all to calculate the intersection of each line (q,m) with all planes (p,n) or pair for pairwise intersections.

Returns: A (nq,np) shaped (mode=all) array of parameter values t, such that the intersection points are given by q+t*m.

geomtools.intersectionPointsLWP(q, m, p, n, mode='all')

Return the intersection points of lines (q,m) with planes (p,n).

This is like intersectionTimesLWP but returns a (nq,np,3) shaped (mode=all) array of intersection points instead of the parameter values.

geomtools.intersectionTimesSWP(S, p, n, mode='all')

Return the intersection of line segments S with planes (p,n).

Parameters:

  • S: (nS,2,3) shaped array (mode=all) or broadcast compatible array (mode=pair), defining a single line segment or a set of line segments.
  • p,`n`: (np,3) shaped arrays of points and normals (mode=all) or broadcast compatible arrays (mode=pair), defining a single plane or a set of planes.
  • mode: all to calculate the intersection of each line segment S with all planes (p,n) or pair for pairwise intersections.

Returns: A (nS,np) shaped (mode=all) array of parameter values t, such that the intersection points are given by (1-t)*S[...,0,:] + t*S[...,1,:].

This function is comparable to intersectionTimesLWP, but ensures that parameter values 0<=t<=1 are points inside the line segments.

geomtools.intersectionPointsSWP(S, p, n, mode='all', return_all=False)

Return the intersection points of line segments S with planes (p,n).

Parameters:

  • S: (nS,2,3) shaped array, defining a single line segment or a set of line segments.
  • p,`n`: (np,3) shaped arrays of points and normals, defining a single plane or a set of planes.
  • mode: all to calculate the intersection of each line segment S with all planes (p,n) or pair for pairwise intersections.
  • return_all: if True, all intersection points of the lines along the segments are returned. Default is to return only the points that lie on the segments.

Returns: if return_all==True, a (nS,np,3) shaped (mode=all) array of intersection points, else, a tuple of intersection points with shape (n,3) and line and plane indices with shape (n), where n <= nS*np.

geomtools.intersectionTimesLWT(q, m, F, mode='all')

Return the intersection of lines (q,m) with triangles F.

Parameters:

  • q,`m`: (nq,3) shaped arrays of points and vectors (mode=all) or broadcast compatible arrays (mode=pair), defining a single line or a set of lines.
  • F: (nF,3,3) shaped array (mode=all) or broadcast compatible array (mode=pair), defining a single triangle or a set of triangles.
  • mode: all to calculate the intersection of each line (q,m) with all triangles F or pair for pairwise intersections.

Returns: A (nq,nF) shaped (mode=all) array of parameter values t, such that the intersection points are given q+tm.

geomtools.intersectionPointsLWT(q, m, F, mode='all', return_all=False)

Return the intersection points of lines (q,m) with triangles F.

Parameters:

  • q,`m`: (nq,3) shaped arrays of points and vectors, defining a single line or a set of lines.
  • F: (nF,3,3) shaped array, defining a single triangle or a set of triangles.
  • mode: all to calculate the intersection points of each line (q,m) with all triangles F or pair for pairwise intersections.
  • return_all: if True, all intersection points are returned. Default is to return only the points that lie inside the triangles.

Returns: if return_all==True, a (nq,nF,3) shaped (mode=all) array of intersection points, else, a tuple of intersection points with shape (n,3) and line and plane indices with shape (n), where n <= nq*nF.

geomtools.intersectionTimesSWT(S, F, mode='all')

Return the intersection of lines segments S with triangles F.

Parameters:

  • S: (nS,2,3) shaped array (mode=all) or broadcast compatible array (mode=pair), defining a single line segment or a set of line segments.
  • F: (nF,3,3) shaped array (mode=all) or broadcast compatible array (mode=pair), defining a single triangle or a set of triangles.
  • mode: all to calculate the intersection of each line segment S with all triangles F or pair for pairwise intersections.

Returns: A (nS,nF) shaped (mode=all) array of parameter values t, such that the intersection points are given by (1-t)*S[...,0,:] + t*S[...,1,:].

geomtools.intersectionPointsSWT(S, F, mode='all', return_all=False)

Return the intersection points of lines segments S with triangles F.

Parameters:

  • S: (nS,2,3) shaped array, defining a single line segment or a set of line segments.
  • F: (nF,3,3) shaped array, defining a single triangle or a set of triangles.
  • mode: all to calculate the intersection points of each line segment S with all triangles F or pair for pairwise intersections.
  • return_all: if True, all intersection points are returned. Default is to return only the points that lie on the segments and inside the triangles.

Returns: if return_all==True, a (nS,nF,3) shaped (mode=all) array of intersection points, else, a tuple of intersection points with shape (n,3) and line and plane indices with shape (n), where n <= nS*nF.

geomtools.intersectionPointsPWP(p1, n1, p2, n2, p3, n3, mode='all')

Return the intersection points of planes (p1,n1), (p2,n2) and (p3,n3).

Parameters:

  • pi,`ni` (i=1...3): (npi,3) shaped arrays of points and normals (mode=all) or broadcast compatible arrays (mode=pair), defining a single plane or a set of planes.
  • mode: all to calculate the intersection of each plane (p1,n1) with all planes (p2,n2) and (p3,n3) or pair for pairwise intersections.

Returns: A (np1,np2,np3,3) shaped (mode=all) array of intersection points.

geomtools.intersectionLinesPWP(p1, n1, p2, n2, mode='all')

Return the intersection lines of planes (p1,n1) and (p2,n2).

Parameters:

  • pi,`ni` (i=1...2): (npi,3) shaped arrays of points and normals (mode=all) or broadcast compatible arrays (mode=pair), defining a single plane or a set of planes.
  • mode: all to calculate the intersection of each plane (p1,n1) with all planes (p2,n2) or pair for pairwise intersections.

Returns: A tuple of (np1,np2,3) shaped (mode=all) arrays of intersection points q and vectors m, such that the intersection lines are given by q+t*m.

geomtools.intersectionTimesPOP(X, p, n, mode='all')

Return the intersection of perpendiculars from points X on planes (p,n).

Parameters:

  • X: a (nX,3) shaped array of points (mode=all) or broadcast compatible array (mode=pair).
  • p,`n`: (np,3) shaped arrays of points and normals (mode=all) or broadcast compatible arrays (mode=pair), defining a single plane or a set of planes.
  • mode: all to calculate the intersection for each point X with all planes (p,n) or pair for pairwise intersections.

Returns: A (nX,np) shaped (mode=all) array of parameter values t, such that the intersection points are given by X+t*n.

geomtools.intersectionPointsPOP(X, p, n, mode='all')

Return the intersection points of perpendiculars from points X on planes (p,n).

This is like intersectionTimesPOP but returns a (nX,np,3) shaped (mode=all) array of intersection points instead of the parameter values.

geomtools.intersectionTimesPOL(X, q, m, mode='all')

Return the intersection of perpendiculars from points X on lines (q,m).

Parameters:

  • X: a (nX,3) shaped array of points (mode=all) or broadcast compatible array (mode=pair).
  • q,`m`: (nq,3) shaped arrays of points and vectors (mode=all) or broadcast compatible arrays (mode=pair), defining a single line or a set of lines.
  • mode: all to calculate the intersection for each point X with all lines (q,m) or pair for pairwise intersections.

Returns: A (nX,nq) shaped (mode=all) array of parameter values t, such that the intersection points are given by q+t*m.

geomtools.intersectionPointsPOL(X, q, m, mode='all')

Return the intersection points of perpendiculars from points X on lines (q,m).

This is like intersectionTimesPOL but returns a (nX,nq,3) shaped (mode=all) array of intersection points instead of the parameter values.

geomtools.distancesPFL(X, q, m, mode='all')

Return the distances of points X from lines (q,m).

Parameters:

  • X: a (nX,3) shaped array of points (mode=all) or broadcast compatible array (mode=pair).
  • q,`m`: (nq,3) shaped arrays of points and vectors (mode=all) or broadcast compatible arrays (mode=pair), defining a single line or a set of lines.
  • mode: all to calculate the distance of each point X from all lines (q,m) or pair for pairwise distances.

Returns: A (nX,nq) shaped (mode=all) array of distances.

geomtools.distancesPFS(X, S, mode='all')

Return the distances of points X from line segments S.

Parameters:

  • X: a (nX,3) shaped array of points (mode=all) or broadcast compatible array (mode=pair).
  • S: (nS,2,3) shaped array of line segments (mode=all) or broadcast compatible array (mode=pair), defining a single line segment or a set of line segments.
  • mode: all to calculate the distance of each point X from all line segments S or pair for pairwise distances.

Returns: A (nX,nS) shaped (mode=all) array of distances.

geomtools.insideTriangle(x, P, method='bary')

Checks whether the points P are inside triangles x.

x is a Coords array with shape (ntri,3,3) representing ntri triangles. P is a Coords array with shape (npts,ntri,3) representing npts points in each of the ntri planes of the triangles. This function checks whether the points of P fall inside the corresponding triangles.

Returns an array with (npts,ntri) bool values.

geomtools.faceDistance(X, Fp, return_points=False)

Compute the closest perpendicular distance to a set of triangles.

X is a (nX,3) shaped array of points. Fp is a (nF,3,3) shaped array of triangles.

Note that some points may not have a normal with footpoint inside any of the facets.

The return value is a tuple OKpid,OKdist,OKpoints where:

  • OKpid is an array with the point numbers having a normal distance;
  • OKdist is an array with the shortest distances for these points;
  • OKpoints is an array with the closest footpoints for these points and is only returned if return_points = True.
geomtools.edgeDistance(X, Ep, return_points=False)

Compute the closest perpendicular distance of points X to a set of edges.

X is a (nX,3) shaped array of points. Ep is a (nE,2,3) shaped array of edge vertices.

Note that some points may not have a normal with footpoint inside any of the edges.

The return value is a tuple OKpid,OKdist,OKpoints where:

  • OKpid is an array with the point numbers having a normal distance;
  • OKdist is an array with the shortest distances for these points;
  • OKpoints is an array with the closest footpoints for these points and is only returned if return_points = True.
geomtools.vertexDistance(X, Vp, return_points=False)

Compute the closest distance of points X to a set of vertices.

X is a (nX,3) shaped array of points. Vp is a (nV,3) shaped array of vertices.

The return value is a tuple OKdist,OKpoints where:

  • OKdist is an array with the shortest distances for the points;
  • OKpoints is an array with the closest vertices for the points and is only returned if return_points = True.
geomtools.baryCoords(S, P)

Compute the barycentric coordinates of points P wrt. simplexes S.

S is a (nel,nplex,3) shaped array of n-simplexes (n=nplex-1): - 1-simplex: line segment - 2-simplex: triangle - 3-simplex: tetrahedron P is a (npts,3), (npts,nel,3) or (npts,1,3) shaped array of points.

The return value is a (nplex,npts,nel) shaped array of barycentric coordinates.

geomtools.insideSimplex(BC, bound=True)

Check if points are in simplexes.

BC is an array of barycentric coordinates (along the first axis), which sum up to one. If bound = True, a point lying on the boundary is considered to be inside the simplex.

pyformex-0.8.6/pyformex/doc/html/ref/fe_abq.html0000644000211500021150000015077111705104252021503 0ustar benebene00000000000000 42. fe_abq — Exporting finite element models in Abaqus™ input file format. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

41. fe — Finite Element Models in pyFormex.

Next topic

43. fe_post — A postprocessor for ABAQUS output files.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

42. fe_abq — Exporting finite element models in Abaqus™ input file format.

This module provides functions and classes to export finite element models from pyFormex in the Abaqus™ input format (.inp). The exporter handles the mesh geometry as well as model, node and element properties gathered in a PropertyDB database (see module properties).

While this module provides only a small part of the Abaqus input file format, it suffices for most standard jobs. While we continue to expand the interface, depending on our own necessities or when asked by third parties, we do not intend to make this into a full implementation of the Abaqus input specification. If you urgently need some missing function, there is always the possibility to edit the resulting text file or to import it into the Abaqus environment for further processing.

The module provides two levels of functionality: on the lowest level, there are functions that just generate a part of an Abaqus input file, conforming to the Abaqus™ Keywords manual.

Then there are higher level functions that read data from the property module and write them to the Abaqus input file and some data classes to organize all the data involved with the finite element model.

Classes defined in module fe_abq

class fe_abq.Output(kind=None, keys=None, set=None, type='FIELD', variable='PRESELECT', extra='', **options)

A request for output to .odb and history.

Parameters:

  • type: ‘FIELD’ or ‘HISTORY’
  • kind: None, ‘NODE’, or ‘ELEMENT’ (first character suffices)
  • extra: an extra string to be added to the command line. This allows to add Abaqus options not handled by this constructor. The string will be appended to the command line preceded by a comma.

For kind==’‘:

  • variable: ‘ALL’, ‘PRESELECT’ or ‘’

For kind==’NODE’ or ‘ELEMENT’:

  • keys: a list of output identifiers (compatible with kind type)
  • set: a single item or a list of items, where each item is either a property number or a node/element set name for which the results should be written. If no set is specified, the default is ‘Nall’ for kind==’NODE’ and ‘Eall’ for kind=’ELEMENT’
fmt()

Format an output request.

Return a string with the formatted output command.

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class fe_abq.Result(kind, keys, set=None, output='FILE', freq=1, time=False, **kargs)

A request for output of results on nodes or elements.

Parameters:

  • kind: ‘NODE’ or ‘ELEMENT’ (first character suffices)
  • keys: a list of output identifiers (compatible with kind type)
  • set: a single item or a list of items, where each item is either a property number or a node/element set name for which the results should be written. If no set is specified, the default is ‘Nall’ for kind==’NODE’ and ‘Eall’ for kind=’ELEMENT’
  • output is either FILE (for .fil output) or PRINT (for .dat output)(Abaqus/Standard only)
  • freq is the output frequency in increments (0 = no output)

Extra keyword arguments are available: see the writeNodeResults and writeElemResults methods for details.

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class fe_abq.Interaction(name=None, cross_section=1, friction=0.0, surfacebehavior=None, noseparation=False, pressureoverclosure=None)

A Dict for setting surface interactions pressureoverclosure is an array = [‘hard’/’soft’,’linear’/’nonlinear’/’exponential’/’tabular’/.., value1,value2,value3,... ] Leave empty for default hard contact ‘hard’ will set penalty contact, either ‘linear’ or ‘nonlinear’ ‘soft’ will set soft pressure-overclosure, combine with ‘linear’/’exponential’/’tabular’/’scale factor’ for needed values on dataline: see abaqus keyword manual

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class fe_abq.AbqData(model, prop, nprop=None, eprop=None, steps=[], res=[], out=[], bound=None)

Contains all data required to write the Abaqus input file.

  • model : a Model instance.
  • prop : the Property database.
  • steps : a list of Step instances.
  • res : a list of Result instances.
  • out : a list of Output instances.
  • bound : a tag or alist of the initial boundary conditions. The default is to apply ALL boundary conditions initially. Specify a (possibly non-existing) tag to override the default.
write(jobname=None, group_by_eset=True, group_by_group=False, header='', create_part=False)

Write an Abaqus input file.

  • jobname : the name of the inputfile, with or without ‘.inp’ extension. If None is specified, the output is written to sys.stdout An extra header text may be specified.
  • create_part : if True, the model will be created as an Abaqus Part, followed by and assembly of that part.

Functions defined in module fe_abq

fe_abq.abqInputNames(job)

Returns corresponding Abq jobname and input filename.

job can be either a jobname or input file name, with or without directory part, with or without extension (.inp)

The Abq jobname is the basename without the extension. The abq filename is the abspath of the job with extension ‘.inp’

fe_abq.nsetName(p)

Determine the name for writing a node set property.

fe_abq.esetName(p)

Determine the name for writing an element set property.

fe_abq.fmtCmd(cmd='*')

Format a command.

fe_abq.fmtData1D(data, npl=8, sep=', ', linesep='\n')

Format numerical data in lines with maximum npl items.

data is a numeric array. The array is flattened and then the data are formatted in lines with maximum npl items, separated by sep. Lines are separated by linesep.

fe_abq.fmtData(data, npl=8, sep=', ', linesep='\n')

Format numerical data in lines with maximum npl items.

data is a numeric array, which is coerced to be a 2D array, either by adding a first axis or by collapsing the first ndim-1 axies. Then the data are formatted in lines with maximum npl items, separated by sep. Lines are separated by linesep.

fe_abq.fmtOptions(options)

Format the options of an Abaqus command line.

  • options: a dict with ABAQUS command keywords and values. If the keyword does not take any value, the value in the dict should be an empty string.

Returns a comma-separated string of ‘keyword’ or ‘keyword=value’ fields. The string includes an initial comma.

fe_abq.fmtHeading(text='')

Format the heading of the Abaqus input file.

fe_abq.fmtPart(name='Part-1')

Start a new Part.

fe_abq.fmtMaterial(mat)

Write a material section.

mat is the property dict of the material. The following keys are recognized and output accordingly:

  • name: if specified, and a material with this name has already been written, this function does nothing.

  • elasticity: one of ‘LINEAR’, ‘HYPERELASTIC’, ‘ANISOTROPIC HYPERELASTIC’, ‘USER’. Default is ‘LINEAR’. Defines the elastic behavior class of the material. The requirements for the other keys depend on this type. The fields labeled (opt) are optional.

  • ‘LINEAR’:

    • young_modulus
    • shear_modulus
    • (opt) poisson_ratio: it is calculated if None
  • ‘HYERELASTIC’:

    required:

    • model: one of ‘ogden’, ‘polynomial’ or ‘reduced polynomial’
    • constants: list of all parameter required for the model (see Abaqus documentation)

    optional:

    • order: order of the model. If blank will be automatically calculated from the len of the constants list

      example:

      intimaMat = {
        'name': 'intima',
        'density': 0.1, # Not Used, but Abaqus does not like a material without
        'elasticity':'hyperelastic',
        'type':'reduced polynomial',
        'constants': [6.79E-03, 5.40E-01, -1.11, 10.65, -7.27, 1.63, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
      }
      
  • ‘ANISOTROPIC HYPERELASTIC’:

  • ‘USER’:

fe_abq.fmtTransform(setname, csys)

Write transform command for the given set.

  • setname is the name of a node set
  • csys is a CoordSystem.
fe_abq.fmtFrameSection(el, setname)

Write a frame section for the named element set.

Recognized data fields in the property record:

  • sectiontype GENERAL:
    • cross_section
    • moment_inertia_11
    • moment_inertia_12
    • moment_inertia_22
    • torsional_constant
  • sectiontype CIRC:
    • radius
  • sectiontype RECT:
    • width
    • height
  • all sectiontypes:
    • young_modulus
    • shear_modulus
  • optional:
    • density: density of the material
    • yield_stress: yield stress of the material
    • orientation: a vector specifying the direction cosines of the 1 axis
fe_abq.fmtGeneralBeamSection(el, setname)

Write a general beam section for the named element set.

To specify a beam section when numerical integration over the section is not required.

Recognized data fields in the property record:

  • sectiontype GENERAL:
    • cross_section
    • moment_inertia_11
    • moment_inertia_12
    • moment_inertia_22
    • torsional_constant
  • sectiontype CIRC:
    • radius
  • sectiontype RECT:
    • width, height
  • all sectiontypes:
    • young_modulus
    • shear_modulus or poisson_ration
  • optional:
    • density: density of the material (required in Abaqus/Explicit)
fe_abq.fmtBeamSection(el, setname)

Write a beam section for the named element set.

To specify a beam section when numerical integration over the section is required.

Recognized data fields in the property record:

  • all sectiontypes: material
  • sectiontype GENERAL:
    • cross_section
    • moment_inertia_11
    • moment_inertia_12
    • moment_inertia_22
    • torsional_constant
  • sectiontype CIRC:
    • radius
    • intpoints1 (number of integration points in the first direction) optional
    • intpoints2 (number of integration points in the second direction) optional
  • sectiontype RECT:
    • width, height
    • intpoints1 (number of integration points in the first direction) optional
    • intpoints2 (number of integration points in the second direction) optional
fe_abq.fmtConnectorSection(el, setname)

Write a connector section.

Optional data:

  • behavior : connector behavior name
  • orient : connector orientation
fe_abq.fmtConnectorBehavior(prop)

Write a connector behavior. Implemented: Elasticity, Stop Examples: Elasticity P.Prop(name=’connbehavior1’,ConnectorBehavior=’‘,Elasticity=dict(component=[1,2,3,4,5,6],value=[1,1,1,1,1,1])) Stop: P.Prop(name=’connbehavior3’,ConnectorBehavior=’‘,Stop=dict(component=[1,2,3,4,5,6],lowerlimit=[1,1,1,1,1,1], upperlimit=[2, 2, 2, 2,2,2]))

fe_abq.fmtSurface(prop)

Format the surface definitions.

Required:

  • set: the elements/nodes in the surface, either numbers or a set name.
  • name: the surface name
  • surftype: ‘ELEMENT’ or ‘NODE’
  • label: face or edge identifier (only required for surftype = ‘ELEMENT’)

This label can be a string, or a list of strings. This allows to use different identifiers for the different elements in the surface. Thus:

Prop(name='mysurf',set=[0,1,2,6],surftype='element',label=['S1','S2','S1','S3')

will get exported to Abaqus as:

*SURFACE, NAME=mysurf, TYPE=element
1, S1
2, S2,
1, S1
7, S3 
fe_abq.fmtAnalyticalSurface(prop)

Format the analytical surface rigid body.

Required:

  • nodeset: refnode.
  • name: the surface name
  • surftype: ‘ELEMENT’ or ‘NODE’
  • label: face or edge identifier (only required for surftype = ‘NODE’)

Example:

>>> P.Prop(name='AnalySurf', nodeset = 'REFNOD', analyticalsurface='')
fe_abq.fmtSurfaceInteraction(prop)

Format the interactions.

Required:

-name

Optional:

  • cross_section (for node based interaction)
  • friction : friction coeff or ‘rough’
  • surface behavior: no separation
  • surface behavior: pressureoverclosure
fe_abq.fmtGeneralContact(prop)

Format the general contact.

Only implemented on model level

Required:

  • interaction: interaction properties : name or Dict

Optional:

  • Exclusions (exl)

Example:

>>> P.Prop(generalinteraction=Interaction(name ='contactprop1'),exl =[['surf11', 'surf12'],['surf21',surf22]])
fe_abq.fmtContactPair(prop)

Format the contact pair.

Required:

  • master: master surface
  • slave: slave surface
  • interaction: interaction properties : name or Dict

Example:

>>> P.Prop(name='contact0',interaction=Interaction(name ='contactprop', surfacebehavior=True, pressureoverclosure=['hard','linear',0.0, 0.0, 0.001]), master ='quadtubeINTSURF1',  slave='hexstentEXTSURF', contacttype='NODE TO SURFACE')
fe_abq.fmtConstraint(prop)

Format Tie constraint

Required:

-name -adjust (yes or no) -slave -master

Optional:

-type (surf2surf, node2surf) -no rotation

Example:

>>> P.Prop(constraint='1', name = 'constr1', adjust = 'no', master = 'hexstentbarSURF', slave = 'hexstentEXTSURF',type='NODE TO SURFACE')    
fe_abq.fmtInitialConditions(prop)

Format initial conditions

Required:

-type -nodes -data

Example:

P.Prop(initialcondition='', nodes ='Nall', type = 'TEMPERATURE', data = 37.)
fe_abq.fmtOrientation(prop)

Format the orientation.

Optional:

  • definition
  • system: coordinate system
  • a: a first point
  • b: a second point
fe_abq.fmtEquation(prop)

Format multi-point constraint using an equation

Required:

  • equation

Equation should be a list, which contains the different terms of the equation. Each term is again a list with three values:

  • First value: node number
  • Second value: degree of freedom
  • Third value: coefficient

Example:

P.nodeProp(equation=[[209,1,1],[32,1,-1]])

This forces the displacement in Y-direction of nodes 209 and 32 to be equal.

fe_abq.writeNodes(fil, nodes, name='Nall', nofs=1)

Write nodal coordinates.

The nodes are added to the named node set. If a name different from ‘Nall’ is specified, the nodes will also be added to a set named ‘Nall’. The nofs specifies an offset for the node numbers. The default is 1, because Abaqus numbering starts at 1.

fe_abq.writeElems(fil, elems, type, name='Eall', eid=None, eofs=1, nofs=1)

Write element group of given type.

elems is the list with the element node numbers. The elements are added to the named element set. If a name different from ‘Eall’ is specified, the elements will also be added to a set named ‘Eall’. The eofs and nofs specify offsets for element and node numbers. The default is 1, because Abaqus numbering starts at 1. If eid is specified, it contains the element numbers increased with eofs.

fe_abq.writeSet(fil, type, name, set, ofs=1)

Write a named set of nodes or elements (type=NSET|ELSET)

set : an ndarray. set can be a list of node/element numbers, in which case the ofs value will be added to them, or a list of names the name of another already defined set.

fe_abq.writeSection(fil, prop)

Write an element section.

prop is a an element property record with a section and eltype attribute

fe_abq.writeDisplacements(fil, prop, dtype='DISPLACEMENT')

Write boundary conditions of type BOUNDARY, TYPE=DISPLACEMENT

prop is a list of node property records that should be scanned for displ attributes to write.

By default, the boundary conditions are applied as a modification of the existing boundary conditions, i.e. initial conditions and conditions from previous steps remain in effect. The user can set op=’NEW’ to remove the previous conditions. This will also remove initial conditions!

fe_abq.writeCloads(fil, prop, op='NEW')

Write cloads.

prop is a list of node property records that should be scanned for displ attributes to write.

By default, the loads are applied as new values in the current step. The user can set op=’MOD’ to add the loads to already existing ones.

fe_abq.writeDloads(fil, prop, op='NEW')

Write Dloads.

prop is a list property records having an attribute dload

By default, the loads are applied as new values in the current step. The user can set op=’MOD’ to add the loads to already existing ones.

fe_abq.writeDsloads(fil, prop, op='NEW')

Write Dsloads.

prop is a list property records having an attribute dsload

By default, the loads are applied as new values in the current step. The user can set op=’MOD’ to add the loads to already existing ones.

fe_abq.writeNodeOutput(fil, kind, keys, set='Nall')

Write a request for nodal result output to the .odb file.

  • keys: a list of NODE output identifiers
  • set: a single item or a list of items, where each item is either a property number or a node set name for which the results should be written
fe_abq.writeNodeResult(fil, kind, keys, set='Nall', output='FILE', freq=1, globalaxes=False, lastmode=None, summary=False, total=False)

Write a request for nodal result output to the .fil or .dat file.

  • keys: a list of NODE output identifiers
  • set: a single item or a list of items, where each item is either a property number or a node set name for which the results should be written
  • output is either FILE (for .fil output) or PRINT (for .dat output)(Abaqus/Standard only)
  • freq is the output frequency in increments (0 = no output)

Extra arguments:

  • globalaxes: If ‘YES’, the requested output is returned in the global axes. Default is to use the local axes wherever defined.

Extra arguments for output=``PRINT``:

  • summary: if True, a summary with minimum and maximum is written
  • total: if True, sums the values for each key

Remark: the kind argument is not used, but is included so that we can easily call it with a Results dict as arguments

fe_abq.writeElemOutput(fil, kind, keys, set='Eall')

Write a request for element output to the .odb file.

  • keys: a list of ELEMENT output identifiers
  • set: a single item or a list of items, where each item is either a property number or an element set name for which the results should be written
fe_abq.writeElemResult(fil, kind, keys, set='Eall', output='FILE', freq=1, pos=None, summary=False, total=False)

Write a request for element result output to the .fil or .dat file.

  • keys: a list of ELEMENT output identifiers
  • set: a single item or a list of items, where each item is either a property number or an element set name for which the results should be written
  • output is either FILE (for .fil output) or PRINT (for .dat output)(Abaqus/Standard only)
  • freq is the output frequency in increments (0 = no output)

Extra arguments:

  • pos: Position of the points in the elements at which the results are written. Should be one of:

    • ‘INTEGRATION POINTS’ (default)
    • ‘CENTROIDAL’
    • ‘NODES’
    • ‘AVERAGED AT NODES’

    Non-default values are only available for ABAQUS/Standard.

Extra arguments for output=’PRINT’:

  • summary: if True, a summary with minimum and maximum is written
  • total: if True, sums the values for each key

Remark: the kind argument is not used, but is included so that we can easily call it with a Results dict as arguments

fe_abq.writeFileOutput(fil, resfreq=1, timemarks=False)

Write the FILE OUTPUT command for Abaqus/Explicit

fe_abq.exportMesh(filename, mesh, eltype=None, header='')

Export a finite element mesh in Abaqus .inp format.

This is a convenience function to quickly export a mesh to Abaqus without having to go through the whole setup of a complete finite element model. This just writes the nodes and elements specified in the mesh to the file with the specified name. The resulting file can then be imported in Abaqus/CAE or manual be edited to create a full model. If an eltype is specified, it will oerride the value stored in the mesh. This should be used to set a correct Abaqus element type matchin the mesh.

pyformex-0.8.6/pyformex/doc/html/ref/odict.html0000644000211500021150000003224011705104254021360 0ustar benebene00000000000000 56. odict — Specialized dictionary type structures. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

55. mydict

Next topic

57. collection — Tools for handling collections of elements belonging to multiple parts.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

56. odict — Specialized dictionary type structures.

Classes defined in module odict

class odict.ODict(data={})

An ordered dictionary.

This is a dictionary that keeps the keys in order. The default order is the insertion order. The current order can be changed at any time.

The ODict can be initialized with a Python dict or another ODict object. If a plain Python dict is used, the resulting order is undefined.

update(data={})

Add a dictionary to the ODict object.

The new keys will be appended to the existing, but the order of the added keys is undetemined if data is a dict object. If data is an ODict its order will be respected..

sort(keys)

Set the order of the keys.

keys should be a list containing exactly all the keys from self.

keys()

Return the keys in order.

values()

Return the values in order of the keys.

items()

Return the key,value pairs in order of the keys.

pos(key)

Return the position of the specified key.

If the key is not in the ODict, None is returned

class odict.KeyedList(alist=[])

A named item list.

A KeyedList is a list of lists or tuples. Each item (sublist or tuple) should at least have 2 elements: the first one is used as a key to identify the item, but is also part of the information (value) of the item.

items()

Return the key+value lists in order of the keys.

update(data={})

Add a dictionary to the ODict object.

The new keys will be appended to the existing, but the order of the added keys is undetemined if data is a dict object. If data is an ODict its order will be respected..

sort(keys)

Set the order of the keys.

keys should be a list containing exactly all the keys from self.

keys()

Return the keys in order.

values()

Return the values in order of the keys.

pos(key)

Return the position of the specified key.

If the key is not in the ODict, None is returned

Functions defined in module odict

pyformex-0.8.6/pyformex/doc/html/ref/coords.html0000644000211500021150000100207411705104250021546 0ustar benebene00000000000000 1. coords — A structured collection of 3D coordinates. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

pyFormex reference manual

Next topic

2. formex — Formex algebra in Python

[FSF Associate Member]

Valid XHTML 1.0 Transitional

1. coords — A structured collection of 3D coordinates.

The coords module defines the Coords class, which is the basic data structure in pyFormex to store the coordinates of points in a 3D space.

This module implements a data class for storing large sets of 3D coordinates and provides an extensive set of methods for transforming these coordinates. Most of pyFormex’s classes which represent geometry (e.g. Geometry, Formex, Mesh, TriSurface, Curve) use a Coords object to store their coordinates, and thus inherit all the transformation methods of this class.

While the user will mostly use the higher level classes, he might occasionally find good reason to use the Coords class directly as well.

Classes defined in module coords

class coords.Coords

A structured collection of points in a 3D cartesian space.

The Coords class is the basic data structure used throughout pyFormex to store coordinates of points in a 3D space. It is used by other classes, such as Formex and Surface, which thus inherit the same transformation capabilities. Applications will mostly use the higher level classes, which usually have more elaborated consistency checking and error handling.

Coords is implemented as a subclass of numpy.ndarray, and thus inherits all its methods. The last axis of the Coords always has a length equal to 3. Each set of 3 values along the last axis represents a single point in 3D cartesian space. The float datatype is only checked at creation time. It is the responsibility of the user to keep this consistent throughout the lifetime of the object.

A new Coords object is created with the following syntax

Coords(data=None,dtyp=Float,copy=False)

Parameters:

  • data: array_like of type float. The last axis should have a length of 1, 2 or 3, bu will always be expanded to 3. If no data are specified, an empty Coords with shape (0,3) is created.
  • dtyp: the float datatype to be used. It not specified, the datatype of data is used, or the default Float (which is equivalent to numpy.float32).
  • copy: boolean. If True, the data are copied. The default setting will try to use the original data if possible, e.g. if data is a correctly shaped and typed numpy.ndarray.

Example:

>>> Coords([1.,0.])
Coords([ 1.,  0.,  0.], dtype=float32)
points()

Returns the Coords object as a simple set of points.

This reshapes the array to a 2-dimensional array, flattening the structure of the points.

pshape()

Returns the shape of the Coords object.

This is the shape of the NumPy array with the last axis removed. The full shape of the Coords array can be obtained from its shape attribute.

npoints()

Return the total number of points.

ncoords()

Return the total number of points.

x()

Return the X-coordinates of all points.

Returns an array with all the X-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with

self[...,0]
y()

Return the Y-coordinates of all points.

Returns an array with all the Y-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with

self[...,1]
z()

Return the Z-coordinates of all points.

Returns an array with all the Z-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with

self[...,0]
bbox()

Return the bounding box of a set of points.

The bounding box is the smallest rectangular volume in the global coordinates, such that no point of the Coords are outside that volume.

Returns:

A Coords object with shape(2,3): the first point contains the minimal coordinates, the second has the maximal ones.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).bbox()
[[ 0.  0.  0.]
 [ 3.  3.  0.]]
center()

Return the center of the Coords.

The center of a Coords is the center of its bbox(). The return value is a (3,) shaped Coords object.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).center()
[ 1.5  1.5  0. ]

See also: centroid()

average(wts=None, axis=0)

Return a (weighted) average of the Coords.

The average of a Coords is a Coords with one axis less than the original, obtained by averaging all the points along that axis. The weights array can either be 1-D (in which case its length must be the size along the given axis) or of the same shape as a. Weights can be specified as a 1-D array with the length of that axis, or as an array with the same shape as the Coords. The sum of the weights (along the specified axis if not 1-D) will generally be equal to 1.0. If wts=None, then all points are assumed to have a weight equal to one divided by the length of the specified axis.

Example:

>>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],                  [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average()
[[ 2.  0.  0.]
 [ 3.  0.  0.]
 [ 4.  0.  0.]]
>>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],                  [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average(axis=1)
[[ 1.  0.  0.]
 [ 5.  0.  0.]]
>>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],                  [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average(wts=[0.5,0.25,0.25],axis=1)
[[ 0.75  0.    0.  ]
 [ 4.75  0.    0.  ]]
centroid()

Return the centroid of the Coords.

The centroid of a Coords is the point whose coordinates are the mean values of all points. The return value is a (3,) shaped Coords object.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).centroid()
[ 1.  1.  0.]

See also: center()

sizes()

Return the sizes of the Coords.

Return an array with the length of the bbox along the 3 axes.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).sizes()
[ 3.  3.  0.]
dsize()

Return an estimate of the global size of the Coords.

This estimate is the length of the diagonal of the bbox().

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).dsize()
4.24264
bsphere()

Return the diameter of the bounding sphere of the Coords.

The bounding sphere is the smallest sphere with center in the center() of the Coords, and such that no points of the Coords are lying outside the sphere.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).bsphere()
2.12132
inertia(mass=None)

Return inertia related quantities of the Coords.

This returns the center of gravity, the principal axes of inertia, the principal moments of inertia and the inertia tensor.

distanceFromPlane(p, n)

Return the distance of all points from the plane (p,n).

p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components.

The return value is a float array with shape self.pshape() with the distance of each point to the plane through p and having normal n. Distance values are positive if the point is on the side of the plane indicated by the positive normal.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromPlane([0.,0.,0.],[1.,0.,0.])
[[ 0.  3.  0.]]
distanceFromLine(p, n)

Return the distance of all points from the line (p,n).

p,n are (1,3) or (npts,3) arrays defining 1 or npts lines p is a point on the line specified by 3 coordinates. n is a vector specifying the direction of the line through p.

The return value is a [...] shaped array with the distance of each point to the line through p with direction n. All distance values are positive or zero.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromLine([0.,0.,0.],[1.,0.,0.])
[[ 0.  0.  3.]]
distanceFromPoint(p)

Return the distance of all points from the point p.

p is a single point specified by 3 coordinates.

The return value is a [...] shaped array with the distance of each point to point p. All distance values are positive or zero. Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromPoint([0.,0.,0.])
[[ 0.  3.  3.]]
closestToPoint(p)

Return the point closest to point p.

directionalSize(n, p=None, _points=False)

Return the extreme distances from the plane p,n.

The direction n can be specified by a 3 component vector or by a single integer 0..2 designing one of the coordinate axes.

p is any point in space. If not specified, it is taken as the center() of the Coords.

The return value is a tuple of two float values specifying the extreme distances from the plane p,n.

directionalExtremes(n, p=None)

Return extremal planes in the direction n.

n and p have the same meaning as in directionalSize.

The return value is a list of two points on the line (p,n), such that the planes with normal n through these points define the extremal planes of the Coords.

directionalWidth(n)

Return the width of a Coords in the given direction.

The direction can be specified by a 3 component vector or by a single integer 0..2 designating one of the coordinate axes.

The return value is the thickness of the object in the direction n.

test(dir=0, min=None, max=None, atol=0.0)

Flag points having coordinates between min and max.

Tests the position of the points of the Coords with respect to one or two planes. This method is very convenient in clipping a Coords in a specified direction. In most cases the clipping direction is one of the global cooordinate axes, but a general direction may be used as well.

Parameters:

  • dir: either a global axis number (0, 1 or 2) or a direction vector consisting of 3 floats. It specifies the direction in which the distances are measured. Default is the 0 (or x) direction.
  • min,`max`: position of the minimum and maximum clipping planes. If dir was specified as an integer (0,1,2), this is a single float value corresponding with the coordinate in that axis direction. Else, it is a point in the clipping plane with normal direction dir. One of the two clipping planes may be left unspecified.
Returns: a 1D integer array with same length as the number of points.
For each point the value is 1 (True) if the point is above the minimum clipping plane and below the maximum clipping plane, or 0 (False) otherwise. An unspecified clipping plane corresponds with an infinitely low or high value. The return value can directly be used as an index to obtain a Coords with the points satisfying the test (or not). See the examples below.

Example:

>>> x = Coords([[0.,0.],[1.,0.],[0.,1.],[0.,2.]])
>>> print x.test(min=0.5)
[False  True False False]
>>> t = x.test(dir=1,min=0.5,max=1.5)
>>> print x[t]
[[ 0.  1.  0.]]
>>> print x[~t]
[[ 0.  0.  0.]
 [ 1.  0.  0.]
 [ 0.  2.  0.]]
fprint(fmt='%10.3e %10.3e %10.3e')

Formatted printing of a Coords object.

The supplied format should contain 3 formatting sequences for the three coordinates of a point.

set(f)

Set the coordinates from those in the given array.

scale(scale, dir=None, center=None, inplace=False)

Return a copy scaled with scale[i] in direction i.

The scale should be a list of 3 scaling factors for the 3 axis directions, or a single scaling factor. In the latter case, dir (a single axis number or a list) may be given to specify the direction(s) to scale. The default is to produce a homothetic scaling. The center of the scaling, if not specified, is the global origin. If a center is specified, the result is equivalent to:

self.translate(-center).scale(scale,dir).translate(center)

Example:

>>> print Coords([1.,1.,1.]).scale(2)
[ 2.  2.  2.]
>>> print Coords([1.,1.,1.]).scale([2,3,4])
[ 2.  3.  4.]
translate(dir, step=None, inplace=False)

Translate a Coords object.

Translates the Coords in the direction dir over a distance step * length(dir).

Parameters:

  • dir: specifies the direction and distance of the translation. It can be either
    • an axis number (0,1,2), specifying a unit vector in the direction of one of the coordinate axes.
    • a single translation vector,
    • an array of translation vectors, compatible with the Coords shape.
  • step: If specified, the translation vector specified by dir will be multiplied with this value. It is commonly used with unit dir vectors to set the translation distance.

Example:

>>> x = Coords([1.,1.,1.])
>>> print x.translate(1)
[ 1.  2.  1.]
>>> print x.translate(1,1.)
[ 1.  2.  1.]
>>> print x.translate([0,1,0])
[ 1.  2.  1.]
>>> print x.translate([0,2,0],0.5)
[ 1.  2.  1.]
centered()

Return a centered copy of the Coords.

Returns a Coords which is a translation thus that the center coincides with the origin. This is equivalent with:

self.trl(-self.center())
align(alignment='---')

Align the Coords along the global axes.

Alignment involves a translation such that the bounding box of the Coords object becomes aligned on the origin of the global axes. The requested alignment is determined by a string of three characters, one for each of the coordinate axes. The character determines how the structure is aligned in the corresponding direction:

  • ‘-‘: aligned on the minimal value of the bounding box,
  • ‘+’: aligned on the maximal value of the bounding box,
  • ‘0’: aligned on the middle value of the bounding box.

Any other value will make the alignment in that direction unchanged.

The default alignment string ‘—’ results in a translation which puts all the points in the octant with all positive coordinate values. A string ‘000’ will center the object around the origin, just like the (slightly faster) centered() method, which is .

rotate(angle, axis=2, around=None)

Return a copy rotated over angle around axis.

The angle is specified in degrees. The axis is either one of (0,1,2) designating the global axes, or a vector specifying an axis through the origin. If no axis is specified, rotation is around the 2(z)-axis. This is convenient for working on 2D-structures.

As a convenience, the user may also specify a 3x3 rotation matrix, in which case the function rotate(mat) is equivalent to affine(mat).

All rotations are performed around the point [0,0,0], unless a rotation origin is specified in the argument ‘around’.

shear(dir, dir1, skew, inplace=False)

Return a copy skewed in the direction dir of plane (dir,dir1).

The coordinate dir is replaced with (dir + skew * dir1).

reflect(dir=0, pos=0.0, inplace=False)

Reflect the coordinates in direction dir against plane at pos.

Parameters:

  • dir: int: direction of the reflection (default 0)
  • pos: float: offset of the mirror plane from origin (default 0.0)
  • inplace: boolean: change the coordinates inplace (default False)
affine(mat, vec=None)

Returns a general affine transform of the Coords object.

mat: a 3x3 float matrix

vec: a length 3 list or array of floats

The returned object has coordinates given by self * mat + vec.

position(x, y)

Position an object so that points x are aligned with y.

Parameters as for arraytools.trfMatrix

cylindrical(dir=[0, 1, 2], scale=[1.0, 1.0, 1.0], angle_spec=0.017453292519943295)

Converts from cylindrical to cartesian after scaling.

dir specifies which coordinates are interpreted as resp. distance(r), angle(theta) and height(z). Default order is [r,theta,z]. scale will scale the coordinate values prior to the transformation. (scale is given in order r,theta,z). The resulting angle is interpreted in degrees.

toCylindrical(dir=[0, 1, 2], angle_spec=0.017453292519943295)

Converts from cartesian to cylindrical coordinates.

dir specifies which coordinates axes are parallel to respectively the cylindrical axes distance(r), angle(theta) and height(z). Default order is [x,y,z]. The angle value is given in degrees.

spherical(dir=[0, 1, 2], scale=[1.0, 1.0, 1.0], angle_spec=0.017453292519943295, colat=False)

Converts from spherical to cartesian after scaling.

  • dir specifies which coordinates are interpreted as resp. longitude(theta), latitude(phi) and distance(r).
  • scale will scale the coordinate values prior to the transformation.

Angles are interpreted in degrees. Latitude, i.e. the elevation angle, is measured from equator in direction of north pole(90). South pole is -90.

If colat=True, the third coordinate is the colatitude (90-lat) instead.

superSpherical(n=1.0, e=1.0, k=0.0, dir=[0, 1, 2], scale=[1.0, 1.0, 1.0], angle_spec=0.017453292519943295, colat=False)

Performs a superspherical transformation.

superSpherical is much like spherical, but adds some extra parameters to enable the creation of virtually any surface.

Just like with spherical(), the input coordinates are interpreted as the longitude, latitude and distance in a spherical coordinate system.

dir specifies which coordinates are interpreted as resp. longitude(theta), latitude(phi) and distance(r). Angles are then interpreted in degrees. Latitude, i.e. the elevation angle, is measured from equator in direction of north pole(90). South pole is -90. If colat=True, the third coordinate is the colatitude (90-lat) instead.

scale will scale the coordinate values prior to the transformation.

The n and e parameters define exponential transformations of the north_south (latitude), resp. the east_west (longitude) coordinates. Default values of 1 result in a circle.

k adds ‘eggness’ to the shape: a difference between the northern and southern hemisphere. Values > 0 enlarge the southern hemishpere and shrink the northern.

toSpherical(dir=[0, 1, 2], angle_spec=0.017453292519943295)

Converts from cartesian to spherical coordinates.

dir specifies which coordinates axes are parallel to respectively the spherical axes distance(r), longitude(theta) and latitude(phi). Latitude is the elevation angle measured from equator in direction of north pole(90). South pole is -90. Default order is [0,1,2], thus the equator plane is the (x,y)-plane.

The returned angle values are given in degrees.

bump1(dir, a, func, dist)

Return a Coords with a one-dimensional bump.

  • dir specifies the axis of the modified coordinates;
  • a is the point that forces the bumping;
  • dist specifies the direction in which the distance is measured;
  • func is a function that calculates the bump intensity from distance and should be such that func(0) != 0.
bump2(dir, a, func)

Return a Coords with a two-dimensional bump.

dir specifies the axis of the modified coordinates; a is the point that forces the bumping; func is a function that calculates the bump intensity from distance !! func(0) should be different from 0.

bump(dir, a, func, dist=None)

Return a Coords with a bump.

A bump is a modification of a set of coordinates by a non-matching point. It can produce various effects, but one of the most common uses is to force a surface to be indented by some point.

dir specifies the axis of the modified coordinates; a is the point that forces the bumping; func is a function that calculates the bump intensity from distance (!! func(0) should be different from 0) dist is the direction in which the distance is measured : this can be one of the axes, or a list of one or more axes. If only 1 axis is specified, the effect is like function bump1 If 2 axes are specified, the effect is like bump2 This function can take 3 axes however. Default value is the set of 3 axes minus the direction of modification. This function is then equivalent to bump2.

flare(xf, f, dir=[0, 2], end=0, exp=1.0)

Create a flare at the end of a Coords block.

The flare extends over a distance xf at the start (end=0) or end (end=1) in direction dir[0] of the coords block, and has a maximum amplitude of f in the dir[1] direction.

map(func)

Return a Coords mapped by a 3-D function.

This is one of the versatile mapping functions. func is a numerical function which takes three arguments and produces a list of three output values. The coordinates [x,y,z] will be replaced by func(x,y,z). The function must be applicable to arrays, so it should only include numerical operations and functions understood by the numpy module. This method is one of several mapping methods. See also map1 and mapd.

Example:

>>> print Coords([[1.,1.,1.]]).map(lambda x,y,z: [2*x,3*y,4*z])
[[ 2.  3.  4.]]
map1(dir, func, x=None)

Return a Coords where coordinate i is mapped by a 1-D function.

func is a numerical function which takes one argument and produces one result. The coordinate dir will be replaced by func(coord[x]). If no x is specified, x is taken equal to dir. The function must be applicable on arrays, so it should only include numerical operations and functions understood by the numpy module. This method is one of several mapping methods. See also map and mapd.

mapd(dir, func, point, dist=None)

Maps one coordinate by a function of the distance to a point.

func a numerical function which takes one argument and produces one result. The coordinate dir will be replaced by func(d), where d is calculated as the distance to point. The function must be applicable on arrays, so it should only include numerical operations and functions understood by the numpy module. By default, the distance d is calculated in 3-D, but one can specify a limited set of axes to calculate a 2-D or 1-D distance. This method is one of several mapping methods. See also map3() and map1().

Example:

E.mapd(2,lambda d:sqrt(10**2-d**2),f.center(),[0,1])

maps E on a sphere with radius 10.

egg(k)

Maps the coordinates to an egg-shape

replace(i, j, other=None)

Replace the coordinates along the axes i by those along j.

i and j are lists of axis numbers or single axis numbers. replace ([0,1,2],[1,2,0]) will roll the axes by 1. replace ([0,1],[1,0]) will swap axes 0 and 1. An optionally third argument may specify another Coords object to take the coordinates from. It should have the same dimensions.

swapAxes(i, j)

Swap coordinate axes i and j.

Beware! This is different from numpy’s swapaxes() method !

rollAxes(n=1)

Roll the axes over the given amount.

Default is 1, thus axis 0 becomes the new 1 axis, 1 becomes 2 and 2 becomes 0.

projectOnPlane(n=2, P=[0.0, 0.0, 0.0])

Project a Coords on a plane (or planes).

Parameters:

  • n: the normal direction to the plane. It can be specified either by a list of three floats, or by a single integer (0, 1 or 2) to use one of the global axes.
  • P: a point on the plane, by default the global origin. If an int, the plane is the coordinate plane perpendicular to the
..note: For planes parallel to a coordinate plane, it is far more
efficient to specify the normal by an axis number than by a three component vector.

Returns: a Coords with same shape as original, with all the points projected on the specified plane(s).

projectOnSphere(radius=1.0, center=[0.0, 0.0, 0.0])

Project Coords on a sphere.

The default sphere is a unit sphere at the origin. The center of the sphere should not be part of the Coords.

projectOnCylinder(radius=1.0, dir=0, center=[0.0, 0.0, 0.0])

Project Coords on a cylinder with axis parallel to a global axis.

The default cylinder has its axis along the x-axis and a unit radius. No points of the Coords should belong to the axis..

projectOnSurface(S, n, ignore_errors=False)

Project the Coords on a triangulated surface.

The points of the Coords are projected in the direction of the vector n onto the surface S.

Parameters:

  • S: TriSurface: any triangulated surface
  • n: int or vector: specifies the direction of the projection
  • ignore_errors: if True, projective lines not cutting the surface will result in NaN values. The default is to raise an error.

If successful, a Coords with the same structure as the input is returned.

isopar(eltype, coords, oldcoords)

Perform an isoparametric transformation on a Coords.

This is a convenience method to transform a Coords object through an isoparametric transformation. It is equivalent to:

Isopar(eltype,coords,oldcoords).transform(self)

See plugins.isopar for more details.

transformCS(currentCS, initialCS=None)

Perform a CoordinateSystem transformation on the Coords.

This method transforms the Coords object by the transformation that turns the initial CoordinateSystem into the currentCoordinateSystem.

currentCS and initialCS are CoordinateSystem or (4,3) shaped Coords instances. If initialCS is None, the global (x,y,z) axes are used.

E.g. the default initialCS and currentCS equal to:

 0.  1.  0.
-1.  0.  0.
 0.  0.  1.
 0.  0.  0.

result in a rotation of 90 degrees around the z-axis.

This is a convenience function equivalent to:

self.isopar('tet4',currentCS,initialCS)
addNoise(rsize=0.1, asize=0.0)

Add random noise to a Coords.

A random amount is added to eacho individual coordinate in the Coords. The difference of any coordinate from its original value will not be maximally asize + rsize * self.sizes().max(). The default is to set it to 0.1 times the geometrical size of the structure.

replicate(n, dir=0, step=None)

Replicate a Coords n times with fixed step in any direction.

Returns a Coords object with shape (n,) + self.shape, thus having an extra first axis. Each component along the axis 0 is equal to the previous component translated over (dir,step), where dir and step are interpreted just like in the translate() method. The first component along the axis 0 is identical to the original Coords.

split()

Split the coordinate array in blocks along first axis.

The result is a sequence of arrays with shape self.shape[1:]. Raises an error if self.ndim < 2.

fuse(nodesperbox=1, shift=0.5, rtol=1e-05, atol=1e-05, repeat=True)

Find (almost) identical nodes and return a compressed set.

This method finds the points that are very close and replaces them with a single point. The return value is a tuple of two arrays:

  • the unique points as a Coords object with shape (npoints,3)
  • an integer (nnod) array holding an index in the unique coordinates array for each of the original nodes. This index will have the same shape as the pshape() of the coords array.

The procedure works by first dividing the 3D space in a number of equally sized boxes, with a mean population of nodesperbox. The boxes are numbered in the 3 directions and a unique integer scalar is computed, that is then used to sort the nodes. Then only nodes inside the same box are compared on almost equal coordinates, using the numpy allclose() function. Two coordinates are considered close if they are within a relative tolerance rtol or absolute tolerance atol. See numpy for detail. The default atol is set larger than in numpy, because pyformex typically runs with single precision. Close nodes are replaced by a single one.

Running the procedure once does not guarantee to find all close nodes: two close nodes might be in adjacent boxes. The performance hit for testing adjacent boxes is rather high, and the probability of separating two close nodes with the computed box limits is very small. Therefore, the most sensible way is to run the procedure twice, with a different shift value (they should differ more than the tolerance). Specifying repeat=True will automatically do this.

match(coords, **kargs)

Match points form another Coords object.

This method finds the points from coords that coincide with (or are very close to) points of self.

Parameters:

  • coords: a Coords object
  • **kargs: keyword arguments that you want to pass to the fuse() method.

This method works by concatenating the serialized point sets of both Coords and then fusing them.

Returns:

  • matches: an Int array with shape (nmatches,2)
  • coords: a Coords with the fused coordinate set
  • index: an index with the position of each of the serialized points of the concatenation in the fused coordinate set. To find the index of the points of the orginal coordinate sets, split this index at the position self.npoints() and reshape the resulting parts to self.pshape(), resp. coords.pshape().
append(coords)

Append coords to a Coords object.

The appended coords should have matching dimensions in all but the first axis.

Returns the concatenated Coords object, without changing the current.

This is comparable to numpy.append(), but the result is a Coords object, the default axis is the first one instead of the last, and it is a method rather than a function.

classmethod concatenate(clas, L, axis=0)

Concatenate a list of Coords object.

All Coords object in the list L should have the same shape except for the length of the specified axis. This function is equivalent to the numpy concatenate, but makes sure the result is a Coords object,and the default axis is the first one instead of the last.

classmethod fromstring(clas, fil, sep=' ', ndim=3, count=-1)

Create a Coords object with data from a string.

This convenience function uses the numpy.fromstring() function to read coordinates from a string.

fil: a string containing a single sequence of float numbers separated by whitespace and a possible separator string.

sep: the separator used between the coordinates. If not a space, all extra whitespace is ignored.

ndim: number of coordinates per point. Should be 1, 2 or 3 (default). If 1, resp. 2, the coordinate string only holds x, resp. x,y values.

count: total number of coordinates to read. This should be a multiple of 3. The default is to read all the coordinates in the string. count can be used to force an error condition if the string does not contain the expected number of values.

The return value is Coords object.

classmethod fromfile(clas, fil, **kargs)

Read a Coords from file.

This convenience function uses the numpy fromfile function to read the coordinates from file. You just have to make sure that the coordinates are read in order (X,Y,Z) for subsequent points, and that the total number of coordinates read is a multiple of 3.

interpolate(X, div)

Create interpolations between two Coords.

Parameters:

  • X: a Coords with same shape as self.
  • div: a list of floating point values, or an int. If an int is specified, a list with (div+1) values for div is created by dividing the interval [0..1] into div equal distances.

Returns:

A Coords with an extra (first) axis, containing the concatenation of the interpolations of self and X at all values in div. Its shape is (n,) + self.shape, where n is the number of values in div.

An interpolation of F and G at value v is a Coords H where each coordinate Hijk is obtained from: Fijk = Fijk + v * (Gijk-Fijk). Thus, X.interpolate(Y,[0.,0.5,1.0]) will contain all points of X and Y and all points with mean coordinates between those of X and Y.

F.interpolate(G,n) is equivalent with F.interpolate(G,arange(0,n+1)/float(n))

rot(angle, axis=2, around=None)

Return a copy rotated over angle around axis.

The angle is specified in degrees. The axis is either one of (0,1,2) designating the global axes, or a vector specifying an axis through the origin. If no axis is specified, rotation is around the 2(z)-axis. This is convenient for working on 2D-structures.

As a convenience, the user may also specify a 3x3 rotation matrix, in which case the function rotate(mat) is equivalent to affine(mat).

All rotations are performed around the point [0,0,0], unless a rotation origin is specified in the argument ‘around’.

trl(dir, step=None, inplace=False)

Translate a Coords object.

Translates the Coords in the direction dir over a distance step * length(dir).

Parameters:

  • dir: specifies the direction and distance of the translation. It can be either
    • an axis number (0,1,2), specifying a unit vector in the direction of one of the coordinate axes.
    • a single translation vector,
    • an array of translation vectors, compatible with the Coords shape.
  • step: If specified, the translation vector specified by dir will be multiplied with this value. It is commonly used with unit dir vectors to set the translation distance.

Example:

>>> x = Coords([1.,1.,1.])
>>> print x.translate(1)
[ 1.  2.  1.]
>>> print x.translate(1,1.)
[ 1.  2.  1.]
>>> print x.translate([0,1,0])
[ 1.  2.  1.]
>>> print x.translate([0,2,0],0.5)
[ 1.  2.  1.]
rep(n, dir=0, step=None)

Replicate a Coords n times with fixed step in any direction.

Returns a Coords object with shape (n,) + self.shape, thus having an extra first axis. Each component along the axis 0 is equal to the previous component translated over (dir,step), where dir and step are interpreted just like in the translate() method. The first component along the axis 0 is identical to the original Coords.

class coords.BoundVectors

A collection of bound vectors in a 3D Cartesian space.

Parameters:

  • coords: a (...,2,3) shaped array of bound vectors defined by their initial and terminal points.
  • origins,`vectors`: (...,3) shaped arrays defining the initial points and vectors from initial to terminal points.

The default constructs a unit vector along the global x-axis.

origins()

Return the initial points of the BoundVectors.

heads()

Return the endpoints of the BoundVectors.

vectors()

Return the vectors of the BoundVectors.

points()

Returns the Coords object as a simple set of points.

This reshapes the array to a 2-dimensional array, flattening the structure of the points.

pshape()

Returns the shape of the Coords object.

This is the shape of the NumPy array with the last axis removed. The full shape of the Coords array can be obtained from its shape attribute.

ncoords()

Return the total number of points.

npoints()

Return the total number of points.

x()

Return the X-coordinates of all points.

Returns an array with all the X-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with

self[...,0]
y()

Return the Y-coordinates of all points.

Returns an array with all the Y-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with

self[...,1]
z()

Return the Z-coordinates of all points.

Returns an array with all the Z-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with

self[...,0]
bbox()

Return the bounding box of a set of points.

The bounding box is the smallest rectangular volume in the global coordinates, such that no point of the Coords are outside that volume.

Returns:

A Coords object with shape(2,3): the first point contains the minimal coordinates, the second has the maximal ones.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).bbox()
[[ 0.  0.  0.]
 [ 3.  3.  0.]]
center()

Return the center of the Coords.

The center of a Coords is the center of its bbox(). The return value is a (3,) shaped Coords object.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).center()
[ 1.5  1.5  0. ]

See also: centroid()

average(wts=None, axis=0)

Return a (weighted) average of the Coords.

The average of a Coords is a Coords with one axis less than the original, obtained by averaging all the points along that axis. The weights array can either be 1-D (in which case its length must be the size along the given axis) or of the same shape as a. Weights can be specified as a 1-D array with the length of that axis, or as an array with the same shape as the Coords. The sum of the weights (along the specified axis if not 1-D) will generally be equal to 1.0. If wts=None, then all points are assumed to have a weight equal to one divided by the length of the specified axis.

Example:

>>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],                  [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average()
[[ 2.  0.  0.]
 [ 3.  0.  0.]
 [ 4.  0.  0.]]
>>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],                  [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average(axis=1)
[[ 1.  0.  0.]
 [ 5.  0.  0.]]
>>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],                  [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average(wts=[0.5,0.25,0.25],axis=1)
[[ 0.75  0.    0.  ]
 [ 4.75  0.    0.  ]]
centroid()

Return the centroid of the Coords.

The centroid of a Coords is the point whose coordinates are the mean values of all points. The return value is a (3,) shaped Coords object.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).centroid()
[ 1.  1.  0.]

See also: center()

sizes()

Return the sizes of the Coords.

Return an array with the length of the bbox along the 3 axes.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).sizes()
[ 3.  3.  0.]
dsize()

Return an estimate of the global size of the Coords.

This estimate is the length of the diagonal of the bbox().

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).dsize()
4.24264
bsphere()

Return the diameter of the bounding sphere of the Coords.

The bounding sphere is the smallest sphere with center in the center() of the Coords, and such that no points of the Coords are lying outside the sphere.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).bsphere()
2.12132
inertia(mass=None)

Return inertia related quantities of the Coords.

This returns the center of gravity, the principal axes of inertia, the principal moments of inertia and the inertia tensor.

distanceFromPlane(p, n)

Return the distance of all points from the plane (p,n).

p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components.

The return value is a float array with shape self.pshape() with the distance of each point to the plane through p and having normal n. Distance values are positive if the point is on the side of the plane indicated by the positive normal.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromPlane([0.,0.,0.],[1.,0.,0.])
[[ 0.  3.  0.]]
distanceFromLine(p, n)

Return the distance of all points from the line (p,n).

p,n are (1,3) or (npts,3) arrays defining 1 or npts lines p is a point on the line specified by 3 coordinates. n is a vector specifying the direction of the line through p.

The return value is a [...] shaped array with the distance of each point to the line through p with direction n. All distance values are positive or zero.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromLine([0.,0.,0.],[1.,0.,0.])
[[ 0.  0.  3.]]
distanceFromPoint(p)

Return the distance of all points from the point p.

p is a single point specified by 3 coordinates.

The return value is a [...] shaped array with the distance of each point to point p. All distance values are positive or zero. Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromPoint([0.,0.,0.])
[[ 0.  3.  3.]]
closestToPoint(p)

Return the point closest to point p.

directionalSize(n, p=None, _points=False)

Return the extreme distances from the plane p,n.

The direction n can be specified by a 3 component vector or by a single integer 0..2 designing one of the coordinate axes.

p is any point in space. If not specified, it is taken as the center() of the Coords.

The return value is a tuple of two float values specifying the extreme distances from the plane p,n.

directionalExtremes(n, p=None)

Return extremal planes in the direction n.

n and p have the same meaning as in directionalSize.

The return value is a list of two points on the line (p,n), such that the planes with normal n through these points define the extremal planes of the Coords.

directionalWidth(n)

Return the width of a Coords in the given direction.

The direction can be specified by a 3 component vector or by a single integer 0..2 designating one of the coordinate axes.

The return value is the thickness of the object in the direction n.

test(dir=0, min=None, max=None, atol=0.0)

Flag points having coordinates between min and max.

Tests the position of the points of the Coords with respect to one or two planes. This method is very convenient in clipping a Coords in a specified direction. In most cases the clipping direction is one of the global cooordinate axes, but a general direction may be used as well.

Parameters:

  • dir: either a global axis number (0, 1 or 2) or a direction vector consisting of 3 floats. It specifies the direction in which the distances are measured. Default is the 0 (or x) direction.
  • min,`max`: position of the minimum and maximum clipping planes. If dir was specified as an integer (0,1,2), this is a single float value corresponding with the coordinate in that axis direction. Else, it is a point in the clipping plane with normal direction dir. One of the two clipping planes may be left unspecified.
Returns: a 1D integer array with same length as the number of points.
For each point the value is 1 (True) if the point is above the minimum clipping plane and below the maximum clipping plane, or 0 (False) otherwise. An unspecified clipping plane corresponds with an infinitely low or high value. The return value can directly be used as an index to obtain a Coords with the points satisfying the test (or not). See the examples below.

Example:

>>> x = Coords([[0.,0.],[1.,0.],[0.,1.],[0.,2.]])
>>> print x.test(min=0.5)
[False  True False False]
>>> t = x.test(dir=1,min=0.5,max=1.5)
>>> print x[t]
[[ 0.  1.  0.]]
>>> print x[~t]
[[ 0.  0.  0.]
 [ 1.  0.  0.]
 [ 0.  2.  0.]]
fprint(fmt='%10.3e %10.3e %10.3e')

Formatted printing of a Coords object.

The supplied format should contain 3 formatting sequences for the three coordinates of a point.

set(f)

Set the coordinates from those in the given array.

scale(scale, dir=None, center=None, inplace=False)

Return a copy scaled with scale[i] in direction i.

The scale should be a list of 3 scaling factors for the 3 axis directions, or a single scaling factor. In the latter case, dir (a single axis number or a list) may be given to specify the direction(s) to scale. The default is to produce a homothetic scaling. The center of the scaling, if not specified, is the global origin. If a center is specified, the result is equivalent to:

self.translate(-center).scale(scale,dir).translate(center)

Example:

>>> print Coords([1.,1.,1.]).scale(2)
[ 2.  2.  2.]
>>> print Coords([1.,1.,1.]).scale([2,3,4])
[ 2.  3.  4.]
translate(dir, step=None, inplace=False)

Translate a Coords object.

Translates the Coords in the direction dir over a distance step * length(dir).

Parameters:

  • dir: specifies the direction and distance of the translation. It can be either
    • an axis number (0,1,2), specifying a unit vector in the direction of one of the coordinate axes.
    • a single translation vector,
    • an array of translation vectors, compatible with the Coords shape.
  • step: If specified, the translation vector specified by dir will be multiplied with this value. It is commonly used with unit dir vectors to set the translation distance.

Example:

>>> x = Coords([1.,1.,1.])
>>> print x.translate(1)
[ 1.  2.  1.]
>>> print x.translate(1,1.)
[ 1.  2.  1.]
>>> print x.translate([0,1,0])
[ 1.  2.  1.]
>>> print x.translate([0,2,0],0.5)
[ 1.  2.  1.]
trl(dir, step=None, inplace=False)

Translate a Coords object.

Translates the Coords in the direction dir over a distance step * length(dir).

Parameters:

  • dir: specifies the direction and distance of the translation. It can be either
    • an axis number (0,1,2), specifying a unit vector in the direction of one of the coordinate axes.
    • a single translation vector,
    • an array of translation vectors, compatible with the Coords shape.
  • step: If specified, the translation vector specified by dir will be multiplied with this value. It is commonly used with unit dir vectors to set the translation distance.

Example:

>>> x = Coords([1.,1.,1.])
>>> print x.translate(1)
[ 1.  2.  1.]
>>> print x.translate(1,1.)
[ 1.  2.  1.]
>>> print x.translate([0,1,0])
[ 1.  2.  1.]
>>> print x.translate([0,2,0],0.5)
[ 1.  2.  1.]
centered()

Return a centered copy of the Coords.

Returns a Coords which is a translation thus that the center coincides with the origin. This is equivalent with:

self.trl(-self.center())
align(alignment='---')

Align the Coords along the global axes.

Alignment involves a translation such that the bounding box of the Coords object becomes aligned on the origin of the global axes. The requested alignment is determined by a string of three characters, one for each of the coordinate axes. The character determines how the structure is aligned in the corresponding direction:

  • ‘-‘: aligned on the minimal value of the bounding box,
  • ‘+’: aligned on the maximal value of the bounding box,
  • ‘0’: aligned on the middle value of the bounding box.

Any other value will make the alignment in that direction unchanged.

The default alignment string ‘—’ results in a translation which puts all the points in the octant with all positive coordinate values. A string ‘000’ will center the object around the origin, just like the (slightly faster) centered() method, which is .

rot(angle, axis=2, around=None)

Return a copy rotated over angle around axis.

The angle is specified in degrees. The axis is either one of (0,1,2) designating the global axes, or a vector specifying an axis through the origin. If no axis is specified, rotation is around the 2(z)-axis. This is convenient for working on 2D-structures.

As a convenience, the user may also specify a 3x3 rotation matrix, in which case the function rotate(mat) is equivalent to affine(mat).

All rotations are performed around the point [0,0,0], unless a rotation origin is specified in the argument ‘around’.

rotate(angle, axis=2, around=None)

Return a copy rotated over angle around axis.

The angle is specified in degrees. The axis is either one of (0,1,2) designating the global axes, or a vector specifying an axis through the origin. If no axis is specified, rotation is around the 2(z)-axis. This is convenient for working on 2D-structures.

As a convenience, the user may also specify a 3x3 rotation matrix, in which case the function rotate(mat) is equivalent to affine(mat).

All rotations are performed around the point [0,0,0], unless a rotation origin is specified in the argument ‘around’.

shear(dir, dir1, skew, inplace=False)

Return a copy skewed in the direction dir of plane (dir,dir1).

The coordinate dir is replaced with (dir + skew * dir1).

reflect(dir=0, pos=0.0, inplace=False)

Reflect the coordinates in direction dir against plane at pos.

Parameters:

  • dir: int: direction of the reflection (default 0)
  • pos: float: offset of the mirror plane from origin (default 0.0)
  • inplace: boolean: change the coordinates inplace (default False)
affine(mat, vec=None)

Returns a general affine transform of the Coords object.

mat: a 3x3 float matrix

vec: a length 3 list or array of floats

The returned object has coordinates given by self * mat + vec.

position(x, y)

Position an object so that points x are aligned with y.

Parameters as for arraytools.trfMatrix

cylindrical(dir=[0, 1, 2], scale=[1.0, 1.0, 1.0], angle_spec=0.017453292519943295)

Converts from cylindrical to cartesian after scaling.

dir specifies which coordinates are interpreted as resp. distance(r), angle(theta) and height(z). Default order is [r,theta,z]. scale will scale the coordinate values prior to the transformation. (scale is given in order r,theta,z). The resulting angle is interpreted in degrees.

toCylindrical(dir=[0, 1, 2], angle_spec=0.017453292519943295)

Converts from cartesian to cylindrical coordinates.

dir specifies which coordinates axes are parallel to respectively the cylindrical axes distance(r), angle(theta) and height(z). Default order is [x,y,z]. The angle value is given in degrees.

spherical(dir=[0, 1, 2], scale=[1.0, 1.0, 1.0], angle_spec=0.017453292519943295, colat=False)

Converts from spherical to cartesian after scaling.

  • dir specifies which coordinates are interpreted as resp. longitude(theta), latitude(phi) and distance(r).
  • scale will scale the coordinate values prior to the transformation.

Angles are interpreted in degrees. Latitude, i.e. the elevation angle, is measured from equator in direction of north pole(90). South pole is -90.

If colat=True, the third coordinate is the colatitude (90-lat) instead.

superSpherical(n=1.0, e=1.0, k=0.0, dir=[0, 1, 2], scale=[1.0, 1.0, 1.0], angle_spec=0.017453292519943295, colat=False)

Performs a superspherical transformation.

superSpherical is much like spherical, but adds some extra parameters to enable the creation of virtually any surface.

Just like with spherical(), the input coordinates are interpreted as the longitude, latitude and distance in a spherical coordinate system.

dir specifies which coordinates are interpreted as resp. longitude(theta), latitude(phi) and distance(r). Angles are then interpreted in degrees. Latitude, i.e. the elevation angle, is measured from equator in direction of north pole(90). South pole is -90. If colat=True, the third coordinate is the colatitude (90-lat) instead.

scale will scale the coordinate values prior to the transformation.

The n and e parameters define exponential transformations of the north_south (latitude), resp. the east_west (longitude) coordinates. Default values of 1 result in a circle.

k adds ‘eggness’ to the shape: a difference between the northern and southern hemisphere. Values > 0 enlarge the southern hemishpere and shrink the northern.

toSpherical(dir=[0, 1, 2], angle_spec=0.017453292519943295)

Converts from cartesian to spherical coordinates.

dir specifies which coordinates axes are parallel to respectively the spherical axes distance(r), longitude(theta) and latitude(phi). Latitude is the elevation angle measured from equator in direction of north pole(90). South pole is -90. Default order is [0,1,2], thus the equator plane is the (x,y)-plane.

The returned angle values are given in degrees.

bump1(dir, a, func, dist)

Return a Coords with a one-dimensional bump.

  • dir specifies the axis of the modified coordinates;
  • a is the point that forces the bumping;
  • dist specifies the direction in which the distance is measured;
  • func is a function that calculates the bump intensity from distance and should be such that func(0) != 0.
bump2(dir, a, func)

Return a Coords with a two-dimensional bump.

dir specifies the axis of the modified coordinates; a is the point that forces the bumping; func is a function that calculates the bump intensity from distance !! func(0) should be different from 0.

bump(dir, a, func, dist=None)

Return a Coords with a bump.

A bump is a modification of a set of coordinates by a non-matching point. It can produce various effects, but one of the most common uses is to force a surface to be indented by some point.

dir specifies the axis of the modified coordinates; a is the point that forces the bumping; func is a function that calculates the bump intensity from distance (!! func(0) should be different from 0) dist is the direction in which the distance is measured : this can be one of the axes, or a list of one or more axes. If only 1 axis is specified, the effect is like function bump1 If 2 axes are specified, the effect is like bump2 This function can take 3 axes however. Default value is the set of 3 axes minus the direction of modification. This function is then equivalent to bump2.

flare(xf, f, dir=[0, 2], end=0, exp=1.0)

Create a flare at the end of a Coords block.

The flare extends over a distance xf at the start (end=0) or end (end=1) in direction dir[0] of the coords block, and has a maximum amplitude of f in the dir[1] direction.

map(func)

Return a Coords mapped by a 3-D function.

This is one of the versatile mapping functions. func is a numerical function which takes three arguments and produces a list of three output values. The coordinates [x,y,z] will be replaced by func(x,y,z). The function must be applicable to arrays, so it should only include numerical operations and functions understood by the numpy module. This method is one of several mapping methods. See also map1 and mapd.

Example:

>>> print Coords([[1.,1.,1.]]).map(lambda x,y,z: [2*x,3*y,4*z])
[[ 2.  3.  4.]]
map1(dir, func, x=None)

Return a Coords where coordinate i is mapped by a 1-D function.

func is a numerical function which takes one argument and produces one result. The coordinate dir will be replaced by func(coord[x]). If no x is specified, x is taken equal to dir. The function must be applicable on arrays, so it should only include numerical operations and functions understood by the numpy module. This method is one of several mapping methods. See also map and mapd.

mapd(dir, func, point, dist=None)

Maps one coordinate by a function of the distance to a point.

func a numerical function which takes one argument and produces one result. The coordinate dir will be replaced by func(d), where d is calculated as the distance to point. The function must be applicable on arrays, so it should only include numerical operations and functions understood by the numpy module. By default, the distance d is calculated in 3-D, but one can specify a limited set of axes to calculate a 2-D or 1-D distance. This method is one of several mapping methods. See also map3() and map1().

Example:

E.mapd(2,lambda d:sqrt(10**2-d**2),f.center(),[0,1])

maps E on a sphere with radius 10.

egg(k)

Maps the coordinates to an egg-shape

replace(i, j, other=None)

Replace the coordinates along the axes i by those along j.

i and j are lists of axis numbers or single axis numbers. replace ([0,1,2],[1,2,0]) will roll the axes by 1. replace ([0,1],[1,0]) will swap axes 0 and 1. An optionally third argument may specify another Coords object to take the coordinates from. It should have the same dimensions.

swapAxes(i, j)

Swap coordinate axes i and j.

Beware! This is different from numpy’s swapaxes() method !

rollAxes(n=1)

Roll the axes over the given amount.

Default is 1, thus axis 0 becomes the new 1 axis, 1 becomes 2 and 2 becomes 0.

projectOnPlane(n=2, P=[0.0, 0.0, 0.0])

Project a Coords on a plane (or planes).

Parameters:

  • n: the normal direction to the plane. It can be specified either by a list of three floats, or by a single integer (0, 1 or 2) to use one of the global axes.
  • P: a point on the plane, by default the global origin. If an int, the plane is the coordinate plane perpendicular to the
..note: For planes parallel to a coordinate plane, it is far more
efficient to specify the normal by an axis number than by a three component vector.

Returns: a Coords with same shape as original, with all the points projected on the specified plane(s).

projectOnSphere(radius=1.0, center=[0.0, 0.0, 0.0])

Project Coords on a sphere.

The default sphere is a unit sphere at the origin. The center of the sphere should not be part of the Coords.

projectOnCylinder(radius=1.0, dir=0, center=[0.0, 0.0, 0.0])

Project Coords on a cylinder with axis parallel to a global axis.

The default cylinder has its axis along the x-axis and a unit radius. No points of the Coords should belong to the axis..

projectOnSurface(S, n, ignore_errors=False)

Project the Coords on a triangulated surface.

The points of the Coords are projected in the direction of the vector n onto the surface S.

Parameters:

  • S: TriSurface: any triangulated surface
  • n: int or vector: specifies the direction of the projection
  • ignore_errors: if True, projective lines not cutting the surface will result in NaN values. The default is to raise an error.

If successful, a Coords with the same structure as the input is returned.

isopar(eltype, coords, oldcoords)

Perform an isoparametric transformation on a Coords.

This is a convenience method to transform a Coords object through an isoparametric transformation. It is equivalent to:

Isopar(eltype,coords,oldcoords).transform(self)

See plugins.isopar for more details.

transformCS(currentCS, initialCS=None)

Perform a CoordinateSystem transformation on the Coords.

This method transforms the Coords object by the transformation that turns the initial CoordinateSystem into the currentCoordinateSystem.

currentCS and initialCS are CoordinateSystem or (4,3) shaped Coords instances. If initialCS is None, the global (x,y,z) axes are used.

E.g. the default initialCS and currentCS equal to:

 0.  1.  0.
-1.  0.  0.
 0.  0.  1.
 0.  0.  0.

result in a rotation of 90 degrees around the z-axis.

This is a convenience function equivalent to:

self.isopar('tet4',currentCS,initialCS)
addNoise(rsize=0.1, asize=0.0)

Add random noise to a Coords.

A random amount is added to eacho individual coordinate in the Coords. The difference of any coordinate from its original value will not be maximally asize + rsize * self.sizes().max(). The default is to set it to 0.1 times the geometrical size of the structure.

rep(n, dir=0, step=None)

Replicate a Coords n times with fixed step in any direction.

Returns a Coords object with shape (n,) + self.shape, thus having an extra first axis. Each component along the axis 0 is equal to the previous component translated over (dir,step), where dir and step are interpreted just like in the translate() method. The first component along the axis 0 is identical to the original Coords.

replicate(n, dir=0, step=None)

Replicate a Coords n times with fixed step in any direction.

Returns a Coords object with shape (n,) + self.shape, thus having an extra first axis. Each component along the axis 0 is equal to the previous component translated over (dir,step), where dir and step are interpreted just like in the translate() method. The first component along the axis 0 is identical to the original Coords.

split()

Split the coordinate array in blocks along first axis.

The result is a sequence of arrays with shape self.shape[1:]. Raises an error if self.ndim < 2.

fuse(nodesperbox=1, shift=0.5, rtol=1e-05, atol=1e-05, repeat=True)

Find (almost) identical nodes and return a compressed set.

This method finds the points that are very close and replaces them with a single point. The return value is a tuple of two arrays:

  • the unique points as a Coords object with shape (npoints,3)
  • an integer (nnod) array holding an index in the unique coordinates array for each of the original nodes. This index will have the same shape as the pshape() of the coords array.

The procedure works by first dividing the 3D space in a number of equally sized boxes, with a mean population of nodesperbox. The boxes are numbered in the 3 directions and a unique integer scalar is computed, that is then used to sort the nodes. Then only nodes inside the same box are compared on almost equal coordinates, using the numpy allclose() function. Two coordinates are considered close if they are within a relative tolerance rtol or absolute tolerance atol. See numpy for detail. The default atol is set larger than in numpy, because pyformex typically runs with single precision. Close nodes are replaced by a single one.

Running the procedure once does not guarantee to find all close nodes: two close nodes might be in adjacent boxes. The performance hit for testing adjacent boxes is rather high, and the probability of separating two close nodes with the computed box limits is very small. Therefore, the most sensible way is to run the procedure twice, with a different shift value (they should differ more than the tolerance). Specifying repeat=True will automatically do this.

match(coords, **kargs)

Match points form another Coords object.

This method finds the points from coords that coincide with (or are very close to) points of self.

Parameters:

  • coords: a Coords object
  • **kargs: keyword arguments that you want to pass to the fuse() method.

This method works by concatenating the serialized point sets of both Coords and then fusing them.

Returns:

  • matches: an Int array with shape (nmatches,2)
  • coords: a Coords with the fused coordinate set
  • index: an index with the position of each of the serialized points of the concatenation in the fused coordinate set. To find the index of the points of the orginal coordinate sets, split this index at the position self.npoints() and reshape the resulting parts to self.pshape(), resp. coords.pshape().
append(coords)

Append coords to a Coords object.

The appended coords should have matching dimensions in all but the first axis.

Returns the concatenated Coords object, without changing the current.

This is comparable to numpy.append(), but the result is a Coords object, the default axis is the first one instead of the last, and it is a method rather than a function.

classmethod concatenate(clas, L, axis=0)

Concatenate a list of Coords object.

All Coords object in the list L should have the same shape except for the length of the specified axis. This function is equivalent to the numpy concatenate, but makes sure the result is a Coords object,and the default axis is the first one instead of the last.

classmethod fromstring(clas, fil, sep=' ', ndim=3, count=-1)

Create a Coords object with data from a string.

This convenience function uses the numpy.fromstring() function to read coordinates from a string.

fil: a string containing a single sequence of float numbers separated by whitespace and a possible separator string.

sep: the separator used between the coordinates. If not a space, all extra whitespace is ignored.

ndim: number of coordinates per point. Should be 1, 2 or 3 (default). If 1, resp. 2, the coordinate string only holds x, resp. x,y values.

count: total number of coordinates to read. This should be a multiple of 3. The default is to read all the coordinates in the string. count can be used to force an error condition if the string does not contain the expected number of values.

The return value is Coords object.

classmethod fromfile(clas, fil, **kargs)

Read a Coords from file.

This convenience function uses the numpy fromfile function to read the coordinates from file. You just have to make sure that the coordinates are read in order (X,Y,Z) for subsequent points, and that the total number of coordinates read is a multiple of 3.

interpolate(X, div)

Create interpolations between two Coords.

Parameters:

  • X: a Coords with same shape as self.
  • div: a list of floating point values, or an int. If an int is specified, a list with (div+1) values for div is created by dividing the interval [0..1] into div equal distances.

Returns:

A Coords with an extra (first) axis, containing the concatenation of the interpolations of self and X at all values in div. Its shape is (n,) + self.shape, where n is the number of values in div.

An interpolation of F and G at value v is a Coords H where each coordinate Hijk is obtained from: Fijk = Fijk + v * (Gijk-Fijk). Thus, X.interpolate(Y,[0.,0.5,1.0]) will contain all points of X and Y and all points with mean coordinates between those of X and Y.

F.interpolate(G,n) is equivalent with F.interpolate(G,arange(0,n+1)/float(n))

class coords.CoordinateSystem

A CoordinateSystem defines a coordinate system in 3D space.

The coordinate system is defined by and stored as a set of four points: three endpoints of the unit vectors along the axes at the origin, and the origin itself as fourth point.

The constructor takes a (4,3) array as input. The default constructs the standard global Cartesian axes system:

1. 0. 0. 0. 1. 0. 0. 0. 1. 0. 0. 0.
origin()

Return the origin of the CoordinateSystem.

axes()

Return the axes of the CoordinateSystem.

points()

Returns the Coords object as a simple set of points.

This reshapes the array to a 2-dimensional array, flattening the structure of the points.

pshape()

Returns the shape of the Coords object.

This is the shape of the NumPy array with the last axis removed. The full shape of the Coords array can be obtained from its shape attribute.

ncoords()

Return the total number of points.

npoints()

Return the total number of points.

x()

Return the X-coordinates of all points.

Returns an array with all the X-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with

self[...,0]
y()

Return the Y-coordinates of all points.

Returns an array with all the Y-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with

self[...,1]
z()

Return the Z-coordinates of all points.

Returns an array with all the Z-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with

self[...,0]
bbox()

Return the bounding box of a set of points.

The bounding box is the smallest rectangular volume in the global coordinates, such that no point of the Coords are outside that volume.

Returns:

A Coords object with shape(2,3): the first point contains the minimal coordinates, the second has the maximal ones.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).bbox()
[[ 0.  0.  0.]
 [ 3.  3.  0.]]
center()

Return the center of the Coords.

The center of a Coords is the center of its bbox(). The return value is a (3,) shaped Coords object.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).center()
[ 1.5  1.5  0. ]

See also: centroid()

average(wts=None, axis=0)

Return a (weighted) average of the Coords.

The average of a Coords is a Coords with one axis less than the original, obtained by averaging all the points along that axis. The weights array can either be 1-D (in which case its length must be the size along the given axis) or of the same shape as a. Weights can be specified as a 1-D array with the length of that axis, or as an array with the same shape as the Coords. The sum of the weights (along the specified axis if not 1-D) will generally be equal to 1.0. If wts=None, then all points are assumed to have a weight equal to one divided by the length of the specified axis.

Example:

>>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],                  [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average()
[[ 2.  0.  0.]
 [ 3.  0.  0.]
 [ 4.  0.  0.]]
>>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],                  [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average(axis=1)
[[ 1.  0.  0.]
 [ 5.  0.  0.]]
>>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],                  [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average(wts=[0.5,0.25,0.25],axis=1)
[[ 0.75  0.    0.  ]
 [ 4.75  0.    0.  ]]
centroid()

Return the centroid of the Coords.

The centroid of a Coords is the point whose coordinates are the mean values of all points. The return value is a (3,) shaped Coords object.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).centroid()
[ 1.  1.  0.]

See also: center()

sizes()

Return the sizes of the Coords.

Return an array with the length of the bbox along the 3 axes.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).sizes()
[ 3.  3.  0.]
dsize()

Return an estimate of the global size of the Coords.

This estimate is the length of the diagonal of the bbox().

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).dsize()
4.24264
bsphere()

Return the diameter of the bounding sphere of the Coords.

The bounding sphere is the smallest sphere with center in the center() of the Coords, and such that no points of the Coords are lying outside the sphere.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).bsphere()
2.12132
inertia(mass=None)

Return inertia related quantities of the Coords.

This returns the center of gravity, the principal axes of inertia, the principal moments of inertia and the inertia tensor.

distanceFromPlane(p, n)

Return the distance of all points from the plane (p,n).

p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components.

The return value is a float array with shape self.pshape() with the distance of each point to the plane through p and having normal n. Distance values are positive if the point is on the side of the plane indicated by the positive normal.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromPlane([0.,0.,0.],[1.,0.,0.])
[[ 0.  3.  0.]]
distanceFromLine(p, n)

Return the distance of all points from the line (p,n).

p,n are (1,3) or (npts,3) arrays defining 1 or npts lines p is a point on the line specified by 3 coordinates. n is a vector specifying the direction of the line through p.

The return value is a [...] shaped array with the distance of each point to the line through p with direction n. All distance values are positive or zero.

Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromLine([0.,0.,0.],[1.,0.,0.])
[[ 0.  0.  3.]]
distanceFromPoint(p)

Return the distance of all points from the point p.

p is a single point specified by 3 coordinates.

The return value is a [...] shaped array with the distance of each point to point p. All distance values are positive or zero. Example:

>>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromPoint([0.,0.,0.])
[[ 0.  3.  3.]]
closestToPoint(p)

Return the point closest to point p.

directionalSize(n, p=None, _points=False)

Return the extreme distances from the plane p,n.

The direction n can be specified by a 3 component vector or by a single integer 0..2 designing one of the coordinate axes.

p is any point in space. If not specified, it is taken as the center() of the Coords.

The return value is a tuple of two float values specifying the extreme distances from the plane p,n.

directionalExtremes(n, p=None)

Return extremal planes in the direction n.

n and p have the same meaning as in directionalSize.

The return value is a list of two points on the line (p,n), such that the planes with normal n through these points define the extremal planes of the Coords.

directionalWidth(n)

Return the width of a Coords in the given direction.

The direction can be specified by a 3 component vector or by a single integer 0..2 designating one of the coordinate axes.

The return value is the thickness of the object in the direction n.

test(dir=0, min=None, max=None, atol=0.0)

Flag points having coordinates between min and max.

Tests the position of the points of the Coords with respect to one or two planes. This method is very convenient in clipping a Coords in a specified direction. In most cases the clipping direction is one of the global cooordinate axes, but a general direction may be used as well.

Parameters:

  • dir: either a global axis number (0, 1 or 2) or a direction vector consisting of 3 floats. It specifies the direction in which the distances are measured. Default is the 0 (or x) direction.
  • min,`max`: position of the minimum and maximum clipping planes. If dir was specified as an integer (0,1,2), this is a single float value corresponding with the coordinate in that axis direction. Else, it is a point in the clipping plane with normal direction dir. One of the two clipping planes may be left unspecified.
Returns: a 1D integer array with same length as the number of points.
For each point the value is 1 (True) if the point is above the minimum clipping plane and below the maximum clipping plane, or 0 (False) otherwise. An unspecified clipping plane corresponds with an infinitely low or high value. The return value can directly be used as an index to obtain a Coords with the points satisfying the test (or not). See the examples below.

Example:

>>> x = Coords([[0.,0.],[1.,0.],[0.,1.],[0.,2.]])
>>> print x.test(min=0.5)
[False  True False False]
>>> t = x.test(dir=1,min=0.5,max=1.5)
>>> print x[t]
[[ 0.  1.  0.]]
>>> print x[~t]
[[ 0.  0.  0.]
 [ 1.  0.  0.]
 [ 0.  2.  0.]]
fprint(fmt='%10.3e %10.3e %10.3e')

Formatted printing of a Coords object.

The supplied format should contain 3 formatting sequences for the three coordinates of a point.

set(f)

Set the coordinates from those in the given array.

scale(scale, dir=None, center=None, inplace=False)

Return a copy scaled with scale[i] in direction i.

The scale should be a list of 3 scaling factors for the 3 axis directions, or a single scaling factor. In the latter case, dir (a single axis number or a list) may be given to specify the direction(s) to scale. The default is to produce a homothetic scaling. The center of the scaling, if not specified, is the global origin. If a center is specified, the result is equivalent to:

self.translate(-center).scale(scale,dir).translate(center)

Example:

>>> print Coords([1.,1.,1.]).scale(2)
[ 2.  2.  2.]
>>> print Coords([1.,1.,1.]).scale([2,3,4])
[ 2.  3.  4.]
translate(dir, step=None, inplace=False)

Translate a Coords object.

Translates the Coords in the direction dir over a distance step * length(dir).

Parameters:

  • dir: specifies the direction and distance of the translation. It can be either
    • an axis number (0,1,2), specifying a unit vector in the direction of one of the coordinate axes.
    • a single translation vector,
    • an array of translation vectors, compatible with the Coords shape.
  • step: If specified, the translation vector specified by dir will be multiplied with this value. It is commonly used with unit dir vectors to set the translation distance.

Example:

>>> x = Coords([1.,1.,1.])
>>> print x.translate(1)
[ 1.  2.  1.]
>>> print x.translate(1,1.)
[ 1.  2.  1.]
>>> print x.translate([0,1,0])
[ 1.  2.  1.]
>>> print x.translate([0,2,0],0.5)
[ 1.  2.  1.]
trl(dir, step=None, inplace=False)

Translate a Coords object.

Translates the Coords in the direction dir over a distance step * length(dir).

Parameters:

  • dir: specifies the direction and distance of the translation. It can be either
    • an axis number (0,1,2), specifying a unit vector in the direction of one of the coordinate axes.
    • a single translation vector,
    • an array of translation vectors, compatible with the Coords shape.
  • step: If specified, the translation vector specified by dir will be multiplied with this value. It is commonly used with unit dir vectors to set the translation distance.

Example:

>>> x = Coords([1.,1.,1.])
>>> print x.translate(1)
[ 1.  2.  1.]
>>> print x.translate(1,1.)
[ 1.  2.  1.]
>>> print x.translate([0,1,0])
[ 1.  2.  1.]
>>> print x.translate([0,2,0],0.5)
[ 1.  2.  1.]
centered()

Return a centered copy of the Coords.

Returns a Coords which is a translation thus that the center coincides with the origin. This is equivalent with:

self.trl(-self.center())
align(alignment='---')

Align the Coords along the global axes.

Alignment involves a translation such that the bounding box of the Coords object becomes aligned on the origin of the global axes. The requested alignment is determined by a string of three characters, one for each of the coordinate axes. The character determines how the structure is aligned in the corresponding direction:

  • ‘-‘: aligned on the minimal value of the bounding box,
  • ‘+’: aligned on the maximal value of the bounding box,
  • ‘0’: aligned on the middle value of the bounding box.

Any other value will make the alignment in that direction unchanged.

The default alignment string ‘—’ results in a translation which puts all the points in the octant with all positive coordinate values. A string ‘000’ will center the object around the origin, just like the (slightly faster) centered() method, which is .

rot(angle, axis=2, around=None)

Return a copy rotated over angle around axis.

The angle is specified in degrees. The axis is either one of (0,1,2) designating the global axes, or a vector specifying an axis through the origin. If no axis is specified, rotation is around the 2(z)-axis. This is convenient for working on 2D-structures.

As a convenience, the user may also specify a 3x3 rotation matrix, in which case the function rotate(mat) is equivalent to affine(mat).

All rotations are performed around the point [0,0,0], unless a rotation origin is specified in the argument ‘around’.

rotate(angle, axis=2, around=None)

Return a copy rotated over angle around axis.

The angle is specified in degrees. The axis is either one of (0,1,2) designating the global axes, or a vector specifying an axis through the origin. If no axis is specified, rotation is around the 2(z)-axis. This is convenient for working on 2D-structures.

As a convenience, the user may also specify a 3x3 rotation matrix, in which case the function rotate(mat) is equivalent to affine(mat).

All rotations are performed around the point [0,0,0], unless a rotation origin is specified in the argument ‘around’.

shear(dir, dir1, skew, inplace=False)

Return a copy skewed in the direction dir of plane (dir,dir1).

The coordinate dir is replaced with (dir + skew * dir1).

reflect(dir=0, pos=0.0, inplace=False)

Reflect the coordinates in direction dir against plane at pos.

Parameters:

  • dir: int: direction of the reflection (default 0)
  • pos: float: offset of the mirror plane from origin (default 0.0)
  • inplace: boolean: change the coordinates inplace (default False)
affine(mat, vec=None)

Returns a general affine transform of the Coords object.

mat: a 3x3 float matrix

vec: a length 3 list or array of floats

The returned object has coordinates given by self * mat + vec.

position(x, y)

Position an object so that points x are aligned with y.

Parameters as for arraytools.trfMatrix

cylindrical(dir=[0, 1, 2], scale=[1.0, 1.0, 1.0], angle_spec=0.017453292519943295)

Converts from cylindrical to cartesian after scaling.

dir specifies which coordinates are interpreted as resp. distance(r), angle(theta) and height(z). Default order is [r,theta,z]. scale will scale the coordinate values prior to the transformation. (scale is given in order r,theta,z). The resulting angle is interpreted in degrees.

toCylindrical(dir=[0, 1, 2], angle_spec=0.017453292519943295)

Converts from cartesian to cylindrical coordinates.

dir specifies which coordinates axes are parallel to respectively the cylindrical axes distance(r), angle(theta) and height(z). Default order is [x,y,z]. The angle value is given in degrees.

spherical(dir=[0, 1, 2], scale=[1.0, 1.0, 1.0], angle_spec=0.017453292519943295, colat=False)

Converts from spherical to cartesian after scaling.

  • dir specifies which coordinates are interpreted as resp. longitude(theta), latitude(phi) and distance(r).
  • scale will scale the coordinate values prior to the transformation.

Angles are interpreted in degrees. Latitude, i.e. the elevation angle, is measured from equator in direction of north pole(90). South pole is -90.

If colat=True, the third coordinate is the colatitude (90-lat) instead.

superSpherical(n=1.0, e=1.0, k=0.0, dir=[0, 1, 2], scale=[1.0, 1.0, 1.0], angle_spec=0.017453292519943295, colat=False)

Performs a superspherical transformation.

superSpherical is much like spherical, but adds some extra parameters to enable the creation of virtually any surface.

Just like with spherical(), the input coordinates are interpreted as the longitude, latitude and distance in a spherical coordinate system.

dir specifies which coordinates are interpreted as resp. longitude(theta), latitude(phi) and distance(r). Angles are then interpreted in degrees. Latitude, i.e. the elevation angle, is measured from equator in direction of north pole(90). South pole is -90. If colat=True, the third coordinate is the colatitude (90-lat) instead.

scale will scale the coordinate values prior to the transformation.

The n and e parameters define exponential transformations of the north_south (latitude), resp. the east_west (longitude) coordinates. Default values of 1 result in a circle.

k adds ‘eggness’ to the shape: a difference between the northern and southern hemisphere. Values > 0 enlarge the southern hemishpere and shrink the northern.

toSpherical(dir=[0, 1, 2], angle_spec=0.017453292519943295)

Converts from cartesian to spherical coordinates.

dir specifies which coordinates axes are parallel to respectively the spherical axes distance(r), longitude(theta) and latitude(phi). Latitude is the elevation angle measured from equator in direction of north pole(90). South pole is -90. Default order is [0,1,2], thus the equator plane is the (x,y)-plane.

The returned angle values are given in degrees.

bump1(dir, a, func, dist)

Return a Coords with a one-dimensional bump.

  • dir specifies the axis of the modified coordinates;
  • a is the point that forces the bumping;
  • dist specifies the direction in which the distance is measured;
  • func is a function that calculates the bump intensity from distance and should be such that func(0) != 0.
bump2(dir, a, func)

Return a Coords with a two-dimensional bump.

dir specifies the axis of the modified coordinates; a is the point that forces the bumping; func is a function that calculates the bump intensity from distance !! func(0) should be different from 0.

bump(dir, a, func, dist=None)

Return a Coords with a bump.

A bump is a modification of a set of coordinates by a non-matching point. It can produce various effects, but one of the most common uses is to force a surface to be indented by some point.

dir specifies the axis of the modified coordinates; a is the point that forces the bumping; func is a function that calculates the bump intensity from distance (!! func(0) should be different from 0) dist is the direction in which the distance is measured : this can be one of the axes, or a list of one or more axes. If only 1 axis is specified, the effect is like function bump1 If 2 axes are specified, the effect is like bump2 This function can take 3 axes however. Default value is the set of 3 axes minus the direction of modification. This function is then equivalent to bump2.

flare(xf, f, dir=[0, 2], end=0, exp=1.0)

Create a flare at the end of a Coords block.

The flare extends over a distance xf at the start (end=0) or end (end=1) in direction dir[0] of the coords block, and has a maximum amplitude of f in the dir[1] direction.

map(func)

Return a Coords mapped by a 3-D function.

This is one of the versatile mapping functions. func is a numerical function which takes three arguments and produces a list of three output values. The coordinates [x,y,z] will be replaced by func(x,y,z). The function must be applicable to arrays, so it should only include numerical operations and functions understood by the numpy module. This method is one of several mapping methods. See also map1 and mapd.

Example:

>>> print Coords([[1.,1.,1.]]).map(lambda x,y,z: [2*x,3*y,4*z])
[[ 2.  3.  4.]]
map1(dir, func, x=None)

Return a Coords where coordinate i is mapped by a 1-D function.

func is a numerical function which takes one argument and produces one result. The coordinate dir will be replaced by func(coord[x]). If no x is specified, x is taken equal to dir. The function must be applicable on arrays, so it should only include numerical operations and functions understood by the numpy module. This method is one of several mapping methods. See also map and mapd.

mapd(dir, func, point, dist=None)

Maps one coordinate by a function of the distance to a point.

func a numerical function which takes one argument and produces one result. The coordinate dir will be replaced by func(d), where d is calculated as the distance to point. The function must be applicable on arrays, so it should only include numerical operations and functions understood by the numpy module. By default, the distance d is calculated in 3-D, but one can specify a limited set of axes to calculate a 2-D or 1-D distance. This method is one of several mapping methods. See also map3() and map1().

Example:

E.mapd(2,lambda d:sqrt(10**2-d**2),f.center(),[0,1])

maps E on a sphere with radius 10.

egg(k)

Maps the coordinates to an egg-shape

replace(i, j, other=None)

Replace the coordinates along the axes i by those along j.

i and j are lists of axis numbers or single axis numbers. replace ([0,1,2],[1,2,0]) will roll the axes by 1. replace ([0,1],[1,0]) will swap axes 0 and 1. An optionally third argument may specify another Coords object to take the coordinates from. It should have the same dimensions.

swapAxes(i, j)

Swap coordinate axes i and j.

Beware! This is different from numpy’s swapaxes() method !

rollAxes(n=1)

Roll the axes over the given amount.

Default is 1, thus axis 0 becomes the new 1 axis, 1 becomes 2 and 2 becomes 0.

projectOnPlane(n=2, P=[0.0, 0.0, 0.0])

Project a Coords on a plane (or planes).

Parameters:

  • n: the normal direction to the plane. It can be specified either by a list of three floats, or by a single integer (0, 1 or 2) to use one of the global axes.
  • P: a point on the plane, by default the global origin. If an int, the plane is the coordinate plane perpendicular to the
..note: For planes parallel to a coordinate plane, it is far more
efficient to specify the normal by an axis number than by a three component vector.

Returns: a Coords with same shape as original, with all the points projected on the specified plane(s).

projectOnSphere(radius=1.0, center=[0.0, 0.0, 0.0])

Project Coords on a sphere.

The default sphere is a unit sphere at the origin. The center of the sphere should not be part of the Coords.

projectOnCylinder(radius=1.0, dir=0, center=[0.0, 0.0, 0.0])

Project Coords on a cylinder with axis parallel to a global axis.

The default cylinder has its axis along the x-axis and a unit radius. No points of the Coords should belong to the axis..

projectOnSurface(S, n, ignore_errors=False)

Project the Coords on a triangulated surface.

The points of the Coords are projected in the direction of the vector n onto the surface S.

Parameters:

  • S: TriSurface: any triangulated surface
  • n: int or vector: specifies the direction of the projection
  • ignore_errors: if True, projective lines not cutting the surface will result in NaN values. The default is to raise an error.

If successful, a Coords with the same structure as the input is returned.

isopar(eltype, coords, oldcoords)

Perform an isoparametric transformation on a Coords.

This is a convenience method to transform a Coords object through an isoparametric transformation. It is equivalent to:

Isopar(eltype,coords,oldcoords).transform(self)

See plugins.isopar for more details.

transformCS(currentCS, initialCS=None)

Perform a CoordinateSystem transformation on the Coords.

This method transforms the Coords object by the transformation that turns the initial CoordinateSystem into the currentCoordinateSystem.

currentCS and initialCS are CoordinateSystem or (4,3) shaped Coords instances. If initialCS is None, the global (x,y,z) axes are used.

E.g. the default initialCS and currentCS equal to:

 0.  1.  0.
-1.  0.  0.
 0.  0.  1.
 0.  0.  0.

result in a rotation of 90 degrees around the z-axis.

This is a convenience function equivalent to:

self.isopar('tet4',currentCS,initialCS)
addNoise(rsize=0.1, asize=0.0)

Add random noise to a Coords.

A random amount is added to eacho individual coordinate in the Coords. The difference of any coordinate from its original value will not be maximally asize + rsize * self.sizes().max(). The default is to set it to 0.1 times the geometrical size of the structure.

rep(n, dir=0, step=None)

Replicate a Coords n times with fixed step in any direction.

Returns a Coords object with shape (n,) + self.shape, thus having an extra first axis. Each component along the axis 0 is equal to the previous component translated over (dir,step), where dir and step are interpreted just like in the translate() method. The first component along the axis 0 is identical to the original Coords.

replicate(n, dir=0, step=None)

Replicate a Coords n times with fixed step in any direction.

Returns a Coords object with shape (n,) + self.shape, thus having an extra first axis. Each component along the axis 0 is equal to the previous component translated over (dir,step), where dir and step are interpreted just like in the translate() method. The first component along the axis 0 is identical to the original Coords.

split()

Split the coordinate array in blocks along first axis.

The result is a sequence of arrays with shape self.shape[1:]. Raises an error if self.ndim < 2.

fuse(nodesperbox=1, shift=0.5, rtol=1e-05, atol=1e-05, repeat=True)

Find (almost) identical nodes and return a compressed set.

This method finds the points that are very close and replaces them with a single point. The return value is a tuple of two arrays:

  • the unique points as a Coords object with shape (npoints,3)
  • an integer (nnod) array holding an index in the unique coordinates array for each of the original nodes. This index will have the same shape as the pshape() of the coords array.

The procedure works by first dividing the 3D space in a number of equally sized boxes, with a mean population of nodesperbox. The boxes are numbered in the 3 directions and a unique integer scalar is computed, that is then used to sort the nodes. Then only nodes inside the same box are compared on almost equal coordinates, using the numpy allclose() function. Two coordinates are considered close if they are within a relative tolerance rtol or absolute tolerance atol. See numpy for detail. The default atol is set larger than in numpy, because pyformex typically runs with single precision. Close nodes are replaced by a single one.

Running the procedure once does not guarantee to find all close nodes: two close nodes might be in adjacent boxes. The performance hit for testing adjacent boxes is rather high, and the probability of separating two close nodes with the computed box limits is very small. Therefore, the most sensible way is to run the procedure twice, with a different shift value (they should differ more than the tolerance). Specifying repeat=True will automatically do this.

match(coords, **kargs)

Match points form another Coords object.

This method finds the points from coords that coincide with (or are very close to) points of self.

Parameters:

  • coords: a Coords object
  • **kargs: keyword arguments that you want to pass to the fuse() method.

This method works by concatenating the serialized point sets of both Coords and then fusing them.

Returns:

  • matches: an Int array with shape (nmatches,2)
  • coords: a Coords with the fused coordinate set
  • index: an index with the position of each of the serialized points of the concatenation in the fused coordinate set. To find the index of the points of the orginal coordinate sets, split this index at the position self.npoints() and reshape the resulting parts to self.pshape(), resp. coords.pshape().
append(coords)

Append coords to a Coords object.

The appended coords should have matching dimensions in all but the first axis.

Returns the concatenated Coords object, without changing the current.

This is comparable to numpy.append(), but the result is a Coords object, the default axis is the first one instead of the last, and it is a method rather than a function.

classmethod concatenate(clas, L, axis=0)

Concatenate a list of Coords object.

All Coords object in the list L should have the same shape except for the length of the specified axis. This function is equivalent to the numpy concatenate, but makes sure the result is a Coords object,and the default axis is the first one instead of the last.

classmethod fromstring(clas, fil, sep=' ', ndim=3, count=-1)

Create a Coords object with data from a string.

This convenience function uses the numpy.fromstring() function to read coordinates from a string.

fil: a string containing a single sequence of float numbers separated by whitespace and a possible separator string.

sep: the separator used between the coordinates. If not a space, all extra whitespace is ignored.

ndim: number of coordinates per point. Should be 1, 2 or 3 (default). If 1, resp. 2, the coordinate string only holds x, resp. x,y values.

count: total number of coordinates to read. This should be a multiple of 3. The default is to read all the coordinates in the string. count can be used to force an error condition if the string does not contain the expected number of values.

The return value is Coords object.

classmethod fromfile(clas, fil, **kargs)

Read a Coords from file.

This convenience function uses the numpy fromfile function to read the coordinates from file. You just have to make sure that the coordinates are read in order (X,Y,Z) for subsequent points, and that the total number of coordinates read is a multiple of 3.

interpolate(X, div)

Create interpolations between two Coords.

Parameters:

  • X: a Coords with same shape as self.
  • div: a list of floating point values, or an int. If an int is specified, a list with (div+1) values for div is created by dividing the interval [0..1] into div equal distances.

Returns:

A Coords with an extra (first) axis, containing the concatenation of the interpolations of self and X at all values in div. Its shape is (n,) + self.shape, where n is the number of values in div.

An interpolation of F and G at value v is a Coords H where each coordinate Hijk is obtained from: Fijk = Fijk + v * (Gijk-Fijk). Thus, X.interpolate(Y,[0.,0.5,1.0]) will contain all points of X and Y and all points with mean coordinates between those of X and Y.

F.interpolate(G,n) is equivalent with F.interpolate(G,arange(0,n+1)/float(n))

Functions defined in module coords

coords.bbox(objects)

Compute the bounding box of a list of objects.

The bounding box of an object is the smallest rectangular cuboid in the global Cartesian coordinates, such that no points of the objects lie outside that cuboid. The resulting bounding box of the list of objects is the smallest bounding box that encloses all the objects in the list. Objects that do not have a bbox() method or whose bbox() method returns invalid values, are ignored.

Parameters:

  • objects: a list of objects (which should probably have the method bbox()).
Returns:
A Coords object with two points: the first contains the minimal coordinate values, the second has the maximal ones of the overall bounding box.

Example:

>>> from formex import *
>>> bbox([Coords([-1.,1.,0.]),Formex('l:5')])
Coords([[-1.,  0.,  0.],
       [ 1.,  1.,  0.]], dtype=float32)
coords.origin()

Return a single point with coordinates [0.,0.,0.].

Returns:
A Coords object with shape(3,) holding three zero coordinates.
coords.pattern(s, connect=True)

Return a series of points lying on a regular grid.

This function creates a series of points that lie on a regular grid with unit step. These points are created from a string input, interpreting each character as a code specifying how to move to the next point. The start position is always the origin (0.,0.,0.).

Currently the following codes are defined:

  • 0: goto origin (0.,0.,0.)
  • 1..8: move in the x,y plane
  • 9: remain at the same place (and duplicate the last point)
  • A..I: same as 1..9 plus step +1. in z-direction
  • a..i: same as 1..9 plus step -1. in z-direction
  • /: do not insert the next point

Any other character raises an error.

When looking at the x,y-plane with the x-axis to the right and the y-axis up, we have the following basic moves: 1 = East, 2 = North, 3 = West, 4 = South, 5 = NE, 6 = NW, 7 = SW, 8 = SE.

Adding 16 to the ordinal of the character causes an extra move of +1. in the z-direction. Adding 48 causes an extra move of -1. This means that ‘ABCDEFGHI’, resp. ‘abcdefghi’, correspond with ‘123456789’ with an extra z +/-= 1. This gives the following schema:

    z+=1             z unchanged            z -= 1

F    B    E          6    2    5         f    b    e 
     |                    |                   |     
     |                    |                   |     
C----I----A          3----9----1         c----i----a  
     |                    |                   |     
     |                    |                   |     
G    D    H          7    4    8         g    d    h

The special character ‘/’ can be put before any character to make the move without inserting the new point. You need to start the string with a ‘0’ or ‘9’ to include the origin in the output.

Returns a list with the generated points as integers.

Example:

>>> pattern('0123')
[(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
coords.xpattern(s, nplex=1)

Create a Coords object from a string pattern.

This is like pattern, but allows grouping the points into elements. First, the string is expanded to a list of points by calling pattern(s). Then the resulting list of points is transformed in a 2D table of points where each row has the length nplex.

If the number of points produced by s is not a multiple of nplex, an error is raised.

coords.sweepCoords(self, path, origin=[0.0, 0.0, 0.0], normal=0, upvector=2, avgdir=False, enddir=None, scalex=None, scaley=None)

Sweep a Coords object along a path, returning a series of copies.

origin and normal define the local path position and direction on the mesh.

At each point of the curve, a copy of the Coords object is created, with its origin in the curve’s point, and its normal along the curve’s direction. In case of a PolyLine, directions are pointing to the next point by default. If avgdir==True, average directions are taken at the intermediate points avgdir can also be an array like sequence of shape (N,3) to explicitely set the the directions for ALL the points of the path

Missing end directions can explicitely be set by enddir, and are by default taken along the last segment. enddir is a list of 2 array like values of shape (3). one of the two can also be an empty list If the curve is closed, endpoints are treated as any intermediate point, and the user should normally not specify enddir.

At each point of the curve, the original Coords object can be scaled in x and y direction by specifying scalex and scaley. The number of values specified in scalex and scaly should be equal to the number of points on the curve.

The return value is a sequence of the transformed Coords objects.

pyformex-0.8.6/pyformex/doc/html/ref/scriptMenu.html0000644000211500021150000005166511705104255022424 0ustar benebene00000000000000 28. scriptMenu — Menu with pyFormex scripts. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

27. imageViewer — A general image viewer

Next topic

29. toolbar — Toolbars for the pyFormex GUI.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

make[2]: Map ‘/home/bene/prj’ wordt binnengegaan make[2]: Map ‘/home/bene/prj’ wordt verlaten .. $Id$ -- rst -- .. pyformex reference manual — scriptMenu .. CREATED WITH py2rst.py: DO NOT EDIT

28. scriptMenu — Menu with pyFormex scripts.

Classes defined in module scriptMenu

class scriptMenu.ScriptMenu(title, dir=None, files=None, ext=None, recursive=None, max=0, autoplay=False, toplevel=True)

A menu of pyFormex scripts in a directory or list.

This class creates a menu of pyFormex scripts collected from a directory or specified in a list. It is e.g. used in the pyFormex GUI to create the examples menu, and for the scripts history. The pyFormex scripts can then be executed from the menu. The user may use this class to add his own scripts into the pyFormex GUI.

Only files that are recognized by utils.is_pyFormex() as being pyFormex scripts will be added to the menu.

The constructor takes the following arguments:

  • title: the top level label for the menu
  • files: a list of file names of pyFormex scripts. If no dir nor ext arguments are given, these should be the full path names to the script files. If omitted, all files in the directory dir whose name is ending with ext and do not start with either ‘.’ or ‘_’, will be selected.
  • dir: an optional directory path. If given, it will be prepended to each file name in files and recursive will be True by default.
  • ext: an extension to be added to each filename. If dir was specified, the default extension is ‘.py’. If no dir was specified, the default extension is an empty string.
  • recursive: if True, a cascading menu of all pyFormex scripts in the directory and below will be constructed.
  • max: if specified, the list of files will be truncated to this number of items. Adding more files to the menu will then be done at the top and the surplus number of files will be dropped from the bottom of the list.

The defaults were thus chosen to be convenient for the two most frequent uses of this class:

ScriptMenu('My Scripts',dir="/path/to/my/sciptsdir")

creates a menu will all pyFormex scripts in the specified path and its subdirectories.

ScriptMenu('History',files=["/my/script1.py","/some/other/script.pye"],recursive=False)

is typically used to create a history menu of previously visited files.

With the resulting file list, a menu is created. Selecting a menu item will make the corresponding file the current script and unless the autoplay configuration variable was set to False, the script is executed.

If the file specification was done by a directory path only, some extra options will be included in the menu. They are fairly self-explaining and mainly intended for the pyFormex developers, in order to test the functionality by running a sets of example scripts:

  • Run next script
  • Run all following scripts
  • Run all scripts
  • Run a random script
  • Run all in random order

Furthermore, if the menu is a toplevel one, it will have the following extra options:

  • Classify scripts
  • Remove catalog
  • Reload scripts

The first option uses the keyword specifications in the scripts docstring to make a classification of the scripts according to keywords. See the scriptKeywords() function for more info. The second option removes the classification. Both options are especially useful for the pyFormex examples.

The last option reloads a ScriptMenu. This can be used to update the menu when you created a new script file.

fileName(scriptname)

Return the full pathname for a scriptname.

getFiles()

Get a list of scripts in self.dir

filterFiles(files)

Filter a list of scripts

loadFiles(files=None)

Load the script files in this menu

run(action)

Run the selected script.

runScript(filename)

Run the specified script.

runNext()

Run the next script.

runAllNext()

Run the current and all following scripts.

runAll()

Run all scripts.

runAllFiles(files, randomize=False, pause=0.0)

Run all the scripts in given list.

runRandom()

Run a random script.

runAllRandom()

Run all scripts in a random order.

reload()

Reload the scripts from dir.

This is only available if a directory path was specified and no files.

add(filename, strict=True)

Add a new filename to the front of the menu.

By default, only legal pyFormex scripts can be added.

classify()

Classify the files in submenus according to keywords.

Functions defined in module scriptMenu

scriptMenu.extractKeyword(s)

Extract a keyword = value pair from a string.

If the input string s is of the form keyword = value a tuple (keyword,value) is returned, else None.

scriptMenu.scriptKeywords(fn, keyw=None)

Read the script keywords from a script file.

  • fn: the full path name of a pyFormex script file.
  • keyw: an optional list of keywords.

Script keywords are written in the form:

key = value

in the docstring of the script. The docstring is the first non-indented multiline string of the file. A multiline string is a string delimited by triple double-quotes. Matching lines are placed in a dictionary which becomes the return value.

If a list of keywords is given, the return dictionary will only contain the matching values.

scriptMenu.sortSets(d)

Turn the set values in d into sorted lists.

  • d: a Python dictionary

All the values in the dictionary are checked. Those that are of type set are converted to a sorted list.

scriptMenu.createScriptMenu(parent=None, before=None)

Create the menu(s) with pyFormex scripts

This creates a menu with all examples distributed with pyFormex. By default, this menu is put in the top menu bar with menu label ‘Examples’.

The user can add his own script directories through the configuration settings. In that case the ‘Examples’ menu and menus for all the configured script paths will be gathered in a top level popup menu labeled ‘Scripts’.

The menu will be placed in the top menu bar before the specified item. If a menu item named ‘Examples’ or ‘Scripts’ already exists, it is replaced.

pyformex-0.8.6/pyformex/doc/html/ref/utils.html0000644000211500021150000011116611705104256021425 0ustar benebene00000000000000 11. utils — A collection of miscellaneous utility functions. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

10. project — project.py

Next topic

12. elements — Definition of elements.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

11. utils — A collection of miscellaneous utility functions.

Classes defined in module utils

class utils.NameSequence(name, ext='')

A class for autogenerating sequences of names.

The name is a string including a numeric part, which is incremented at each call of the ‘next()’ method.

The constructor takes name template and a possible extension as arguments. If the name starts with a non-numeric part, it is taken as a constant part. If the name ends with a numeric part, the next generated names will be obtained by incrementing this part. If not, a string ‘-000’ will be appended and names will be generated by incrementing this part.

If an extension is given, it will be appended as is to the names. This makes it possible to put the numeric part anywhere inside the names.

Example:

>>> N = NameSequence('hallo.98')
>>> [ N.next() for i in range(3) ]
['hallo.98', 'hallo.99', 'hallo.100']
>>> NameSequence('hallo','.png').next()
'hallo-000.png'
>>> N = NameSequence('/home/user/hallo23','5.png')
>>> [ N.next() for i in range(2) ]
['/home/user/hallo235.png', '/home/user/hallo245.png']
next()

Return the next name in the sequence

peek()

Return the next name in the sequence without incrementing.

glob()

Return a UNIX glob pattern for the generated names.

A NameSequence is often used as a generator for file names. The glob() method returns a pattern that can be used in a UNIX-like shell command to select all the generated file names.

Functions defined in module utils

utils.checkVersion(name, version, external=False)

Checks a version of a program/module.

name is either a module or an external program whose availability has been registered. Default is to treat name as a module. Add external=True for a program.

Return value is -1, 0 or 1, depending on a version found that is <, == or > than the requested values. This should normally understand version numbers in the format 2.10.1 Returns -2 if no version found.

utils.hasModule(name, check=False)

Test if we have the named module available.

Returns a nonzero (version) string if the module is available, or an empty string if it is not.

By default, the module is only checked on the first call. The result is remembered in the the_version dict. The optional argument check==True forces a new detection.

utils.hasExternal(name, force=False)

Test if we have the external command ‘name’ available.

Returns a nonzero string if the command is available, or an empty string if it is not.

The external command is only checked on the first call. The result is remembered in the the_external dict.

utils.checkModule(name=None)

Check if the named Python module is available, and record its version.

The version string is returned, empty if the module could not be loaded. The (name,version) pair is also inserted into the the_version dict.

utils.checkExternal(name=None, command=None, answer=None)

Check if the named external command is available on the system.

name is the generic command name, command is the command as it will be executed to check its operation, answer is a regular expression to match positive answers from the command. answer should contain at least one group. In case of a match, the contents of the match will be stored in the the_external dict with name as the key. If the result does not match the specified answer, an empty value is inserted.

Usually, command will contain an option to display the version, and the answer re contains a group to select the version string from the result.

As a convenience, we provide a list of predeclared external commands, that can be checked by their name alone. If no name is given, all commands in that list are checked, and no value is returned.

utils.strNorm(s)

Normalize a string.

Text normalization removes all ‘&’ characters and converts it to lower case.

utils.prefixFiles(prefix, files)

Prepend a prefix to a list of filenames.

utils.matchMany(regexps, target)

Return multiple regular expression matches of the same target string.

utils.matchCount(regexps, target)

Return the number of matches of target to regexps.

utils.matchAny(regexps, target)

Check whether target matches any of the regular expressions.

utils.matchNone(regexps, target)

Check whether targes matches none of the regular expressions.

utils.matchAll(regexps, target)

Check whether targets matches all of the regular expressions.

utils.listTree(path, listdirs=True, topdown=True, sorted=False, excludedirs=[], excludefiles=[], includedirs=[], includefiles=[])

List all files in path.

If dirs==False, directories are not listed. By default the tree is listed top down and entries in the same directory are unsorted.

exludedirs and excludefiles are lists of regular expressions with dirnames, resp. filenames to exclude from the result.

includedirs and includefiles can be given to include only the directories, resp. files matching any of those patterns.

Note that ‘excludedirs’ and ‘includedirs’ force top down handling.

utils.removeTree(path, top=True)

Remove all files below path. If top==True, also path is removed.

utils.pyformexFiles(relative=False)

Return a list of the pyformex source .py files.

utils.setSaneLocale(localestring='')

Set a sane local configuration for LC_NUMERIC.

localestring is the locale string to be set, e.g. ‘en_US.UTF-8’

This will change the LC_ALL setting to the specified string, and set the LC_NUMBERIC to ‘C’.

Changing the LC_NUMERIC setting is a very bad idea! It makes floating point values to be read or written with a comma instead of a the decimal point. Of course this makes input and output files completely incompatible. You will often not be able to process these files any further and create a lot of troubles for yourself and other people if you use an LC_NUMERIC setting different from the standard.

Because we do not want to help you shoot yourself in the foot, this function always sets LC_NUMERIC back to a sane value and we call this function when pyFormex is starting up.

utils.all_image_extensions()

Return a list with all known image extensions.

utils.fileDescription(ftype)

Return a description of the specified file type.

The description of known types are listed in a dict file_description. If the type is unknown, the returned string has the form TYPE files (*.type)

utils.fileType(ftype)

Normalize a filetype string.

The string is converted to lower case and a leading dot is removed. This makes it fit for use with a filename extension.

Example:

>>> fileType('pdf')
'pdf'
>>> fileType('.pdf')
'pdf'
>>> fileType('PDF')
'pdf'
>>> fileType('.PDF')
'pdf'
utils.fileTypeFromExt(fname)

Derive the file type from the file name.

The derived file type is the file extension part in lower case and without the leading dot.

Example:

>>> fileTypeFromExt('pyformex.pdf')
'pdf'
>>> fileTypeFromExt('.pyformexrc')
''
>>> fileTypeFromExt('pyformex')
''
utils.findIcon(name)

Return the file name for an icon with given name.

If no icon file is found, returns the question mark icon.

utils.projectName(fn)

Derive a project name from a file name.

The project name is the basename f the file without the extension.

utils.mtime(fn)

Return the (UNIX) time of last change of file fn.

utils.timeEval(s, glob=None)

Return the time needed for evaluating a string.

s is a string with a valid Python instructions. The string is evaluated using Python’s eval() and the difference in seconds between the current time before and after the evaluation is printed. The result of the evaluation is returned.

This is a simple method to measure the time spent in some operation. It should not be used for microlevel instructions though, because the overhead of the time calls. Use Python’s timeit module to measure microlevel execution time.

utils.countLines(fn)

Return the number of lines in a text file.

utils.runCommand(cmd, RaiseError=True, quiet=False)

Run a command and raise error if exited with error.

cmd is a string with the command to be run. The command is run in the background, waiting for the result. If no error occurs, the exit status and stdout are returned. Else an error is raised by default.

utils.spawn(cmd)

Spawn a child process.

utils.killProcesses(pids, signal)

Send the specified signal to the processes in list

utils.changeExt(fn, ext)

Change the extension of a file name.

The extension is the minimal trailing part of the filename starting with a ‘.’. If the filename has no ‘.’, the extension will be appended. If the given extension does not start with a dot, one is prepended.

utils.tildeExpand(fn)

Perform tilde expansion on a filename.

Bash, the most used command shell in Linux, expands a ‘~’ in arguments to the users home direction. This function can be used to do the same for strings that did not receive the bash tilde expansion, such as strings in the configuration file.

utils.userName()

Find the name of the user.

utils.is_pyFormex(filename)

Checks whether a file is a pyFormex script.

A script is considered to be a pyFormex script if its first line starts with ‘#!’ and contains the substring ‘pyformex’ A file is considered to be a pyFormex script if its name ends in ‘.py’ and the first line of the file contains the substring ‘pyformex’. Typically, a pyFormex script starts with a line:

#!/usr/bin/pyformex
utils.splitEndDigits(s)

Split a string in any prefix and a numerical end sequence.

A string like ‘abc-0123’ will be split in ‘abc-‘ and ‘0123’. Any of both can be empty.

utils.splitStartDigits(s)

Split a string in a numerical sequence and any suffix.

A string like ‘0123-abc’ will be split in ‘0123’ and ‘-abc’. Any of both can be empty.

utils.prefixDict(d, prefix='')

Prefix all the keys of a dict with the given prefix.

  • d: a dict where all the keys are strings.
  • prefix: a string

The return value is a dict with all the items of d, but where the keys have been prefixed with the given string.

utils.subDict(d, prefix='', strip=True)

Return a dict with the items whose key starts with prefix.

  • d: a dict where all the keys are strings.
  • prefix: a string
  • strip: if True (default), the prefix is stripped from the keys.

The return value is a dict with all the items from d whose key starts with prefix. The keys in the returned dict will have the prefix stripped off, unless strip=False is specified.

utils.selectDict(d, keys)

Return a dict with the items whose key is in keys.

  • d: a dict where all the keys are strings.
  • keys: a set of key values, can be a list or another dict.

The return value is a dict with all the items from d whose key is in keys. See removeDict() for the complementary operation.

Example:

>>> d = dict([(c,c*c) for c in range(6)])
>>> selectDict(d,[4,0,1])
{0: 0, 1: 1, 4: 16}
utils.removeDict(d, keys)

Remove a set of keys from a dict.

  • d: a dict
  • keys: a set of key values

The return value is a dict with all the items from d whose key is not in keys. This is the complementary operation of selectDict.

Example:

>>> d = dict([(c,c*c) for c in range(6)])
>>> removeDict(d,[4,0])
{1: 1, 2: 4, 3: 9, 5: 25}
utils.refreshDict(d, src)

Refresh a dict with values from another dict.

The values in the dict d are update with those in src. Unlike the dict.update method, this will only update existing keys but not add new keys.

utils.stuur(x, xval, yval, exp=2.5)

Returns a (non)linear response on the input x.

xval and yval should be lists of 3 values: [xmin,x0,xmax], [ymin,y0,ymax]. Together with the exponent exp, they define the response curve as function of x. With an exponent > 0, the variation will be slow in the neighbourhood of (x0,y0). For values x < xmin or x > xmax, the limit value ymin or ymax is returned.

utils.interrogate(item)

Print useful information about item.

pyformex-0.8.6/pyformex/doc/html/ref/flatkeydb.html0000644000211500021150000005236011705104252022226 0ustar benebene00000000000000 59. flatkeydb — Flat Text File Database. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

58. config — A general yet simple configuration class.

Next topic

60. sendmail — sendmail.py: a simple program to send an email message

[FSF Associate Member]

Valid XHTML 1.0 Transitional

59. flatkeydb — Flat Text File Database.

A simple database stored as a flat text file.

(C) 2005 Benedict Verhegghe.
Distributed under the GNU GPL version 3 or later.

Classes defined in module flatkeydb

class flatkeydb.FlatDB(req_keys=[], comment='#', key_sep='=', beginrec='beginrec', endrec='endrec', strip_blanks=True, strip_quotes=True, check_func=None)

A database stored as a dictionary of dictionaries.

Each record is a dictionary where keys and values are just strings. The field names (keys) can be different for each record, but there is at least one field that exists for all records and will be used as the primary key. This field should have unique values for all records.

The database itself is also a dictionary, with the value of the primary key as key and the full record as value.

On constructing the database a list of keys must be specified that will be required for each record. The first key in this list will be used as the primary key. Obviously, the list must at least have one required key.

The database is stored in a flat text file. Each field (key,value pair) is put on a line by itself. Records are delimited by a (beginrec, endrec) pair. The beginrec marker can be followed by a (key,value) pair on the same line. The endrec marker should be on a line by itself. If endrec is an empty string, each occurrence of beginrec will implicitly end the previous record.

Lines starting with the comment string are ignored. They can occur anywhere between or inside records. Blank lines are also ignored (except they serve as record delimiter if endrec is empty)

Thus, with the initialization:

FlatDB(req_keys=['key1'], comment = 'com', key_sep = '=',
beginrec = 'rec', endrec = '')

the following is a legal database:

com This is a comment
com
rec key1=val1
   key2=val2
rec
com Yes, this starts another record
   key1=val3
   key3=val4

The readFile function can even be instructed to ignore anything not between a (beginrec,endrec) pair. This allows for multiple databases being stored on the same file, even with records intermixed.

Keys and values can be any strings, except that a key can not begin nor end with a blank, and can not be equal to any of the comment, beginrec or endrec markers. Whitespace around the key is always stripped. By default, this is also done for the value (though this can be switched off.) If strip_quotes is True (default), a single pair of matching quotes surrounding the value will be stripped off. Whitespace is stripped before stripping the quotes, so that by including the value in quotes, you can keep leading and trailing whitespace in the value.

A record checking function can be specified. It takes a record as its argument. It is called whenever a new record is inserted in the database (or an existing one is replaced). Before calling this check_func, the system will already have checked that the record is a dictionary and that it has all the required keys.

Two error handlers may be overridden by the user:

  • record_error_handler(record) is called when the record does not pass the checks;
  • key_error_handler(key) is called when a dunplicat key is encountered.

The default for both is to raise an error. Overriding is done by changing the instance attibute.

newRecord()

Returns a new (empty) record.

The new record is a temporary storage. It should be added to the database by calling append(record). This method can be overriden in subclasses.

checkKeys(record)

Check that record has the required keys.

checkRecord(record)

Check a record.

This function checks that the record is a dictionary type, that the record has the required keys, and that check_func(record) returns True (if a check_func was specified). If the record passes, just return True. If it does not, call the record_error_handler and (if it returns) return False. This method can safely be overriden in subclasses.

record_error_handler(record)

Error handler called when a check error on record is discovered.

Default is to raise a runtime error. This method can safely be overriden in subclasses.

key_error_handler(key)

Error handler called when a duplicate key is found.

Default is to raise a runtime error. This method can safely be overriden in subclasses.

insert(record)

Insert a record to the database, overwriting existing records.

This is equivalent to __setitem__ but using the value stored in the the primary key field of the record as key for storing the record. This is also similar to append(), but overwriting an old record with the same primary key.

append(record)

Add a record to the database.

Since the database is a dictionary, keys are unique and appending a record with an existing key is not allowed. If you want to overwrite the old record, use insert() instead.

splitKeyValue(line)

Split a line in key,value pair.

The field is split on the first occurrence of the key_sep. Key and value are then stripped of leading and trailing whitespace. If there is no key_sep, the whole line becomes the key and the value is an empty string. If the key_sep is the first character, the key becomes an empty string.

parseLine(line)

Parse a line of the flat database file.

A line starting with the comment string is ignored. Leading whitespace on the remaining lines is ignored. Empty (blank) lines are ignored, unless the ENDREC mark was set to an empty string, in which case they count as an end of record if a record was started. Lines starting with a ‘BEGINREC’ mark start a new record. The remainder of the line is then reparsed. Lines starting with an ‘ENDREC’ mark close and store the record. All lines between the BEGINREC and ENDREC should be field definition lines of the type ‘KEY [ = VALUE ]’. This function returns 0 if the line was parsed correctly. Else, the variable self.error_msg is set.

parse(lines, ignore=False, filename=None)

Read a database from text.

lines is an iterater over text lines (e.g. a text file or a multiline string splitted on ‘n’) Lines starting with a comment string are ignored. Every record is delimited by a (beginrec,endrec) pair. If ignore is True, all lines that are not between a (beginrec,endrec) pair are simply ignored. Default is to raise a RuntimeError.

readFile(filename, ignore=False)

Read a database from file.

Lines starting with a comment string are ignored. Every record is delimited by a (beginrec,endrec) pair. If ignore is True, all lines that are not between a (beginrec,endrec) pair are simply ignored. Default is to raise a RuntimeError.

writeFile(filename, mode='w', header=None)

Write the database to a text file.

Default mode is ‘w’. Use ‘a’ to append to the file. The header is written at the start of the database. Make sure to start each line with a comment marker if you want to read it back!

match(key, value)

Return a list of records matching key=value.

This returns a list of primary keys of the matching records.

Functions defined in module flatkeydb

flatkeydb.firstWord(s)

Return the first word of a string.

Words are delimited by blanks. If the string does not contain a blank, the whole string is returned.

flatkeydb.unQuote(s)

Remove one level of quotes from a string.

If the string starts with a quote character (either single or double) and ends with the SAME character, they are stripped of the string.

flatkeydb.splitKeyValue(s, key_sep)

Split a string in a (key,value) on occurrence of key_sep.

The string is split on the first occurrence of the substring key_sep. Key and value are then stripped of leading and trailing whitespace. If there is no key_sep, the whole string becomes the key and the value is an empty string. If the string starts with key_sep, the key becomes an empty string.

flatkeydb.ignore_error(dummy)

This function can be used to override the default error handlers.

The effect will be to ignore the error (duplicate key, invalid record) and to not add the affected data to the database.

pyformex-0.8.6/pyformex/doc/html/ref/properties.html0000644000211500021150000010407411705104254022457 0ustar benebene00000000000000 40. properties — General framework for attributing properties to geometrical elements. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

39. datareader — Numerical data reader

Next topic

41. fe — Finite Element Models in pyFormex.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

40. properties — General framework for attributing properties to geometrical elements.

Properties can really be just about any Python object. Properties can be attributed to a set of geometrical elements.

Classes defined in module properties

class properties.Database(data={})

A class for storing properties in a database.

readDatabase(filename, *args, **kargs)

Import all records from a database file.

For now, it can only read databases using flatkeydb. args and kargs can be used to specify arguments for the FlatDB constructor.

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class properties.MaterialDB(data={})

A class for storing material properties.

readDatabase(filename, *args, **kargs)

Import all records from a database file.

For now, it can only read databases using flatkeydb. args and kargs can be used to specify arguments for the FlatDB constructor.

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class properties.SectionDB(data={})

A class for storing section properties.

readDatabase(filename, *args, **kargs)

Import all records from a database file.

For now, it can only read databases using flatkeydb. args and kargs can be used to specify arguments for the FlatDB constructor.

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class properties.ElemSection(section=None, material=None, orientation=None, behavior=None, **kargs)

Properties related to the section of an element.

An element section property can hold the following sub-properties:

section

the geometric properties of the section. This can be a dict or a string. If it is a string, its value is looked up in the global section database. The required data in the dict depend on the sectiontype. Currently the following keys are used by fe_abq.py:

sectiontype

the type of section: should be one of following:

  • ‘solid’: a solid 2D or 3D section,
  • ‘circ’ : a plain circular section,
  • ‘rect’ : a plain rectangular section,
  • ‘pipe’ : a hollow circular section,
  • ‘box’ : a hollow rectangular section,
  • ‘I’ : an I-beam,
  • ‘general’ : anything else (automatically set if not specified).

!! Currently only ‘solid’ and ‘general’ are allowed.

  • the cross section characteristics : cross_section, moment_inertia_11, moment_inertia_12, moment_inertia_22, torsional_constant
  • for sectiontype ‘circ’: radius
material
the element material. This can be a dict or a string. Currently known keys to fe_abq.py are: young_modulus, shear_modulus, density, poisson_ratio . (see fmtMaterial in fe_abq)
orientation
  • a Dict, or
  • a list of 3 direction cosines of the first beam section axis.
behavior
the behavior of a connector
addSection(section)

Create or replace the section properties of the element.

If ‘section’ is a dict, it will be added to ‘self.secDB’. If ‘section’ is a string, this string will be used as a key to search in ‘self.secDB’.

computeSection(section)

Compute the section characteristics of specific sections.

addMaterial(material)

Create or replace the material properties of the element.

If the argument is a dict, it will be added to ‘self.matDB’. If the argument is a string, this string will be used as a key to search in ‘self.matDB’.

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class properties.ElemLoad(label=None, value=None)

Distributed loading on an element.

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class properties.EdgeLoad(edge=-1, label=None, value=None)

Distributed loading on an element edge.

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class properties.CoordSystem(csys, cdata)

A class for storing coordinate systems.

class properties.Amplitude(data, definition='TABULAR')

A class for storing an amplitude.

The amplitude is a list of tuples (time,value).

class properties.PropertyDB

A database class for all properties.

This class collects all properties that can be set on a geometrical model.

This should allow for storing:

  • materials
  • sections
  • any properties
  • node properties
  • elem properties
  • model properties (current unused: use unnamed properties)
setMaterialDB(aDict)

Set the materials database to an external source

setSectionDB(aDict)

Set the sections database to an external source

Prop(kind='', tag=None, set=None, name=None, **kargs)

Create a new property, empty by default.

A property can hold almost anything, just like any Dict type. It has however four predefined keys that should not be used for anything else than explained hereafter:

  • nr: a unique id, that never should be set/changed by the user.
  • tag: an identification tag used to group properties
  • name: the name to be used for this set. Default is to use an automatically generated name.
  • set: identifies the geometrical elements for which the defined properties will hold. This can be either:
    • a single number,
    • a list of numbers,
    • the name of an already defined set,
    • a list of such names.

Besides these, any other fields may be defined and will be added without checking.

getProp(kind='', rec=None, tag=None, attr=[], noattr=[], delete=False)

Return all properties of type kind matching tag and having attr.

kind is either ‘’, ‘n’, ‘e’ or ‘m’ If rec is given, it is a list of record numbers or a single number. If a tag or a list of tags is given, only the properties having a matching tag attribute are returned.

attr and noattr are lists of attributes. Only the properties having all the attributes in attr and none of the properties in noattr are returned. Attributes whose value is None are treated as non-existing.

If delete==True, the returned properties are removed from the database.

delProp(kind='', rec=None, tag=None, attr=[])

Delete properties.

This is equivalent to getProp() but the returned properties are removed from the database.

nodeProp(prop=None, set=None, name=None, tag=None, cload=None, bound=None, displ=None, veloc=None, accel=None, csys=None, ampl=None, **kargs)

Create a new node property, empty by default.

A node property can contain any combination of the following fields:

  • tag: an identification tag used to group properties (this is e.g. used to flag Step, increment, load case, ...)
  • set: a single number or a list of numbers identifying the node(s) for which this property will be set, or a set name If None, the property will hold for all nodes.
  • cload: a concentrated load: a list of 6 float values [FX,FY,FZ,MX,MY,MZ] or a list of (dofid,value) tuples.
  • displ,veloc,accel: prescribed displacement, velocity or acceleration: a list of 6 float values [UX,UY,UZ,RX,RY,RZ] or a list of tuples (dofid,value)
  • bound: a boundary condition: a str or a list of 6 codes (0/1)
  • csys: a CoordSystem
  • ampl: the name of an Amplitude
elemProp(prop=None, grp=None, set=None, name=None, tag=None, section=None, eltype=None, dload=None, eload=None, ampl=None, **kargs)

Create a new element property, empty by default.

An elem property can contain any combination of the following fields:

  • tag: an identification tag used to group properties (this is e.g. used to flag Step, increment, load case, ...)
  • set: a single number or a list of numbers identifying the element(s) for which this property will be set, or a set name If None, the property will hold for all elements.
  • grp: an elements group number (default None). If specified, the element numbers given in set are local to the specified group. If not, elements are global and should match the global numbering according to the order in which element groups will be specified in the Model.
  • eltype: the element type (currently in Abaqus terms).
  • section: an ElemSection specifying the element section properties.
  • dload: an ElemLoad specifying a distributed load on the element.
  • ampl: the name of an Amplitude
update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

Functions defined in module properties

properties.checkIdValue(values)

Check that a variable is a list of (id,value) tuples

id should be convertible to an int, value to a float. If ok, return the values as a list of (int,float) tuples.

properties.checkArrayOrIdValue(values)

Check that a variable is an list of values or (id,value) tuples

This convenience function checks that the argument is either:

  • a list of 6 float values (or convertible to it), or
  • a list of (id,value) tuples where id is convertible to an int, value to a float.

If ok, return the values as a list of (int,float) tuples.

properties.checkString(a, valid)

Check that a string a has one of the valid values.

This is case insensitive, and returns the upper case string if valid. Else, an error is raised.

properties.FindListItem(l, p)

Find the item p in the list l.

If p is an item in the list (not a copy of it!), this returns its position. Else, -1 is returned.

Matches are found with a ‘is’ function, not an ‘==’. Only the first match will be reported.

properties.RemoveListItem(l, p)

Remove the item p from the list l.

If p is an item in the list (not a copy of it!), it is removed from the list. Matches are found with a ‘is’ comparison. This is different from the normal Python list.remove() method, which uses ‘==’. As a result, we can find complex objects which do not allow ‘==’, such as ndarrays.

pyformex-0.8.6/pyformex/doc/html/ref/mydict.html0000644000211500021150000004041311705104254021550 0ustar benebene00000000000000 55. mydict — — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

54. olist — Some convenient shortcuts for common list operations.

Next topic

56. odict — Specialized dictionary type structures.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

55. mydict

CDict is a Dict with lookup cascading into the next level Dict’s if the key is not found in the CDict itself.

(C) 2005,2008 Benedict Verhegghe Distributed under the GNU GPL version 3 or later

Classes defined in module mydict

class mydict.Dict(data={}, default=None)

A Python dictionary with default values and attribute syntax.

Dict is functionally nearly equivalent with the builtin Python dict, but provides the following extras:

  • Items can be accessed with attribute syntax as well as dictionary syntax. Thus, if C is a Dict, C['foo'] and C.foo are equivalent. This works as well for accessing values as for setting values. In the following, the terms key or attribute therefore have the same meaning.
  • Lookup of a nonexisting key/attribute does not automatically raise an error, but calls a _default_ lookup method which can be set by the user. The default is to raise a KeyError, but an alternative is to return None or some other default value.

There are a few caveats though:

  • Keys that are also attributes of the builtin dict type, can not be used with the attribute syntax to get values from the Dict. You should use the dictionary syntax to access these items. It is possible to set such keys as attributes. Thus the following will work:

    C['get'] = 'foo'
    C.get = 'foo'
    print(C['get'])
    

    but this will not:

    print(C.get)
    

    This is done so because we want all the dict attributes to be available with their normal binding. Thus,

    print(C.get('get'))
    

    will print foo

To avoid name clashes with user defines, many Python internal names start and end with ‘__’. The user should avoid such names. The Python dict has the following attributes not enclosed between ‘__’, so these are the ones to watch out for: ‘clear’, ‘copy’, ‘fromkeys’, ‘get’, ‘has_key’, ‘items’, ‘iteritems’, ‘iterkeys’, ‘itervalues’, ‘keys’, ‘pop’, ‘popitem’, ‘setdefault’, ‘update’, ‘values’.

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

class mydict.CDict(data={}, default=<function returnNone at 0x435fc80>)

A cascading Dict: properties not in Dict are searched in all Dicts.

This is equivalent to the Dict class, except that if a key is not found and the CDict has items with values that are themselves instances of Dict or CDict, the key will be looked up in those Dicts as well.

As you expect, this will make the lookup cascade into all lower levels of CDict’s. The cascade will stop if you use a Dict. There is no way to guarantee in which order the (Cascading)Dict’s are visited, so if multiple Dicts on the same level hold the same key, you should know yourself what you are doing.

update(data={})

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

get(key, default)

Return the value for key or a default.

This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError.

setdefault(key, default)

Replaces the setdefault function of a normal dictionary.

This is the same as the get method, except that it also sets the default value if get found a KeyError.

Functions defined in module mydict

mydict.cascade(d, key)

Cascading lookup in a dictionary.

This is equivalent to the dict lookup, except that when the key is not found, a cascading lookup through lower level dict’s is started and the first matching key found is returned.

mydict.returnNone(key)

Always returns None.

mydict.raiseKeyError(key)

Raise a KeyError.

pyformex-0.8.6/pyformex/doc/html/ref/draw.html0000644000211500021150000015616711705104252021230 0ustar benebene00000000000000 5. draw — Create 3D graphical representations. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

4. script — Basic pyFormex script functions

Next topic

6. colors — Definition of some RGB colors and color conversion functions

[FSF Associate Member]

Valid XHTML 1.0 Transitional

5. draw — Create 3D graphical representations.

The draw module provides the basic user interface to the OpenGL rendering capabilities of pyFormex. The full contents of this module is available to scripts running in the pyFormex GUI without the need to import it.

Classes defined in module draw

Functions defined in module draw

draw.closeGui()

Close the GUI.

Calling this function from a script closes the GUI and terminates pyFormex.

draw.closeDialog(name)

Close the named dialog.

Closes the InputDialog with the given name. If multiple dialogs are open with the same name, all these dialogs are closed.

This only works for dialogs owned by the pyFormex GUI.

draw.showMessage(text, actions=['OK'], level='info', modal=True, **kargs)

Show a short message widget and wait for user acknowledgement.

There are three levels of messages: ‘info’, ‘warning’ and ‘error’. They differ only in the icon that is shown next to the test. By default, the message widget has a single button with the text ‘OK’. The dialog is closed if the user clicks a button. The return value is the button text.

draw.showInfo(text, actions=['OK'], modal=True)

Show an informational message and wait for user acknowledgement.

draw.warning(text, actions=['OK'])

Show a warning message and wait for user acknowledgement.

draw.error(text, actions=['OK'])

Show an error message and wait for user acknowledgement.

draw.ask(question, choices=None, **kargs)

Ask a question and present possible answers.

Return answer if accepted or default if rejected. The remaining arguments are passed to the InputDialog getResult method.

draw.ack(question, **kargs)

Show a Yes/No question and return True/False depending on answer.

draw.showText(text, type=None, actions=[('OK', None)], modal=True, mono=False)

Display a text and wait for user response.

This opens a TextBox widget and displays the text in the widget. Scrollbars will be added if the text is too large to display at once.

The text can be plain text format. Some rich text formats will be recognized and rendered appropriately. See widgets.TextBox.

mono=True forces the use of a monospaced font.

draw.showFile(filename, mono=False, **kargs)

Display a text file.

This will use the showText() function to display a text read from a file. By default this uses a monospaced font. Other arguments may also be passed to ShowText.

draw.showDescription(filename=None)

Show the Description part of the docstring of a pyFormex script.

If no file name is specified, the current script is used. If the script’s docstring has no Description part, a default text is shown.

draw.askItems(items, caption=None, timeout=None, **kargs)

Ask the value of some items to the user.

Create an interactive widget to let the user set the value of some items. ‘items’ is a list of input items (basically [key,value] pairs). See the widgets.InputDialog class for complete description of the available input items.

Two InputDialog classes are defined in gui.widgets. The OldInputDialog class is deprecated in favor of InputDialog, which has become the default as of pyFormex 0.8.3. The two classes differ in how the input is specified. In the new format, each input item is either a simpleInputItem, a groupInputItem or a tabInputItem.

You can specify ‘legacy=False’ to indicate that you are using the new format, or ‘legacy=True’ if your data are in the old format. The default (‘legacy = None’), will make this function try to detect the format and convert the input items to the proper new format. This conversion will work on most, but not all legacy formats that have been used in the past by pyFormex. Since the legacy format is scheduled to be withdrawn in future, users are encouraged to change their input to the new format.

The remaining arguments are keyword arguments that are passed to the InputDialog.getResult method. A timeout (in seconds) can be specified to have the input dialog interrupted automatically.

Return a dictionary with the results: for each input item there is a (key,value) pair. Returns an empty dictionary if the dialog was canceled. Sets the dialog timeout and accepted status in global variables.

draw.currentDialog()

Returns the current dialog widget.

This returns the dialog widget created by the askItems() function, while the dialog is still active. If no askItems() has been called or if the user already closed the dialog, None is returned.

draw.dialogAccepted()

Returns True if the last askItems() dialog was accepted.

draw.dialogRejected()

Returns True if the last askItems() dialog was rejected.

draw.dialogTimedOut()

Returns True if the last askItems() dialog timed out.

draw.askFilename(cur=None, filter='All files (*.*)', exist=True, multi=False, change=True)

Ask for a file name or multiple file names using a file dialog.

cur is a directory or filename. All the files matching the filter in that directory (or that file’s directory) will be shown. If cur is a file, it will be selected as the current filename.

Unless the user cancels the operation, or the change parameter was set to False, the parent directory of the selected file will become the new working directory.

draw.askNewFilename(cur=None, filter='All files (*.*)')

Ask a single new filename.

This is a convenience function for calling askFilename with the arguments exist=False.

draw.askDirname(path=None, change=True)

Interactively select a directory and change the current workdir.

The user is asked to select a directory through the standard file dialog. Initially, the dialog shows all the subdirectories in the specified path, or by default in the current working directory.

The selected directory becomes the new working directory, unless the user canceled the operation, or the change parameter was set to False.

draw.checkWorkdir()

Ask the user to change the current workdir if it is not writable.

Returns True if the new workdir is writable.

draw.printMessage(s)

Print a message on the message board.

If a logfile was opened, the message is also written to the log file.

draw.message(s)

Print a message on the message board.

If a logfile was opened, the message is also written to the log file.

draw.flatten(objects, recurse=True)

Flatten a list of geometric objects.

Each item in the list should be either:

  • a drawable object,
  • a string with the name of such an object,
  • a list of any of these three.

This function will flatten the lists and replace the string items with the object they point to. The result is a single list of drawable objects. This function does not enforce the objects to be drawable. That should be done by the caller.

draw.drawable(objects)

Filters the drawable objects from a list.

The input is a list, usually of drawable objects. For each item in the list, the following is done:

  • if the item is drawable, it is kept as is,
  • if the item is not drawable but can be converted to a Formex, it is converted,
  • if it is neither drawable nor convertible to Formex, it is removed.

The result is a list of drawable objects (since a Formex is drawable).

draw.draw(F, view=None, bbox=None, color='prop', colormap=None, bkcolor=None, bkcolormap=None, alpha=None, mode=None, linewidth=None, linestipple=None, shrink=None, marksize=None, wait=True, clear=None, allviews=False, highlight=False, nolight=False, ontop=False, silent=True, **kargs)

Draw object(s) with specified settings and direct camera to it.

The first argument is an object to be drawn. All other arguments are settings that influence how the object is being drawn.

F is one of:

  • a drawable object (a geometry object like Formex, Mesh or TriSurface),
  • the name of a global pyFormex variable refering to such an object,
  • a list of any of these three items.

The variables are replaced with their value and the lists are flattened, to create a single list of objects. This then filtered for the drawable objects, and the resulting list of drawables is drawn using the remaining arguments.

The remaining arguments are drawing options. If None, they are filled in from the current viewport drawing options.

view is either the name of a defined view or ‘last’ or None. Predefined views are ‘front’,’back’,’top’,’bottom’,’left’,’right’,’iso’. With view=None the camera settings remain unchanged (but might be changed interactively through the user interface). This may make the drawn object out of view! With view=’last’, the camera angles will be set to the same camera angles as in the last draw operation, undoing any interactive changes. The initial default view is ‘front’ (looking in the -z direction).

bbox specifies the 3D volume at which the camera will be aimed (using the angles set by view). The camera position wil be set so that the volume comes in view using the current lens (default 45 degrees). bbox is a list of two points or compatible (array with shape (2,3)). Setting the bbox to a volume not enclosing the object may make the object invisible on the canvas. The special value bbox=’auto’ will use the bounding box of the objects getting drawn (object.bbox()), thus ensuring that the camera will focus on these objects. The special value bbox=None will use the bounding box of the previous drawing operation, thus ensuring that the camera’s target volume remains unchanged.

color,colormap,linewidth,alpha,marksize are passed to the creation of the 3D actor.

if color is None, it is drawn with the color specified on creation. if color == ‘prop’ and a colormap was installed, props define color. else, color should be an array of RGB values, either with shape (3,) for a single color, or (nelems,3) for differently colored elements

shrink is a floating point shrink factor that will be applied to object before drawing it.

If the Formex has properties and a color list is specified, then the the properties will be used as an index in the color list and each member will be drawn with the resulting color. If color is one color value, the whole Formex will be drawn with that color. Finally, if color=None is specified, the whole Formex is drawn in black.

Each draw action activates a locking mechanism for the next draw action, which will only be allowed after drawdelay seconds have elapsed. This makes it easier to see subsequent images and is far more elegant that an explicit sleep() operation, because all script processing will continue up to the next drawing instruction. The value of drawdelay is set in the config, or 2 seconds by default. The user can disable the wait cycle for the next draw operation by specifying wait=False. Setting drawdelay=0 will disable the waiting mechanism for all subsequent draw statements (until set >0 again).

draw.focus(object)

Move the camera thus that object comes fully into view.

object can be anything having a bbox() method or a list thereof. if no view is given, the default is used.

The camera is moved with fixed axis directions to a place where the whole object can be viewed using a 45. degrees lens opening. This technique may change in future!

draw.setDrawOptions(kargs0={}, **kargs)

Set default values for the draw options.

Draw options are a set of options that hold default values for the draw() function arguments and for some canvas settings. The draw options can be specified either as a dictionary, or as keyword arguments.

draw.askDrawOptions(d={})

Interactively ask the Drawing options from the user.

A dictionary may be specified to override the current defaults.

draw.drawVectors(P, v, size=None, nolight=True, **drawOptions)

Draw a set of vectors.

If size==None, draws the vectors v at the points P. If size is specified, draws the vectors size*normalize(v) P, v and size are single points or sets of points. If sets, they should be of the same size.

Other drawoptions can be specified and will be passed to the draw function.

draw.drawMarks(X, M, color='black', leader='', ontop=True)

Draw a list of marks at points X.

X is a Coords array. M is a list with the same length as X. The string representation of the marks are drawn at the corresponding 3D coordinate.

draw.drawFreeEdges(M, color='black')

Draw the feature edges of a Mesh

draw.drawNumbers(F, numbers=None, color='black', trl=None, offset=0, leader='', ontop=True)

Draw numbers on all elements of F.

numbers is an array with F.nelems() integer numbers. If no numbers are given, the range from 0 to nelems()-1 is used. Normally, the numbers are drawn at the centroids of the elements. A translation may be given to put the numbers out of the centroids, e.g. to put them in front of the objects to make them visible, or to allow to view a mark at the centroids. If an offset is specified, it is added to the shown numbers.

draw.drawPropNumbers(F, **kargs)

Draw property numbers on all elements of F.

This calls drawNumbers to draw the property numbers on the elements. All arguments of drawNumbers except numbers may be passed. If the object F thus not have property numbers, -1 values are drawn.

draw.drawVertexNumbers(F, color='black', trl=None, ontop=False)

Draw (local) numbers on all vertices of F.

Normally, the numbers are drawn at the location of the vertices. A translation may be given to put the numbers out of the location, e.g. to put them in front of the objects to make them visible, or to allow to view a mark at the vertices.

draw.drawText3D(P, text, color=None, font='sans', size=18, ontop=True)

Draw a text at a 3D point P.

draw.drawAxes(*args, **kargs)

Draw the axes of a CoordinateSystem.

This draws an AxesActor corresponding to the specified Coordinatesystem. The arguments are the same as those of the AxesActor constructor.

draw.drawImage(image, nx=-1, ny=-1, pixel='dot')

Draw an image as a colored Formex

Draws a raster image as a colored Formex. While there are other and better ways to display an image in pyFormex (such as using the imageView widget), this function allows for interactive handling the image using the OpenGL infrastructure.

Parameters:

  • image: a QImage holding a raster image. An image can be loaded from most standard image files using the loadImage() function
  • nx,`ny`: resolution you want to use for the display
  • pixel: the Formex representing a single pixel. It should be either a single element Formex, or one of the strings ‘dot’ or ‘quad’. If ‘dot’ a single point will be used, if ‘quad’ a unit square. The difference will be important when zooming in. The default is ‘dot’.
draw.drawViewportAxes3D(pos, color=None)

Draw two viewport axes at a 3D position.

draw.drawBbox(A)

Draw the bbox of the actor A.

draw.drawActor(A)

Draw an actor and update the screen.

draw.undraw(itemlist)

Remove an item or a number of items from the canvas.

Use the return value from one of the draw... functions to remove the item that was drawn from the canvas. A single item or a list of items may be specified.

draw.view(v, wait=True)

Show a named view, either a builtin or a user defined.

This shows the current scene from another viewing angle. Switching views of a scene is much faster than redrawing a scene. Therefore this function is prefered over draw() when the actors in the scene remain unchanged and only the camera viewpoint changes.

Just like draw(), this function obeys the drawing lock mechanism, and by default it will restart the lock to retard the next draing operation.

draw.setTriade(on=None, pos='lb', siz=100)

Toggle the display of the global axes on or off.

If on is True, the axes triade is displayed, if False it is removed. The default (None) toggles between on and off.

draw.drawText(text, x, y, gravity='E', font='helvetica', size=14, color=None, zoom=None)

Show a text at position x,y using font.

draw.annotate(annot)

Draw an annotation.

draw.decorate(decor)

Draw a decoration.

draw.createView(name, angles, addtogui=False)

Create a new named view (or redefine an old).

The angles are (longitude, latitude, twist). By default, the view is local to the script’s viewport. If gui is True, it is also added to the GUI.

draw.setView(name, angles=None)

Set the default view for future drawing operations.

If no angles are specified, the name should be an existing view, or the predefined value ‘last’. If angles are specified, this is equivalent to createView(name,angles) followed by setView(name).

draw.bgcolor(color, color2=None, mode='h')

Change the background color (and redraw).

If one color is given, the background is a solid color. If two colors are given, the background color will get a vertical gradient with color on top and color2 at the bottom.

draw.fgcolor(color)

Set the default foreground color.

draw.colormap(color=None)

Gets/Sets the current canvas color map

draw.lights(state=True)

Set the lights on or off

draw.transparent(state=True)

Set the transparency mode on or off.

draw.set_material_value(typ, val)

Set the value of one of the material lighting parameters

typ is one of ‘ambient’,’specular’,’emission’,’shininess’ val is a value between 0.0 and 1.0

draw.linewidth(wid)

Set the linewidth to be used in line drawings.

draw.linestipple(factor, pattern)

Set the linewidth to be used in line drawings.

draw.pointsize(siz)

Set the size to be used in point drawings.

draw.canvasSize(width, height)

Resize the canvas to (width x height).

draw.clear_canvas()

Clear the canvas.

This is a low level function not intended for the user.

draw.clear()

Clear the canvas.

Removes everything from the current scene and displays an empty background.

This function waits for the drawing lock to be released, but will not reset it.

draw.delay(s=None)

Get/Set the draw delay time.

Returns the current setting of the draw wait time (in seconds). This drawing delay is obeyed by drawing and viewing operations.

A parameter may be given to set the delay time to a new value. It should be convertable to a float. The function still returns the old setting. This may be practical to save that value to restore it later.

draw.wait(relock=True)

Wait until the drawing lock is released.

This uses the drawing lock mechanism to pause. The drawing lock ensures that subsequent draws are retarded to give the user the time to view. This use of this function is prefered over that of pause() or sleep(), because it allows your script to continue the numerical computations while waiting to draw the next screen.

This function can be used to retard other functions than `

draw.fforward()

Releases the drawing lock mechanism indefinely.

Releasing the drawing lock indefinely means that the lock will not be set again and your script will execute till the end.

draw.step()

Perform one step of a script.

A step is a set of instructions until the next draw operation. If a script is running, this just releases the draw lock. Else, it starts the script in step mode.

draw.pause(msg='Use the Step or Continue button to proceed', timeout=None)

Pause the execution until an external event occurs or timeout.

When the pause statement is executed, execution of the pyformex script is suspended until some external event forces it to proceed again. Clicking the STEP or CONTINUE button will produce such an event.

draw.sleep(timeout=None)

Sleep until key/mouse press in the canvas or until timeout

draw.wakeup(mode=0)

Wake up from the sleep function.

This is the only way to exit the sleep() function. Default is to wake up from the current sleep. A mode > 0 forces wakeup for longer period.

draw.zoomRectangle()

Zoom a rectangle selected by the user.

draw.zoomBbox(bb)

Zoom thus that the specified bbox becomes visible.

draw.zoomAll()

Zoom thus that all actors become visible.

draw.flyAlong(path, upvector=[0.0, 1.0, 0.0], sleeptime=None)

Fly through the current scene along the specified path.

  • path: a plex-2 or plex-3 Formex (or convertibel to such Formex) specifying the paths of camera eye and center (and upvector).
  • upvector: the direction of the vertical axis of the camera, in case of a 2-plex camera path.
  • sleeptime: a delay between subsequent images, to slow down the camera movement.

This function moves the camera through the subsequent elements of the Formex. For each element the first point is used as the center of the camera and the second point as the eye (the center of the scene looked at). For a 3-plex Formex, the third point is used to define the upvector (i.e. the vertical axis of the image) of the camera. For a 2-plex Formex, the upvector is constant as specified in the arguments.

draw.viewport(n=None)

Select the current viewport.

n is an integer number in the range of the number of viewports, or is one of the viewport objects in pyformex.GUI.viewports

if n is None, selects the current GUI viewport for drawing

draw.layout(nvps=None, ncols=None, nrows=None, pos=None, rstretch=None, cstretch=None)

Set the viewports layout.

draw.addViewport()

Add a new viewport.

draw.removeViewport()

Remove the last viewport.

draw.linkViewport(vp, tovp)

Link viewport vp to viewport tovp.

Both vp and tovp should be numbers of viewports.

draw.updateGUI()

Update the GUI.

draw.highlightActors(K)

Highlight a selection of actors on the canvas.

K is Collection of actors as returned by the pick() method. colormap is a list of two colors, for the actors not in, resp. in the Collection K.

draw.highlightElements(K)

Highlight a selection of actor elements on the canvas.

K is Collection of actor elements as returned by the pick() method. colormap is a list of two colors, for the elements not in, resp. in the Collection K.

draw.highlightEdges(K)

Highlight a selection of actor edges on the canvas.

K is Collection of TriSurface actor edges as returned by the pick() method. colormap is a list of two colors, for the edges not in, resp. in the Collection K.

draw.highlightPoints(K)

Highlight a selection of actor elements on the canvas.

K is Collection of actor elements as returned by the pick() method.

draw.highlightPartitions(K)

Highlight a selection of partitions on the canvas.

K is a Collection of actor elements, where each actor element is connected to a collection of property numbers, as returned by the partitionCollection() method.

draw.removeHighlights()

Remove the highlights from the current viewport

draw.pick(mode='actor', filter=None, oneshot=False, func=None)

Enter interactive picking mode and return selection.

See viewport.py for more details. This function differs in that it provides default highlighting during the picking operation, a button to stop the selection operation

Parameters:

  • mode: one of the pick modes
  • filter: one of the selection_filters. The default picking filter activated on entering the pick mode. All available filters are presented in a combobox.
draw.highlight(K, mode)

Highlight a Collection of actor/elements.

K is usually the return value of a pick operation, but might also be set by the user. mode is one of the pick modes.

draw.set_edit_mode(s)

Set the drawing edit mode.

draw.drawLinesInter(mode='line', single=False, func=None)

Enter interactive drawing mode and return the line drawing.

See viewport.py for more details. This function differs in that it provides default displaying during the drawing operation and a button to stop the drawing operation.

The drawing can be edited using the methods ‘undo’, ‘clear’ and ‘close’, which are presented in a combobox.

draw.showLineDrawing(L)

Show a line drawing.

L is usually the return value of an interactive draw operation, but might also be set by the user.

pyformex-0.8.6/pyformex/doc/html/ref/formex.html0000644000211500021150000023637311705104253021572 0ustar benebene00000000000000 2. formex — Formex algebra in Python — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

1. coords — A structured collection of 3D coordinates.

Next topic

3. arraytools — A collection of numerical array utilities.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

2. formex — Formex algebra in Python

This module defines the Formex class, which is the major class for representing geometry in pyFormex. The Formex class implements most functionality of Formex algebra in a consistent and easy to understand syntax.

Classes defined in module formex

class formex.Formex(data=[], prop=None, eltype=None)

A Formex is a numpy array of order 3 (axes 0,1,2) and type Float. A scalar element represents a coordinate (F:uniple).

A row along the axis 2 is a set of coordinates and represents a point (node, vertex, F: signet). For simplicity’s sake, the current implementation only deals with points in a 3-dimensional space. This means that the length of axis 2 is always 3. The user can create formices (plural of Formex) in a 2-D space, but internally these will be stored with 3 coordinates, by adding a third value 0. All operations work with 3-D coordinate sets. However, a method exists to extract only a limited set of coordinates from the results, permitting to return to a 2-D environment

A plane along the axes 2 and 1 is a set of points (F: cantle). This can be thought of as a geometrical shape (2 points form a line segment, 3 points make a triangle, ...) or as an element in FE terms. But it really is up to the user as to how this set of points is to be interpreted.

Finally, the whole Formex represents a set of such elements.

Additionally, a Formex may have a property set, which is an 1-D array of integers. The length of the array is equal to the length of axis 0 of the Formex data (i.e. the number of elements in the Formex). Thus, a single integer value may be attributed to each element. It is up to the user to define the use of this integer (e.g. it could be an index in a table of element property records). If a property set is defined, it will be copied together with the Formex data whenever copies of the Formex (or parts thereof) are made. Properties can be specified at creation time, and they can be set, modified or deleted at any time. Of course, the properties that are copied in an operation are those that exist at the time of performing the operation.

Because the Formex class is derived from Geometry, the following Formex methods exist and return the value of the same method applied on the coords attribute: x, y, z, bbox, center, centroid, sizes, dsize, bsphere, distanceFromPlane, distanceFromLine, distanceFromPoint, directionalSize, directionalWidth, directionalExtremes, __str__. Refer to the correponding Coords method for their usage.

Also, the following Coords transformation methods can be directly applied to a Formex object or a derived class object. The return value is a new object identical to the original, except for the coordinates, which will have been transformed by the specified method. Refer to the correponding Coords method for the usage of these methods: scale, translate, rotate, shear, reflect, affine, cylindrical, hyperCylindrical, toCylindrical, spherical, superSpherical, toSpherical, bump, bump1, bump2, flare, map, map1, mapd, newmap, replace, swapAxes, rollAxes, projectOnSphere, projectOnCylinder, rot, trl.

element(i)

Return element i of the Formex

point(i, j)

Return point j of element i

coord(i, j, k)

Return coord k of point j of element i

nelems()

Return the number of elements in the formex.

nplex()

Return the number of points per element.

Examples:

1: unconnected points, 2: straight line elements, 3: triangles or quadratic line elements, 4: tetraeders or quadrilaterals or cubic line elements.

ndim()

Return the number of dimensions.

This is the number of coordinates for each point. In the current implementation this is always 3, though you can define 2D Formices by given only two coordinates: the third will automatically be set to zero.

npoints()

Return the number of points in the formex.

This is the product of the number of elements in the formex with the number of nodes per element.

shape()

Return the shape of the Formex.

The shape of a Formex is the shape of its data array, i.e. a tuple (nelems, nplex, ndim).

view()

Return the Formex coordinates as a numpy array (ndarray).

Since the ndarray object has a method view() returning a view on the ndarray, this method allows writing code that works with both Formex and ndarray instances. The results is always an ndarray.

getProp(index=None)

Return the property numbers of the element in index

maxProp()

Return the highest property value used, or None

propSet()

Return a list with unique property values on this Formex.

centroids()

Return the centroids of all elements of the Formex.

The centroid of an element is the point whose coordinates are the mean values of all points of the element. The return value is a Coords object with nelems points.

fuse(repeat=True, nodesperbox=1, rtol=1e-05, atol=None)

Return a tuple of nodal coordinates and element connectivity.

A tuple of two arrays is returned. The first is float array with the coordinates of the unique nodes of the Formex. The second is an integer array with the node numbers connected by each element. The elements come in the same order as they are in the Formex, but the order of the nodes is unspecified. By the way, the reverse operation of coords,elems = fuse(F) is accomplished by F = Formex(coords[elems])

There is a (very small) probability that two very close nodes are not equivalenced by this procedure. Use it multiple times with different parameters to check. You can also set the rtol /atol parameters to influence the equivalence checking of two points. The default settting for atol is rtol * self.dsize()

toMesh(*args, **kargs)

Convert a Formex to a Mesh.

Converts a geometry in Formex model to the equivalent Mesh model. In the Mesh model, all points with nearly identical coordinates are fused into a single point, and elements are defined by a connectivity table with integers pointing to the corresponding vertex.

toSurface()

Convert a Formex to a Surface.

If the plexitude of the Formex is 3, returns a TriSurface equivalent with the Formex. Else, an error is raised.

info()

Return formatted information about a Formex.

classmethod point2str(clas, point)

Return a string representation of a point

classmethod element2str(clas, elem)

Return a string representation of an element

asFormex()

Return string representation of a Formex as in Formian.

Coordinates are separated by commas, points are separated by semicolons and grouped between brackets, elements are separated by commas and grouped between braces:

>>> F = Formex([[[1,0],[0,1]],[[0,1],[1,2]]])
>>> print(F)
{[1.0,0.0,0.0; 0.0,1.0,0.0], [0.0,1.0,0.0; 1.0,2.0,0.0]}
asFormexWithProp()

Return string representation as Formex with properties.

The string representation as done by asFormex() is followed by the words “with prop” and a list of the properties.

asArray()

Return string representation as a numpy array.

classmethod setPrintFunction(clas, func)

Choose the default formatting for printing formices.

This sets how formices will be formatted by a print statement. Currently there are two available functions: asFormex, asArray. The user may create its own formatting method. This is a class method. It should be used asfollows: Formex.setPrintFunction(Formex.asArray).

setProp(p=None)

Create or destroy the property array for the Formex.

A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored.

If a value None is given, the properties are removed from the Formex.

append(F)

Append the members of Formex F to this one.

This function changes the original one! Use __add__ if you want to get a copy with the sum. >>> F = Formex([[[1.0,1.0,1.0]]]) >>> G = F.append(F) >>> print(F) {[1.0,1.0,1.0], [1.0,1.0,1.0]}

classmethod concatenate(clas, Flist)

Concatenate all formices in Flist.

This is a class method, not an instance method! >>> F = Formex([[[1,2,3]]],1) >>> print(Formex.concatenate([F,F,F])) {[1.0,2.0,3.0], [1.0,2.0,3.0], [1.0,2.0,3.0]}

Formex.concatenate([F,G,H]) is functionally equivalent with F+G+H. The latter is simpler to write for a list with a few elements. If the list becomes large, or the number of items in the list is not fixed, the concatenate method is easier (and faster). We made it a class method and not a global function, because that would interfere with NumPy’s own concatenate function.

select(idx)

Return a Formex which holds only element with numbers in ids.

idx can be a single element number or a list of numbers or any other index mechanism accepted by numpy’s ndarray

cselect(idx)

Return a Formex without the elements with numbers in ids.

idx can be a single element number or a list of numbers or any other index mechanism accepted by numpy’s ndarray

This is the complementary operation of select

selectNodes(idx)

Return a Formex which holds only some nodes of the parent.

idx is a list of node numbers to select. Thus, if F is a plex 3 Formex representing triangles, the sides of the triangles are given by F.selectNodes([0,1]) + F.selectNodes([1,2]) + F.selectNodes([2,0]) The returned Formex inherits the property of its parent.

points()

Return a Formex containing only the points.

This is obviously a Formex with plexitude 1. It holds the same data as the original Formex, but in another shape: the number of points per element is 1, and the number of elements is equal to the total number of points. The properties are not copied over, since they will usually not make any sense.

The vertices() method returns the same data, but as a Coords object.

vertices()

Return the points of a Formex as a 2dim Coords object.

The return value holds the same coordinate data as the input Formex, but in another shape: (npoints,3).

The points() method returns the same data, but as a Formex.

remove(F)

Return a Formex where the elements in F have been removed.

This is also the subtraction of the current Formex with F. Elements are only removed if they have the same nodes in the same order. This is a slow operation: for large structures, you should avoid it where possible.

whereProp(val)

Return the numbers of the elements with property val.

val is either a single integer, or a list/array of integers. The return value is an array holding all the numbers of all the elements that have the property val, resp. one of the values in val.

If the Formex has no properties, a empty array is returned.

withProp(val)

Return a Formex which holds only the elements with property val.

val is either a single integer, or a list/array of integers. The return value is a Formex holding all the elements that have the property val, resp. one of the values in val. The returned Formex inherits the matching properties.

If the Formex has no properties, a copy with all elements is returned.

splitProp()

Partition a Formex according to its prop values.

Returns a dict with the prop values as keys and the corresponding partitions as values. Each value is a Formex instance. It the Formex has no props, an empty dict is returned.

elbbox()

Return a Formex where each element is replaced by its bbox.

The returned Formex has two points for each element: two corners of the bbox.

removeDuplicate(permutations=True, rtol=0.0001, atol=1e-06)

Return a Formex which holds only the unique elements.

Two elements are considered equal when all its points are (nearly) coincident. By default any permutation of point order is also allowed.

Two coordinate value are considered equal if they are both small compared to atol or if their difference divided by the second value is small compared to rtol.

If permutations is set False, two elements are not considered equal if one’s points are a permutation of the other’s.

test(nodes='all', dir=0, min=None, max=None, atol=0.0)

Flag elements having nodal coordinates between min and max.

This function is very convenient in clipping a Formex in a specified direction. It returns a 1D integer array flagging (with a value 1 or True) the elements having nodal coordinates in the required range. Use where(result) to get a list of element numbers passing the test. Or directly use clip() or cclip() to create the clipped Formex.

The test plane can be defined in two ways, depending on the value of dir. If dir == 0, 1 or 2, it specifies a global axis and min and max are the minimum and maximum values for the coordinates along that axis. Default is the 0 (or x) direction.

Else, dir should be compaitble with a (3,) shaped array and specifies the direction of the normal on the planes. In this case, min and max are points and should also evaluate to (3,) shaped arrays.

nodes specifies which nodes are taken into account in the comparisons. It should be one of the following: - a single (integer) point number (< the number of points in the Formex) - a list of point numbers - one of the special strings: ‘all’, ‘any’, ‘none’ The default (‘all’) will flag all the elements that have all their nodes between the planes x=min and x=max, i.e. the elements that fall completely between these planes. One of the two clipping planes may be left unspecified.

clip(t)

Return a Formex with all the elements where t>0.

t should be a 1-D integer array with length equal to the number of elements of the formex. The resulting Formex will contain all elements where t > 0. This is a convenience function for the user, equivalent to F.select(t>0).

cclip(t)

This is the complement of clip, returning a Formex where t<=0.

circulize(angle)

Transform a linear sector into a circular one.

A sector of the (0,1) plane with given angle, starting from the 0 axis, is transformed as follows: points on the sector borders remain in place. Points inside the sector are projected from the center on the circle through the intersection points of the sector border axes and the line through the point and perpendicular to the bisector of the angle. See Diamatic example.

circulize1()

Transforms the first octant of the 0-1 plane into 1/6 of a circle.

Points on the 0-axis keep their position. Lines parallel to the 1-axis are transformed into circular arcs. The bisector of the first quadrant is transformed in a straight line at an angle Pi/6. This function is especially suited to create circular domains where all bars have nearly same length. See the Diamatic example.

shrink(factor)

Shrinks each element with respect to its own center.

Each element is scaled with the given factor in a local coordinate system with origin at the element center. The element center is the mean of all its nodes. The shrink operation is typically used (with a factor around 0.9) in wireframe draw mode to show all elements disconnected. A factor above 1.0 will grow the elements.

reverse()

Return a Formex where all elements have been reversed.

Reversing an element means reversing the order of its points. This is equivalent to:

self.selectNodes(arange(self.nplex()-1,-1,-1))
mirror(dir=2, pos=0, keep_orig=True)

Reflect a Formex in one of the coordinate directions

This method behaves like reflect(), but adds the reflected part to the original. Setting keep_orig=False makes it behave just like reflect().

replicate(n, dir=0, step=1.0)

Replicate a Formex n times with fixed step in any direction.

Returns a Formex which is the concatenation of n copies, where each copy is equal to the previous one translated over (dir,step), where dir and step are interpreted just like in the translate() method. The first of the copies is equal to the original.

replic(n, step=1.0, dir=0)

Return a Formex with n replications in direction dir with step.

The original Formex is the first of the n replicas.

replic2(n1, n2, t1=1.0, t2=1.0, d1=0, d2=1, bias=0, taper=0)

Replicate in two directions.

n1,n2 number of replications with steps t1,t2 in directions d1,d2 bias, taper : extra step and extra number of generations in direction d1 for each generation in direction d2

rosette(n, angle, axis=2, point=[0.0, 0.0, 0.0])

Return a Formex with n rotational replications with angular step angle around an axis parallel with one of the coordinate axes going through the given point. axis is the number of the axis (0,1,2). point must be given as a list (or array) of three coordinates. The original Formex is the first of the n replicas.

translatem(*args, **kargs)

Multiple subsequent translations in axis directions.

The argument list is a sequence of tuples (axis, step). Thus translatem((0,x),(2,z),(1,y)) is equivalent to translate([x,y,z]). This function is especially conveniant to translate in calculated directions.

extrude(n, step=1.0, dir=0)

Extrude a Formex in one of the axis directions.

Returns a Formex with doubled plexitude.

First the original Formex is translated over n steps of length step in direction dir. Then each pair of subsequent Formices is connected to form a higher plexitude structure.

Currently, this function correctly transforms: point1 to line2, line2 to quad4, tri3 to wedge6, quad4 to hex8.

See the ‘connect’ function for a more versatile tool.

divide(div)

Divide a plex-2 Formex at the values in div.

Replaces each member of the Formex by a sequence of members obtained by dividing the Formex at the relative values specified in div. The values should normally range from 0.0 to 1.0.

As a convenience, if an integer is specified for div, it is taken as a number of divisions for the interval [0..1].

This function only works on plex-2 Formices (line segments).

intersectionWithPlane(p, n)

Return the intersection of a Formex with the plane (p,n).

Currently this only works for plex-2 and plex-3 Formices.

The intersection of the Formex with a plane specified by a point p and normal n is returned. For a plex-2 Formex (lines), the returned Formex will be of plexitude 1 (points). For a plex-3 Formex (triangles) the returned Fomrex has plexitude 2 (lines).

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

copy()

Return a deep copy of the object.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

cutWithPlane(p, n, side='', atol=None, newprops=None)

Cut a Formex with the plane(s) (p,n).

..warning :: This method currently only works for plexitude 2 or 3!

  • p,`n`: a point and normal vector defining the cutting plane. In case of plexitude 3, p and n can be sequences of points and vector, allowing to cut with multiple planes. Both p and n have shape (3) or (npoints,3).

The default return value is a tuple of two Formices of the same plexitude as the input: (Fpos,Fneg), where Fpos is the part of the Formex at the positive side of the plane (as defined by the normal vector), and Fneg is the part at the negative side. Elements of the input Formex that are lying completely on one side of the plane will return unaltered. Elements that are crossing the plane will be cut and split up into multiple parts.

When side = ‘+’ or ‘-‘ (or ‘positive’or ‘negative’), only one of the sides is returned.

The other arguments (atol,newprops) are currently specific for the plexitude. See the cut2AtPlane and cut3AtPlane, which hold the actual implementation of this method.

split(n=1)

Split a Formex in subFormices containing n elements.

The number of elements in the Formex should be a multiple of n. Returns a list of Formices each comprising n elements.

write(fil, sep=' ', mode='w')

Write a Formex to file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Formex is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

classmethod read(clas, fil, sep=' ')

Read a Formex from file.

fil is a filename or a file object. If the file is in a valid Formex file format, the Formex is read and returned. Otherwise, None is returned. Valid Formex file formats are described in the manual.

classmethod fromstring(clas, fil, sep=' ', nplex=1, ndim=3, count=-1)

Create a Formex from coodinates in a string.

This uses the Coords.fromstring() method to read coordinates from a string and restructures them into a Formex of the specified plexitude.

fil: a string containing a single sequence of float numbers separated
by whitespace and a possible separator string.
sep: the separator used between the coordinates. If not a space,
all extra whitespace is ignored.
ndim: number of coordinates per point. Should be 1, 2 or 3 (default).
If 1, resp. 2, the coordinate string only holds x, resp. x,y values.
count: total number of coordinates to read. This should be a multiple
of 3. The default is to read all the coordinates in the string. count can be used to force an error condition if the string does not contain the expected number of values.

The return value is a Coords object.

classmethod fromfile(clas, fil, sep=' ', nplex=1)

Read the coordinates of a Formex from a file

nnodes()

Return the number of points in the formex.

This is the product of the number of elements in the formex with the number of nodes per element.

rep(n, step=1.0, dir=0)

Return a Formex with n replications in direction dir with step.

The original Formex is the first of the n replicas.

ros(n, angle, axis=2, point=[0.0, 0.0, 0.0])

Return a Formex with n rotational replications with angular step angle around an axis parallel with one of the coordinate axes going through the given point. axis is the number of the axis (0,1,2). point must be given as a list (or array) of three coordinates. The original Formex is the first of the n replicas.

unique(permutations=True, rtol=0.0001, atol=1e-06)

Return a Formex which holds only the unique elements.

Two elements are considered equal when all its points are (nearly) coincident. By default any permutation of point order is also allowed.

Two coordinate value are considered equal if they are both small compared to atol or if their difference divided by the second value is small compared to rtol.

If permutations is set False, two elements are not considered equal if one’s points are a permutation of the other’s.

Functions defined in module formex

formex.lpattern(s, connect=True)

Return a line segment pattern created from a string.

This function creates a list of line segments where all points lie on a regular grid with unit step. The first point of the list is [0,0,0]. Each character from the input string is interpreted as a code specifying how to move to the next point. Currently defined are the following codes: 1..8 move in the x,y plane 9 remains at the same place 0 = goto origin [0,0,0] + = go back to origin without creating a line segment When looking at the plane with the x-axis to the right, 1 = East, 2 = North, 3 = West, 4 = South, 5 = NE, 6 = NW, 7 = SW, 8 = SE. Adding 16 to the ordinal of the character causes an extra move of +1 in the z-direction. Adding 48 causes an extra move of -1. This means that ‘ABCDEFGHI’, resp. ‘abcdefghi’, correspond with ‘123456789’ with an extra z +/-= 1. This gives the following schema:

    z+=1             z unchanged            z -= 1

F    B    E          6    2    5         f    b    e 
     |                    |                   |     
     |                    |                   |     
C----I----A          3----9----1         c----i----a  
     |                    |                   |     
     |                    |                   |     
G    D    H          7    4    8         g    d    h

The special character ‘/’ can be put before any character to make the move without inserting an element. The effect of any other character is undefined.

The resulting list is directly suited to initialize a Formex.

formex.pointsAt(F, t)

Return the points of a plex-2 Formex at times t.

F is a plex 2 Formex and t is an array with F.nelems() float values which are interpreted as local parameters along the edges of the Formex, such that the first node has value 0.0 and the last has value 1.0. The return value is a coords.Coords array with the points at values t.

formex.intersectionLinesWithPlane(F, p, n, atol=0.0001)

Return the intersection lines of a plex-3 Formex with plane (p,n).

F is a Formex of plexitude 3. p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components. atol is a tolerance factor defining whether an edge is intersected by the plane.

formex.cut2AtPlane(F, p, n, side='', atol=None, newprops=None)

Returns all elements of the Formex cut at plane.

F is a Formex of plexitude 2. p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components.

The return value is:

  • with side = ‘+’ or ‘-‘ or ‘positive’or ‘negative’ : a Formex of the same plexitude with all elements located completely at the positive/negative side of the plane(s) (p,n) retained, all elements lying completely at the negative/positive side removed and the elements intersecting the plane(s) replaced by new elements filling up the parts at the positive/negative side.
  • with side = ‘’: two Formices of the same plexitude, one representing the positive side and one representing the negative side.

To avoid roundoff errors and creation of very small elements, a tolerance can be specified. Points lying within the tolerance distance will be considered lying in the plane, and no cutting near these points.

formex.cut3AtPlane(F, p, n, side='', atol=None, newprops=None)

Returns all elements of the Formex cut at plane(s).

F is a Formex of plexitude 3. p is a point or a list of points. n is the normal vector to a plane or a list of normal vectors. Both p and n have shape (3) or (npoints,3).

The return value is:

  • with side = ‘+’ or ‘-‘ or ‘positive’or ‘negative’ : a Formex of the same plexitude with all elements located completely at the positive/negative side of the plane(s) (p,n) retained, all elements lying completely at the negative/positive side removed and the elements intersecting the plane(s) replaced by new elements filling up the parts at the positive/negative side.
  • with side = ‘’: two Formices of the same plexitude, one representing the positive side and one representing the negative side.

Let dist be the signed distance of the vertices to a plane. The elements located completely at the positive or negative side of a plane have three vertices for which |dist| > atol. The elements intersecting a plane can have one or more vertices for which |dist| < atol. These vertices are projected on the plane so that their distance is zero.

If the Formex has a property set, the new elements will get the property numbers defined in newprops. This is a list of 7 property numbers flagging elements with following properties:

  1. no vertices with |dist| < atol, triangle after cut
  2. no vertices with |dist| < atol, triangle 1 from quad after cut
  3. no vertices with |dist| < atol, triangle 2 from quad after cut
  4. one vertex with |dist| < atol, two vertices at pos. or neg. side
  5. one vertex with |dist| < atol, one vertex at pos. side, one at neg.
  6. two vertices with |dist| < atol, one vertex at pos. or neg. side
  7. three vertices with |dist| < atol
formex.cutElements3AtPlane(F, p, n, newprops=None, side='', atol=0.0)

This function needs documentation.

Should it be called by the user? or only via cut3AtPlane? For now, lets suppose the last, so no need to check arguments here.

newprops should be a list of 7 values: each an integer or None side is either ‘+’, ‘-‘ or ‘’

formex.connect(Flist, nodid=None, bias=None, loop=False)

Return a Formex which connects the formices in list.

Flist is a list of formices, nodid is an optional list of nod ids and bias is an optional list of element bias values. All lists should have the same length. The returned Formex has a plexitude equal to the number of formices in list. Each element of the Formex consist of a node from the corresponding element of each of the formices in list. By default this will be the first node of that element, but a nodid list may be given to specify the node id to be used for each of the formices. Finally, a list of bias values may be given to specify an offset in element number for the subsequent formices. If loop==False, the order of the Formex will be the minimum order of the formices in Flist, each minus its respective bias. By setting loop=True however, each Formex will loop around if its end is encountered, and the order of the result is the maximum order in Flist.

formex.interpolate(F, G, div, swap=False)

Create interpolations between two formices.

F and G are two Formices with the same shape. v is a list of floating point values. The result is the concatenation of the interpolations of F and G at all the values in div. An interpolation of F and G at value v is a Formex H where each coordinate Hijk is obtained from: Hijk = Fijk + v * (Gijk-Fijk). Thus, a Formex interpolate(F,G,[0.,0.5,1.0]) will contain all elements of F and G and all elements with mean coordinates between those of F and G.

As a convenience, if an integer is specified for div, it is taken as a number of divisions for the interval [0..1]. Thus, interpolate(F,G,n) is equivalent with interpolate(F,G,arange(0,n+1)/float(n))

The swap argument sets the order of the elements in the resulting Formex. By default, if n interpolations are created of an m-element Formex, the element order is in-Formex first (n sequences of m elements). If swap==True, the order is swapped and you get m sequences of n interpolations.

pyformex-0.8.6/pyformex/doc/html/ref/geometry.html0000644000211500021150000011756611705104253022127 0ustar benebene00000000000000 7. geometry — A generic interface to the Coords transformation methods — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

6. colors — Definition of some RGB colors and color conversion functions

Next topic

8. connectivity — A class and functions for handling nodal connectivity.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

7. geometry — A generic interface to the Coords transformation methods

This module defines a generic Geometry superclass which adds all the possibilities of coordinate transformations offered by the Coords class to the derived classes.

Classes defined in module geometry

class geometry.Geometry

A generic geometry object allowing transformation of coords sets.

The Geometry class is a generic parent class for all geometric classes, intended to make the Coords transformations available without explicit declaration. This class is not intended to be used directly, only through derived classes. Examples of derived classes are Formex, Mesh, Curve.

There is no initialization to be done when constructing a new instance of this class. The class just defines a set of methods which operate on the attribute coords, which should be a Coords object. Most of the transformation methods of the Coords class are thus exported through the Geometry class to its derived classes, and when called, will get executed on the coords attribute. The derived class constructor should make sure that the coords attribute exists, has the proper type and contains the coordinates of all the points that should get transformed under a Coords transformation.

Derived classes can (and in most cases should) declare a method _set_coords(coords) returning an object that is identical to the original, except for its coords being replaced by new ones with the same array shape.

The Geometry class provides two possible default implementations:

  • _set_coords_inplace sets the coords attribute to the provided new coords, thus changing the object itself, and returns itself,
  • _set_coords_copy creates a deep copy of the object before setting the coords attribute. The original object is unchanged, the returned one is the changed copy.

When using the first method, a statement like `B = A.scale(0.5)` will result in both A and B pointing to the same scaled object, while with the second method, A would still be the untransformed object. Since the latter is in line with the design philosophy of pyFormex, it is set as the default _set_coords method. Most derived classes that are part of pyFormex however override this default and implement a more efficient copy method.

The following Geometry methods return the value of the same method applied on the coords attribute. Refer to the correponding coords.Coords method for their precise arguments.

x(), y(), z(), bbox(), center(), centroid(), sizes(), dsize(), bsphere(), distanceFromPlane(), distanceFromLine(), distanceFromPoint(), directionalSize(), directionalWidth(), directionalExtremes(), __str__().

The following Coords transformation methods can be directly applied to a Geometry object or a derived class object. The return value is a new object identical to the original, except for the coordinates, which will have been transformed by the specified method. Refer to the correponding coords.Coords method in for the precise arguments of these methods:

scale(), translate(), centered(), rotate(), shear(), reflect(), affine(), position(), cylindrical(), hyperCylindrical(), toCylindrical(), spherical(), superSpherical(), toSpherical(), bump(), bump1(), bump2(), flare(), map(), map1(), mapd(), replace(), swapAxes(), rollAxes(), projectOnPlane(), projectOnSphere(), projectOnCylinder(), isopar(), transformCS(), addNoise(), rot(), trl().

copy()

Return a deep copy of the object.

scale(*args, **kargs)

Apply ‘scale’ transformation to the Geometry object.

See coords.Coords.scale() for details.

resized(size=1.0, tol=1e-05)

Return a scaled copy of the Formex with given size in all directions.

If a direction has zero size, it is not rescaled.

translate(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

centered(*args, **kargs)

Apply ‘centered’ transformation to the Geometry object.

See coords.Coords.centered() for details.

align(*args, **kargs)

Apply ‘align’ transformation to the Geometry object.

See coords.Coords.align() for details.

rotate(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

shear(*args, **kargs)

Apply ‘shear’ transformation to the Geometry object.

See coords.Coords.shear() for details.

reflect(*args, **kargs)

Apply ‘reflect’ transformation to the Geometry object.

See coords.Coords.reflect() for details.

affine(*args, **kargs)

Apply ‘affine’ transformation to the Geometry object.

See coords.Coords.affine() for details.

position(*args, **kargs)

Apply ‘position’ transformation to the Geometry object.

See coords.Coords.position() for details.

cylindrical(*args, **kargs)

Apply ‘cylindrical’ transformation to the Geometry object.

See coords.Coords.cylindrical() for details.

hyperCylindrical(*args, **kargs)

Apply ‘hyperCylindrical’ transformation to the Geometry object.

See coords.Coords.hyperCylindrical() for details.

toCylindrical(*args, **kargs)

Apply ‘toCylindrical’ transformation to the Geometry object.

See coords.Coords.toCylindrical() for details.

spherical(*args, **kargs)

Apply ‘spherical’ transformation to the Geometry object.

See coords.Coords.spherical() for details.

superSpherical(*args, **kargs)

Apply ‘superSpherical’ transformation to the Geometry object.

See coords.Coords.superSpherical() for details.

egg(*args, **kargs)

Apply ‘egg’ transformation to the Geometry object.

See coords.Coords.egg() for details.

toSpherical(*args, **kargs)

Apply ‘toSpherical’ transformation to the Geometry object.

See coords.Coords.toSpherical() for details.

bump(*args, **kargs)

Apply ‘bump’ transformation to the Geometry object.

See coords.Coords.bump() for details.

bump1(*args, **kargs)

Apply ‘bump1’ transformation to the Geometry object.

See coords.Coords.bump1() for details.

bump2(*args, **kargs)

Apply ‘bump2’ transformation to the Geometry object.

See coords.Coords.bump2() for details.

flare(*args, **kargs)

Apply ‘flare’ transformation to the Geometry object.

See coords.Coords.flare() for details.

map(*args, **kargs)

Apply ‘map’ transformation to the Geometry object.

See coords.Coords.map() for details.

map1(*args, **kargs)

Apply ‘map1’ transformation to the Geometry object.

See coords.Coords.map1() for details.

mapd(*args, **kargs)

Apply ‘mapd’ transformation to the Geometry object.

See coords.Coords.mapd() for details.

replace(*args, **kargs)

Apply ‘replace’ transformation to the Geometry object.

See coords.Coords.replace() for details.

swapAxes(*args, **kargs)

Apply ‘swapAxes’ transformation to the Geometry object.

See coords.Coords.swapAxes() for details.

rollAxes(*args, **kargs)

Apply ‘rollAxes’ transformation to the Geometry object.

See coords.Coords.rollAxes() for details.

projectOnPlane(*args, **kargs)

Apply ‘projectOnPlane’ transformation to the Geometry object.

See coords.Coords.projectOnPlane() for details.

projectOnSphere(*args, **kargs)

Apply ‘projectOnSphere’ transformation to the Geometry object.

See coords.Coords.projectOnSphere() for details.

projectOnCylinder(*args, **kargs)

Apply ‘projectOnCylinder’ transformation to the Geometry object.

See coords.Coords.projectOnCylinder() for details.

isopar(*args, **kargs)

Apply ‘isopar’ transformation to the Geometry object.

See coords.Coords.isopar() for details.

transformCS(*args, **kargs)

Apply ‘transformCS’ transformation to the Geometry object.

See coords.Coords.transformCS() for details.

addNoise(*args, **kargs)

Apply ‘addNoise’ transformation to the Geometry object.

See coords.Coords.addNoise() for details.

rot(*args, **kargs)

Apply ‘rotate’ transformation to the Geometry object.

See coords.Coords.rotate() for details.

trl(*args, **kargs)

Apply ‘translate’ transformation to the Geometry object.

See coords.Coords.translate() for details.

write(fil, sep=' ', mode='w')

Write a Geometry to a .pgf file.

If fil is a string, a file with that name is opened. Else fil should be an open file. The Geometry is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning.

Functions defined in module geometry

pyformex-0.8.6/pyformex/doc/html/ref/collection.html0000644000211500021150000002604511705104247022421 0ustar benebene00000000000000 57. collection — Tools for handling collections of elements belonging to multiple parts. — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Previous topic

56. odict — Specialized dictionary type structures.

Next topic

58. config — A general yet simple configuration class.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

57. collection — Tools for handling collections of elements belonging to multiple parts.

This module defines the Collection class.

Classes defined in module collection

class collection.Collection

A collection is a set of (int,int) tuples.

The first part of the tuple has a limited number of values and are used as the keys in a dict. The second part can have a lot of different values and is implemented as an integer array with unique values. This is e.g. used to identify a set of individual parts of one or more OpenGL actors.

add(data, key=-1)

Add new data to the collection.

data can be a 2d array with (key,val) tuples or a 1-d array of values. In the latter case, the key has to be specified separately, or a default value will be used.

set(data, key=-1)

Set the collection to the specified data.

This is equivalent to clearing the corresponding keys before adding.

remove(data, key=-1)

Remove data from the collection.

has_key(key)

Check whether the collection has an entry for the key.

get(key, default=[])

Return item with given key or default.

keys()

Return a sorted array with the keys

items()

Return a zipped list of keys and values.

Functions defined in module collection

pyformex-0.8.6/pyformex/doc/html/running.html0000644000211500021150000003210011705104256021157 0ustar benebene00000000000000 Running pyFormex — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Table Of Contents

Previous topic

pyFormex user guide

Next topic

The Graphical User Interface

[FSF Associate Member]

Valid XHTML 1.0 Transitional

Running pyFormex

To run pyFormex, simply enter the command pyformex in a terminal window. This will start the Graphical User Interface (GUI), from where you can launch examples or load, edit and run your own scripts.

The installation procedure may have installed into your desktop menu or even have created a start button in the desktop panel. These provide convenient shortcuts to start the GUI by the click of a mouse button.

The program takes some optional command line arguments, that modify the behaviour of the program. Appendix Command line options gives a full list of all options. For normal use however you will seldom need to use any of them. Therefore, we will only explain here the more commonly used ones.

By default, sends diagnostical and informational messages to the terminal from which the program was started. Sometimes this may be inconvenient, e.g. because the user has no access to the starting terminal. You can redirect these messages to the message window of the GUI by starting pyformex with the command pyformex --redirect. The desktop starters installed by the installation procedure use this option.

In some cases the user may want to use the mathematical power of without the GUI. This is e.g. useful to run complex automated procedures from a script file. For convenience, will automatically enter this batch mode (without GUI) if the name of a script file was specified on the command line; when a script file name is absent, start in GUI mode. Even when specifying a script file, You can still force the GUI mode by adding the option –gui to the command line.

Command line options

The following is a complete list of the options for the pyformex command.This output can also be generated by the command pyformex --help.

Usage
=====
  pyformex [<options>] [ [ scriptname [scriptargs] ] ...]

pyFormex is a tool for generating, manipulating and transforming large
geometrical models of 3D structures by sequences of mathematical
transformations.

Options
=======
--gui                   start the GUI (default if no scriptfile argument is
                        given)
--nogui                 do not load the GUI (default if a scriptfile argument
                        is given)
--interactive, -i       Go into interactive mode after processing the command
                        line parameters. This is implied by the --gui option.
--force-dri             Force use of Direct Rendering
--force-nodri           Disables the Direct Rendering
--uselib                Use the pyFormex C lib if available. This is the
                        default.
--nouselib              Do not use the pyFormex C-lib.
--norst2html            Do not try to convert rst messages to html before
                        displaying.
--config=CONFIG         Use file CONFIG for settings
--nodefaultconfig       Skip the default site and user config files. This
                        option can only be used in conjunction with the
                        --config option.
--redirect              Redirect standard output to the message board (ignored
                        with --nogui)
--debug                 display debugging info to sys.stdout
--debuglevel=DEBUG      display debugging info to sys.stdout
--newviewports          Use the new multiple viewport canvas implementation.
                        This is an experimental feature only intended for
                        developers.
--testmodule=TESTMODULE
                        Run the docstring tests for module TESTMODULE.
                        TESTMODULE is the name of the module, using . as path
                        separator.
--testcamera            Print camera settings whenever they change.
--testexecutor          test alternate executor: only for developers!
--fastnurbs             test C library nurbs drawing: only for developers!
--listfiles             list the pyformex Python source files.
--search                search the pyformex source for a specified pattern and
                        exit. This can optionally be followed by -- followed
                        by options for the grep command. The final argument is
                        the pattern to search.
--remove                remove the pyformex installation and exit
--whereami              show where the pyformex package is installed and exit
--detect                show detected helper software and exit
--version               show program's version number and exit
--help, -h              show this help message and exit

Running without the GUI

If you start with the --nogui option, no Graphical User Interface is created. This is extremely useful to run automated scripts in batch mode. In this operating mode, will interprete all arguments remaining after interpreting the options, as filenames of scripts to be run (and possibly arguments to be interpreted by these scripts). Thus, if you want to run a script myscript.py in batch mode, just give the command pyformex myscript.py.

The running script has access to the remaining arguments in the global list variable argv. The script can use any arguments of it and pop them of the list. Any arguments remaining in the argv list when the script finishes, will be used for another execution cycle. This means that the first remaining argument should again be a script.

pyformex-0.8.6/pyformex/doc/html/license.html0000644000211500021150000013731111705104246021132 0ustar benebene00000000000000 GNU GENERAL PUBLIC LICENSE — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Table Of Contents

Previous topic

BuMPix Live GNU/Linux system

Next topic

About the pyFormex documentation

[FSF Associate Member]

Valid XHTML 1.0 Transitional

GNU GENERAL PUBLIC LICENSE

Version 3, 29 June 2007

Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

Preamble

The GNU General Public License is a free, copyleft license for software and other kinds of works.

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program–to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

For the developers’ and authors’ protection, the GPL clearly explains that there is no warranty for this free software. For both users’ and authors’ sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users’ freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general- purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.

The precise terms and conditions for copying, distribution and modification follow.

Terms and Conditions

0. Definitions.

“This License” refers to version 3 of the GNU General Public License.

“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.

To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.

A “covered work” means either the unmodified Program or a work based on the Program.

To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

1. Source Code.

The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.

A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work’s System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

The Corresponding Source for a work in source code form is that same work.

2. Basic Permissions.

All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

4. Conveying Verbatim Copies.

You may convey verbatim copies of the Program’s source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

5. Conveying Modified Source Versions.

You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

  • a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
  • b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
  • c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
  • d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation’s users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

6. Conveying Non-Source Forms.

You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

  • a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
  • b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
  • c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
  • d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
  • e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

7. Additional Terms.

“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

  • a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
  • b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
  • c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
  • d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
  • e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
  • f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

8. Termination.

You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

9. Acceptance Not Required for Having Copies.

You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

10. Automatic Licensing of Downstream Recipients.

Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party’s predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

11. Patents.

A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor’s “contributor version”.

A contributor’s “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor’s essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient’s use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

12. No Surrender of Others’ Freedom.

If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

13. Use with the GNU Affero General Public License.

Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.

14. Revised Versions of this License.

The Free Software Foundation may publish revised and/or new versions of the GNU 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 Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy’s public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

15. Disclaimer of Warranty.

THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. Limitation of Liability.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

17. Interpretation of Sections 15 and 16.

If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

End of Terms and Conditions

How to Apply These Terms to Your New Programs

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.

<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year>  <name of author>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:

<program>  Copyright (C) <year>  <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

The hypothetical commands show w and show c should show the appropriate parts of the General Public License. Of course, your program’s commands might be different; for a GUI interface, you would use an “about box”.

You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.

The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>.

pyformex-0.8.6/pyformex/doc/html/widgets.html0000644000211500021150000002010411705104257021147 0ustar benebene00000000000000 Using Widgets — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Table Of Contents

Previous topic

Assigning properties to geometry

Next topic

pyFormex plugins

[FSF Associate Member]

Valid XHTML 1.0 Transitional

Using Widgets

Warning

This document still needs to be written!

Abstract

This chapter gives an overview of the specialized widgets in pyFormex and how to use them to quickly create a specialized graphical interface for you application.

The input dialog

The user menu

Other widgets

pyformex-0.8.6/pyformex/doc/html/bumpix.html0000644000211500021150000005303311705104245021011 0ustar benebene00000000000000 BuMPix Live GNU/Linux system — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Table Of Contents

Previous topic

pyFormex file formats

Next topic

GNU GENERAL PUBLIC LICENSE

[FSF Associate Member]

Valid XHTML 1.0 Transitional

BuMPix Live GNU/Linux system

Abstract

This document gives a short introduction on the BuMPix Live GNU/Linux system and how to use it to run pyFormex directly on nearly any computer system without having to install it.

What is BuMPix

Bumpix Live is a fully featured GNU/Linux system including pyFormex that can be run from a single removable medium such as a CD or a USB key. BuMPix is still an experimental project, but new versions are already produced at regular intervals. While those are primarily intended for our students, the install images are made available for download on the Bumpix Live GNU/Linux FTP server, so that anyone can use them.

All you need to use the Bumpix Live GNU/Linux is some proper PC hardware: the system boots and runs from the removable medium and leaves everything that is installed on the hard disk of the computer untouched.

Because the size of the image (since version 0.4) exceeds that of a CD, we no longer produce CD-images (.iso) by default, but some older images remain avaliable on the server. New (reduced) CD images will only be created on request. On the other hand, USB-sticks of 2GB and larger have become very affordable and most computers nowadays can boot from a USB stick. USB sticks are also far more easy to work with than CD’s: you can create a persistent partition where you can save your changes, while a CD can not be changed.

You can easily take your USB stick with you wherever you go, plug it into any available computer, and start or continue your previous pyFormex work. Some users even prefer this way to run pyFormex for that single reason. The Live system is also an excellent way to test and see what pyFormex can do for you, without having to install it. Or to demonstrate pyFormex to your friends or colleagues.

Obtain a BuMPix Live bootable medium

Download BuMPix

The numbering scheme of the BuMPix images is independent from the pyFormex numbering. Just pick the latest BuMPix image to get the most recent pyFormex available on USB stick. After you downloaded the .img file, write it to a USB stick as an image, not as file! Below, you find instructions on how to do this on a GNU/Linux system or on a Windows platform.

Warning

Make sure you’ve got the device designation correct, or you might end up overwriting your whole hard disk!

Also, be aware that the USB stick will no longer be usable to store your files under Windows.

Create the BuMPix USB stick under GNU/Linux

If you have an existing GNU/Linux system available, you can write the downloaded image to the USB-stick using the command:

dd if=bumpix-VERSION.img of=USBDEV

where bumpix-VERSION.img is the downloaded file and USBDEV is the device corresponding to your USB key. This should be /dev/sda or /dev/sdb or, generally, /dev/sd? where ? is a single character from a-z. The value you should use depends on your hardware. You can find out the correct value by giving the command dmesg after you have plugged in the USB key. You will see messages mentioning the correct [sd?] device.

The dd command above will overwrite everything on the specified device, so copy your files off the stick before you start, and make sure you’ve got the device designation correct.

Create the BuMPix USB stick under Windows

If you have no GNU/Linux machine available to create the USB key, there are ways to do this under Windows as well. We recommend to use dd for Windows. You can then proceed as follows.

  • Download dd for Windows to a folder, say C:\\download\ddWrite.

  • Download the latest BuMPix image to the same folder.

  • Mount the target USB stick and look for the number of the mounted USB. This can be done with the command c:\\download\ddWrite dd --list. Look at the description (Removable media) and the size to make sure you’ve got the correct harddisk designation (e.g. harddisk1).

  • Write the image to the USB stick with the command, substituting the harddisk designation found above:

    dd if=c:\download\ddwrite\bumpix-0.4-b1.img of=\\?\device\harddisk1\partition0 bs=1M --progress

The dd command above will overwrite everything on the specified device, so copy your files off the stick before you start, and make sure you’ve got the device designation correct.

Buy a USB stick with BuMPix

Alternatively,

  • if you do not succeed in properly writing the image to a USB key, or
  • if you just want an easy solution without any install troubles, or
  • if you want to financially support the further development of pyFormex, or
  • if you need a large number of pyFormex USB installations,

you may be happy to know that we can provide ready-made BuMPix USB sticks with the pyformex.org logo at a cost hardly exceeding that of production and distribution. If you think this is the right choice for you, just email us for a quotation.

pyFormex USB stick with BuMPix Live GNU/Linux

Boot your BuMPix system

Once the image has been written, reboot your computer from the USB stick. You may have to change your BIOS settings or use the boot menu to do that. On success, you will have a full GNU/Linux system running, containing pyFormex ready to use. There is even a start button in the toolbar at the bottom.

Warning

More detailed documentation on how to use the system is currently under preparation. For now, feel free to email us if you have any problems or urgent questions. But first check that your question is not solved in the FAQ below.

FAQ

A collection of hints and answers to frequently asked questions.

  1. The initial user name is user and the password live.

  2. On shutdown/reboot, the system pauses with the advice to remove the USB stick before hitting ENTER to proceed. We advice not to do this (especially when running in PERSISTENT mode): instead first hit ENTER and remove the USB stick when the screen goes black.

  3. BuMPix 0.7.0 may contain a few user configuration files with incorrect owner settings. As a result some XFCE configuration may not be permanent. To solve the problem, you should run the following command in a terminal

    sudo chown -R user:user /home/user
  4. For BuMPix 0.7.0 (featuring pyFormex 0.8.4) with XFCE desktop, some users have reported occaional problems with starting the window manager. Windows remain undecorated and the mouse cursor keeps showing the BUSY symbol. This is probably caused by an improper previous shutdown and can be resolved as follows: open a terminal and enter the command xfwm4. That will start up the window manager for your current session and most likely will also remove the problem for your next sessions.

  5. Install the latest pyFormex version from the SVN repository. The BuMPix stick contains a script pyformex-svn under the user’s bin directory to install a pyFormex version directly from the SVN repository. However, the repository has been relocated to a ne server and the script might still contain the old location. You can download a fixed script from ftp://bumps.ugent.be/pub/pyformex/pyformex-svn.

Upgrade the pyFormex version on a BuMPix-0.6.1 USB stick

This describes how you can upgrade (or downgrade) the pyFormex version on your BuMPix 0.6.1 USB key. You need to have network connection to do this.

  • First, we need to fix some file ownerships. Open a Terminal and do the following

    sudo -i
    chown -R user:user /home/user
    exit
  • Then, add your own bin directory to the PATH:

    echo 'export PATH=~/bin:$PATH' >> ~/.bash_profile
  • Change the configuration of your teminal. Click Edit -> Profiles -> Edit -> Title and Command and check the option ‘Run command as a login shell’.

  • Close the terminal and open a new one. Check that the previous operation went correct:

    echo $PATH
  • This should start with ‘/home/user/bin’. If ok, then do:

    cd bin
    chmod +x pyformex-svn
    ls
  • You should now see a green ‘pyformex-svn’ script. Execute it as follows:

    ./pyformex-svn install makelib symlink
    ls
  • If everything went well, you should now also have a blue ‘pyformex’ link. Test it:

    cd ..
    pyformex
  • The latest svn version of pyFormex should start. If ok, close it and you can make this the default version to start from the pyFormex button in the top panel. Right click on the button, then ‘Properties’. Change the Command to:

    bin/pyformex --redirect
    
  • Now you should always have the updated pyformex running, from the command line as well as from the panel button. Next time you want to upgrade (or downgrade), you can just do:

    cd pyformex-svn
    svn up
  • or, for a downgrade, add a specific revision number:

    svn up -r 1833
pyformex-0.8.6/pyformex/doc/html/faq.html0000644000211500021150000005632611705104246020265 0ustar benebene00000000000000 pyFormex FAQ ‘n TRICKS — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Table Of Contents

Previous topic

61. timer — A timer class.

Next topic

pyFormex file formats

[FSF Associate Member]

Valid XHTML 1.0 Transitional

pyFormex FAQ ‘n TRICKS

Date:January 16, 2012
Version:0.8.6
Author:Benedict Verhegghe <benedict.verhegghe@ugent.be>

Abstract

This chapter answers some frequently asked questions about pyFormex and present some nice tips to solve common problems. If you have some question that you want answered, or want to present a original solution to some problem, feel free to communicate it to us (by preference via the pyFormex Support tracker) and we’ll probably include it in the next version of this FAQ.

FAQ

  1. How was the pyFormex logo created?

    We used the GNU Image Manipulation Program (GIMP). It has a wide variety of scripts to create logos. With newer versions (>= 2.6) use the menu Fille‣Create‣Logos‣Alien-neon. With older versions (<=2.4) use Xtra‣Script-Fu‣Logos‣Alien-neon.

    In the Alien Neon dialog specify the following data:

    Text: pyFormex
    Font Size: 150
    Font: Blippo-Heavy
    Glow Color: 0xFF3366
    Background Color: 0x000000
    Width of Bands: 2
    Width of Gaps: 2
    Number of Bands: 7
    Fade Away: Yes

    Press OK to create the logo. Then switch off the background layer and save the image in PNG format. Export the image with Save Background Color option switched off!

  2. How was the pyFormex favicon created? With FTGL, save as icon, handedited .xpm in emacs to set background color to None (transparent), then converted to .png and .ico with convert.

  3. Why is pyFormex written in Python?

    Because

    • it is very easy to learn (See the Python website)
    • it is extremely powerful (More on Python website)

    Being a scripting language without the need for variable declaration, it allows for quick program development. On the other hand, Python provides numerous interfaces with established compiled libraries, so it can be surprisingly fast.

  4. Is an interpreted language like Python fast enough with large data models?

    See the question above.

    Note

    We should add something about NumPy and the pyFormex C-library.

TRICKS

  1. Use your script path as the current working directory

    Start your script with the following:

    chdir(__file__)
    

    When executing a script, pyFormex sets the name of the script file in a variable __file__ passed with the global variables to the execution environment of the script.

  2. Import modules from your own script directories

    In order for Python to find the modules in non-standard locations, you should add the directory path of the module to the sys.path variable.

    A common example is a script that wants to import modules from the same directory where it is located. In that case you can just add the following two lines to the start of your script:

    import os,sys
    sys.path.insert(0,os.dirname(__file__))
    
  3. Automatically load plugin menus on startup

    Plugin menus can be loaded automatically on pyFormex startup, by adding a line to the [gui] section of your configuration file (~/.pyformexrc):

    [gui]
    plugins = ['surface_menu', 'formex_menu']
    
  4. Automatically execute your own scripts on startup

    If you create your own pugin menus for pyFormex, you cannot autoload them like the regular plugin menus from the distribution, because they are not in the plugin directory of the installation. Do not be tempted to put your own files under the installation directory (even if you can acquire the permissions to do so), because on removal or reinstall your files might be deleted! You can however automatically execute your own scripts by adding their full path names in the autorun variable of your configuration file

    autorun = '/home/user/myscripts/startup/'
    

    This script will then be run when the pyFormex GUI starts up. You can even specify a list of scripts, which will be executed in order. The autorun scripts are executed as any other pyFormex script, before any scripts specified on the command line, and before giving the input focus to the user.

  5. Multiple viewports with unequal size

    The multiple viewports are ordered in a grid layout, and you can specify relative sizes for the different columns and/or rows of viewports. You can use setColumnStretch and setRowStretch to give the columns a relative stretch compared toi the other ones. The following example produces 4 viewports in a 2x2 layout, with the right column(1) having double width of the left one(0), while the bottom row has a height equal to 1.5 times the height of the top row

    layout(4)
    pf.GUI.viewports.setColumnStretch(0,1)
    pf.GUI.viewports.setColumnStretch(1,2)
    pf.GUI.viewports.setRowStretch(0,2)
    pf.GUI.viewports.setRowStretch(1,3)
    
  6. Activate pyFormex debug messages from your script

    import pyformex
    pyformex.options.debug = True
    
  7. Get a list of all available image formats

    import gui.image
    print image.imageFormats()
    
  8. Create a movie from a sequence of recorded images

    The multisave option allows you to easily record a series of images while working with pyFormex. You may want to turn this sequence into a movie afterwards. This can be done with the mencoder and/or ffmpeg programs. The internet provides comprehensive information on how to use these video encoders.

    If you are looking for a quick answer, however, here are some of the commands we have often used to create movies.

    • Create MNPG movies from PNG To keep the quality of the PNG images in your movie, you should not encode them into a compressed format like MPEG. You can use the MPNG codec instead. Beware though that uncompressed encodings may lead to huge video files. Also, the MNPG is (though freely available), not installed by default on Windows machines.

      Suppose you have images in files image-000.png, image-001.png, .... First, you should get the size of the images (they all should have the same size). The command

      file image*.png

      will tell you the size. Then create movie with the command

      mencoder mf://image-*.png -mf w=796:h=516:fps=5:type=png -ovc copy -oac copy -o movie1.avi

      Fill in the correct width(w) and height(h) of the images, and set the frame rate(fps). The result will be a movie movie1.avi.

    • Create a movie from (compressed) JPEG images. Because the compressed format saves a lot of space, this will be the prefered format if you have lots of image files. The quality of the compressed image movie will suffer somewhat, though.

      ffmpeg -r 5 -b 800 -i image-%03d.jpg movie.mp4
  9. Install the gl2ps extension

    Note

    This belongs in Installing pyFormex

    Saving images in EPS format is done through the gl2ps library, which can be accessed from Python using wrapper functions. Recent versions of pyFormex come with an installation script that will also generate the required Python interface module.

    Warning

    The older python-gl2ps-1.1.2.tar.gz available from the web is no longer supported

    You need to have the OpenGL header files installed in order to do this (on Debian: apt-get install libgl1-mesa-dev).

  10. Permission denied error when running calpy simulation

    If you have no write permission in your current working directory, running a calpy simulation will result in an error like this:

    fil = file(self.tempfilename,'w')
    IOError
    :
    [Errno 13] Permission denied: 'calpy.tmp.part-0'

    You can fix this by changing your current working directory to a path where you have write permission (e.g. your home directory). You can do this using the File->Change workdir menu option. The setting will be saved when you leave pyFormex (but other scripts might change the setting again).

  11. Reading back old Project (.pyf) files

    When the implementation of some pyFormex class changes, or when the location of a module is changed, an error may result when trying to read back old Project (.pyf) files. While in principle it is possible to create the necessary interfaces to read back the old data and transform them to new ones, our current policy is to not do this by default for all classes and all changes. That would just require too much resources for maybe a few or no cases occurring. We do provide here some guidelines to help you with solving the problems yourself. And if you are not able to fix it, just file a support request at our Support tracker and we will try to help you.

    If the problem is with a changed implementation of a class, it can usually be fixed by adding an appropriate __set_state__ method to the class. Currently we have this for Formex and Mesh classes. Look at the code in formex.py and mesh.py respectively.

    If the problem comes from a relocation of a module (e.g. the mesh module was moved from plugins to the pyFormex core), you may get an error like this:

    AttributeError: 'NoneType' object has no attribute 'Mesh'

    The reason is that the path recorded in the Project file pointed to the old location of the mesh module under plugins while the mesh module is now in the top pyformex directory. This can be fixed in two ways:

    • The easy (but discouraged) way is to add a symbolic link in the old position, linking to the new one. We do not encourage to use this method, because it sustains the dependency on legacy versions.

    • The recommended way is to convert your Project file to point to the new path. To take care of the above relocation of the mesh module, you could e.g. use the following command to convert your old.pyf to a new.pyf that can be properly read. It just replaces the old module path (plugins.mesh) with the current path (mesh):

      sed 's|plugins.mesh|mesh|'g old.pyf >new.pyf
pyformex-0.8.6/pyformex/doc/html/projects.html0000644000211500021150000001743511705104246021345 0ustar benebene00000000000000 Using Projects — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Table Of Contents

Previous topic

Creating Images

Next topic

Assigning properties to geometry

[FSF Associate Member]

Valid XHTML 1.0 Transitional

Using Projects

Warning

This document still needs to be written!

Abstract

This chapter explains how to use projects to make your work persistent. We will explain how to create new projects, how to add or remove data from the project and how to save and reopen project files.

What is a project

A pyFormex project is a persistent copy of some data created by pyFormex. These data are saved in a project file, which you can later re-open to import the data in another pyFormex session.

pyformex-0.8.6/pyformex/doc/html/examples.html0000644000211500021150000015734511705104246021337 0ustar benebene00000000000000 pyFormex example scripts — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Table Of Contents

Previous topic

Configuring pyFormex

Next topic

pyFormex reference manual

[FSF Associate Member]

Valid XHTML 1.0 Transitional

pyFormex example scripts

Warning

This document still needs some cleanup!

Sometimes you learn quicker from studying an example than from reading a tutorial or user guide. To help you we have created this collection of annotated examples. Beware that the script texts presented in this document may differ slightly from the corresponding example coming with the pyFormex distribution.

WireStent

To get acquainted with the modus operandi of pyFormex, the WireStent.py script is studied step by step. The lines are numbered for easy referencing, but are not part of the script itself.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# $Id: WireStent.py 2146 2012-01-09 08:57:52Z bverheg $   *** pyformex ***
##
##  This file is part of pyFormex 0.8.6  (Mon Jan 16 21:15:46 CET 2012)
##  pyFormex is a tool for generating, manipulating and transforming 3D
##  geometrical models by sequences of mathematical operations.
##  Home page: http://pyformex.org
##  Project page:  http://savannah.nongnu.org/projects/pyformex/
##  Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) 
##  Distributed under the GNU General Public License version 3 or later.
##
##
##  This program is free software: you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation, either version 3 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program.  If not, see http://www.gnu.org/licenses/.
##
"""Wirestent.py

A pyFormex script to generate a geometrical model of a wire stent.

This version is for inclusion in the pyFormex documentation.
"""

from formex import *

class DoubleHelixStent:
    """Constructs a double helix wire stent.

    A stent is a tubular shape such as used for opening obstructed
    blood vessels. This stent is made frome sets of wires spiraling
    in two directions.
    The geometry is defined by the following parameters:
      L  : approximate length of the stent
      De : external diameter of the stent 
      D  : average stent diameter
      d  : wire diameter
      be : pitch angle (degrees)
      p  : pitch  
      nx : number of wires in one spiral set
      ny : number of modules in axial direction
      ds : extra distance between the wires (default is 0.0 for
           touching wires)
      dz : maximal distance of wire center to average cilinder
      nb : number of elements in a strut (a part of a wire between two
           crossings), default 4
    The stent is created around the z-axis. 
    By default, there will be connectors between the wires at each
    crossing. They can be switched off in the constructor.
    The returned formex has one set of wires with property 1, the
    other with property 3. The connectors have property 2. The wire
    set with property 1 is winding positively around the z-axis.
    """
    def __init__(self,De,L,d,nx,be,ds=0.0,nb=4,connectors=True):
        """Create the Wire Stent."""
        D = De - 2*d - ds
        r = 0.5*D
        dz = 0.5*(ds+d)
        p = math.pi*D*tand(be)
        nx = int(nx)
        ny = int(round(nx*L/p))  # The actual length may differ a bit from L
        # a single bumped strut, oriented along the x-axis
        bump_z=lambda x: 1.-(x/nb)**2
        base = Formex(pattern('1')).replic(nb,1.0).bump1(2,[0.,0.,dz],bump_z,0)
        # scale back to size 1.
        base = base.scale([1./nb,1./nb,1.])
        # NE and SE directed struts
        NE = base.shear(1,0,1.)
        SE = base.reflect(2).shear(1,0,-1.)
        NE.setProp(1)
        SE.setProp(3)
        # a unit cell of crossing struts
        cell1 = (NE+SE).rosette(2,180)
        # add a connector between first points of NE and SE
        if connectors:
            cell1 += Formex([[NE[0][0],SE[0][0]]],2)
        # and create its mirror
        cell2 = cell1.reflect(2)
        # and move both to appropriate place
        self.cell1 = cell1.translate([1.,1.,0.])
        self.cell2 = cell2.translate([-1.,-1.,0.])
        # the base pattern cell1+cell2 now has size [-2,-2]..[2,2]
        # Create the full pattern by replication
        dx = 4.
        dy = 4.
        F = (self.cell1+self.cell2).replic2(nx,ny,dx,dy)
        # fold it into a cylinder
        self.F = F.translate([0.,0.,r]).cylindrical(
            dir=[2,0,1],scale=[1.,360./(nx*dx),p/nx/dy])
        self.ny = ny

    def all(self):
        """Return the Formex with all bar elements."""
        return self.F


if __name__ == "draw":

    # show an example

    wireframe()
    reset()

    D = 10.
    L = 80.
    d = 0.2
    n = 12
    b = 30.
    res = askItems([['Diameter',D],
                    ['Length',L],
                    ['WireDiam',d],
                    ['NWires',n],
                    ['Pitch',b]])

    if not res:
        exit()
        
    D = float(res['Diameter'])
    L = float(res['Length'])
    d = float(res['WireDiam'])
    n = int(res['NWires'])
    if (n % 2) != 0:
        warning('Number of wires must be even!')
        exit()
    b = float(res['Pitch'])

    H = DoubleHelixStent(D,L,d,n,b).all()
    clear()
    draw(H,view='iso')
    
    # and save it in a lot of graphics formats
    if ack("Do you want to save this image (in lots of formats) ?"):
        for ext in [ 'bmp', 'jpg', 'pbm', 'png', 'ppm', 'xbm', 'xpm',
                     'eps', 'ps', 'pdf', 'tex' ]: 
            image.save('WireStent.'+ext)

# End

As all pyFormex scripts, it starts with a comments line holding the word pyformex (line 1). This is followed more comments lines specifying the copyright and license notices. If you intend to distribute your scripts, you should give these certainly special consideration.

Next is a documentation string explaining the purpose of the script (lines 25-30). The script then starts by importing all definitions from other modules required to run the WireStent.py script (line 32).

Subsequently, the class DoubleHelixStent is defined which allows the simple use of the geometrical model in other scripts for e.g. parametric, optimization and finite element analyses of braided wire stents. Consequently, the latter scripts do not have to contain the wire stent geometry building and can be condensed and conveniently arranged. The definition of the class starts with a documentation string, explaining its aim and functioning (lines 34-60).

The constructor __init__ of the DoubleHelixStent class requires 8 arguments (line 61):

  • stent external diameter De (mm).
  • stent length L (mm).
  • wire diameter d (mm).
  • Number of wires in one spiral set, i.e. wires with the same orientation, nx (-).
  • Pitch angle \beta (\deg).
  • Extra radial distance between the crossing wires ds (mm). By default, ds is [0.0]mm for crossing wires, corresponding with a centre line distance between two crossing wires of exactly d.
  • Number of elements in a strut, i.e. part of a wire between two crossings, nb (-). As every base element is a straight line, multiple elements are required to approximate the curvature of the stent wires. The default value of 4 elements in a strut is a good assumption.
  • If connectors=True, extra elements are created at the positions where there is physical contact between the crossing wires. These elements are required to enable contact between these wires in finite element analyses.

The virtual construction of the wire stent structure is defined by the following sequence of four operations: (i) Creation of a nearly planar base module of two crossing wires; (ii) Extending the base module with a mirrored and translated copy; (iii) Replicating the extended base module in both directions of the base plane; and (iv) Rolling the nearly planar grid into the cylindrical stent structure, which is easily parametric adaptable.

Creating the base module

(lines 63-71)

Depending on the specified arguments in the constructor, the mean stent diameter D, the average stent radius r, the bump or curvature of the wires dz, the pitch p and the number of base modules in the axial direction ny are calculated with the following script. As the wire stent structure is obtained by braiding, the wires have an undulating course and the bump dz corresponds to the amplitude of the wave. If no extra distance ds is specified, there will be exactly one wire diameter between the centre lines of the crossing wires. The number of modules in the axial direction ny is an integer, therefore, the actual length of the stent model might differ slightly from the specified, desired length L. However, this difference has a negligible impact on the numerical results.

Of now, all parameters to describe the stent geometry are specified and available to start the construction of the wire stent. Initially a simple Formex is created using the pattern()-function: a straigth line segment of length 1 oriented along the X-axis (East or 1-direction). The replic()-functionality replicates this line segment nb times with step 1 in the X-direction (0-direction). Subsequently, these nb line segments form a new Formex which is given a one-dimensional bump with the bump1()-function. The Formex undergoes a deformation in the Z-direction (2-direction), forced by the point [0,0,dz]. The bump intensity is specified by the quadratic bump_z function and varies along the X-axis (0-axis). The creation of this single bumped strut, oriented along the X-axis is summarized in the next script and depicted in figures A straight line segment, The line segment with replications and A bumped line segment,.

straight line segment

A straight line segment

line segment with replications

The line segment with replications

bumped line segment

A bumped line segment

The single bumped strut (base) is rescaled homothetically in the XY-plane to size one with the scale()-function. Subsequently, the shear()-functionality generates a new NE Formex by skewing the base Formex in the Y-direction (1-direction) with a skew factor of 1 in the YX-plane. As a result, the Y-coordinates of the base Formex are altered according to the following rule: y_2 = y_1 + skew \* x_1. Similarly a SE Formex is generated by a shear() operation on a mirrored copy of the base Formex. The base copy, mirrored in the direction of the XY-plane (perpendicular to the 2-axis), is obtained by the reflect() command. Both Formices are given a different property number by the setProp()-function, visualised by the different color codes in Figure Unit cell of crossing wires and connectors This number can be used as an entry in a database, which holds some sort of property. The Formex and the database are two seperate entities, only linked by the property numbers. The rosette()-function creates a unit cell of crossing struts by 2 rotational replications with an angular step of [180]:math:deg around the Z-axis (the original Formex is the first of the 2 replicas). If specified in the constructor, an additional Formex with property 2 connects the first points of the NE and SE Formices.

(lines 72-83)

rescaled bumped line segment

Rescaled bumped strut

mirrored and skewed bumped strut

Mirrored and skewed bumped strut

unit cell of wires and connectors

Unit cell of crossing wires and connectors

Extending the base module

Subsequently, a mirrored copy of the base cell is generated (Figure Mirrored unit cell). Both Formices are translated to their appropriate side by side position with the translate()-option and form the complete extended base module with 4 by 4 dimensions as depicted in Figure Completed base module. Furthermore, both Formices are defined as an attribute of the DoubleHelixStent class by the self-statement, allowing their use after every DoubleHelixStent initialisation. Such further use is impossible with local variables, such as for example the NE and SE Formices.

(lines 84-89)

mirrored unit cell

Mirrored unit cell

completed base module

Completed base module

Full nearly planar pattern

The fully nearly planar pattern is obtained by copying the base module in two directions and shown in Figure Full planar topology. replic2() generates this pattern with nx and ny replications with steps dx and dy in respectively, the default X- and Y-direction.

(lines 90-93)

Full planar topology

Full planar topology

orthogonal view of the full planar topology

Orthogonal view of the full planar topology

Cylindrical stent structure

Finally the full pattern is translated over the stent radius r in Z-direction and transformed to the cylindrical stent structure by a coordinate transformation with the Z-coordinates as distance r, the X-coordinates as angle \theta and the Y-coordinates as height z. The scale()-operator rescales the stent structure to the correct circumference and length. The resulting stent geometry is depicted in Figure Cylindrical stent. (lines 94-96)

In addition to the stent initialization, the DoubleHelixStent class script contains a function all() representing the complete stent Formex. Consequently, the DoubleHelixStent class has four attributes: the Formices cell1, cell2 and all; and the number ny. (lines 97-100)

cylindrical stent

Cylindrical stent

orthogonal view of thecylindrical stent

Orthogonal view of the cylindrical stent

Parametric stent geometry

An inherent feature of script-based modeling is the possibility of easily generating lots of variations on the original geometry. This is a huge advantage for parametric analyses and illustrated in figures Stent variant with : these wire stents are all created with the same script, but with other values of the parameters De, nx and \beta. As the script for building the wire stent geometry is defined as a the DoubleHelixStent class in the (WireStent.py) script, it can easily be imported for e.g. this purpose.

Stent variant

Stent variant with De=16, nx=6, \beta=25

Stent variant

Stent variant with De=16, nx=6, \beta=50

Stent variant

Stent variant with De=16, nx=10, \beta=25

Stent variant

Stent variant with De=16, nx=10, \beta=50

Stent variant

Stent variant with De=32, nx=6, \beta=25

Stent variant

Stent variant with De=32, nx=6, \beta=50

Stent variant

Stent variant with De=32, nx=10, \beta=25

Stent variant

Stent variant with De=32, nx=10, \beta=50

# $Id: WireStentParametricExample.py 2146 2012-01-09 08:57:52Z bverheg $   *** pyformex ***
##
##  This file is part of pyFormex 0.8.6  (Mon Jan 16 21:15:46 CET 2012)
##  pyFormex is a tool for generating, manipulating and transforming 3D
##  geometrical models by sequences of mathematical operations.
##  Home page: http://pyformex.org
##  Project page:  http://savannah.nongnu.org/projects/pyformex/
##  Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) 
##  Distributed under the GNU General Public License version 3 or later.
##
##
##  This program is free software: you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation, either version 3 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program.  If not, see http://www.gnu.org/licenses/.
##


from examples.WireStent import DoubleHelixStent

for De in [16.,32.]:
    for nx in [6,10]:
        for beta in [25,50]:
            stent = DoubleHelixStent(De,40.,0.22,nx,beta).all()
            draw(stent,view='iso')
            pause()
            clear()
            

Obviously, generating such parametric wire stent geometries with classical CAD methodologies is feasible, though probably (very) time consuming. However, as provides a multitude of features (such as parametric modeling, finite element pre- and postprocessing, optimization strategies, etcetera) in one single consistent environment, it appears to be the obvious way to go when studying the mechanical behavior of braided wire stents.

Operating on surface meshes

Besides being used for creating geometries, also offers interesting possibilities for executing specialized operations on surface meshes, usually STL type triangulated meshes originating from medical scan (CT) images. Some of the algorithms developed were included in .

Unroll stent

A stent is a medical device used to reopen narrowed arteries. The vast majority of stents are balloon-expandable, which means that the metal structure is deployed by inflating a balloon, located inside the stent. Figure Triangulated mesh of a Cypher® stent shows an example of such a stent prior to expansion (balloon not shown). The 3D surface is obtained by micro CT and consists of triangles.

Cypher stent

Triangulated mesh of a Cypher® stent

The structure of such a device can be quite complex and difficult to analyse. The same functions offers for creating geometries can also be employed to investigate triangulated meshes. A simple unroll operation of the stent gives a much better overview of the complete geometrical structure and allows easier analysis (see figure Result of the unroll operation).

F = F.toCylindrical().scale([1.,2*radius*pi/360,1.])

Cypher stent unrolled

Result of the unroll operation

The unrolled geometry can then be used for further investigations. An important property of such a stent is the circumference of a single stent cell. The clip() method can be used to isolate a single stent cell. In order to obtain a line describing the stent cell, the function intersectionLinesWithPlane() has been used. The result can be seen in figures Part of the intersection with a plane.

Intersection with plane

Part of the intersection with a plane

Finally, one connected circumference of a stent cell is selected (figure Circumference of a stent cell) and the length() function returns its length, which is 9.19 mm.

circumference of stent cell

Circumference of a stent cell

pyformex-0.8.6/pyformex/doc/html/file_format.html0000644000211500021150000003140511705104246021774 0ustar benebene00000000000000 pyFormex file formats — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Table Of Contents

Previous topic

pyFormex FAQ ‘n TRICKS

Next topic

BuMPix Live GNU/Linux system

[FSF Associate Member]

Valid XHTML 1.0 Transitional

pyFormex file formats

Date:January 16, 2012
Version:0.8.6
Author:Benedict Verhegghe <benedict.verhegghe@ugent.be>

Abstract

This document describes the native file formats used by pyFormex. The are currently two file formats: the pyFormex Project File (.pyf) and the pyFormex Geometry File (.pgf/.formex).

Introduction

pyFormex uses two native file formats to save data on a persistent medium: the pyFormex Project File (.pyf) and the pyFormex Geometry File (.pgf).

A Project File can store any pyFormex data and is the prefered way to store your data for later reuse within pyFormex. The data in the resulting file can normally not be used by humans and can only be easily restored by pyFormex itself.

The pyFormex Geometry File on the other hand can be used to exchange data between pyFormex projects or with other software. Because of its plain text format, the data can be read and evend edited by humans. You may also wish to save data in this format to make them accessible the need for pyFormex, or to bridge incompatible changes in pyFormex.

Because the geometrical data in pyFormex can be quite voluminous, the format has been chosen so as to allow efficient read and write operations from inside pyFormex. If you want a nicer layout and efficiency is not your concern, you can used the fprint() method of the geometry object.

pyFormex Project File Format

A pyFormex project file is just a pickled Python dictionary stored on file, possibly with compression. Any pyFormex objects can be exported and stored on the project file. The resulting file is normally not readable for humans and because all the class definitions of the exported data have to be present, the file can only be read back by pyFormex itself.

The format of the project file is therefore currently not further documented. See Using Projects for the use of project files from within pyFormex.

pyFormex Geometry File Format 1.2

This describes the pyFormex Geometry File Format version 1.2 as drawn on 2010-01-04. The version numbering is such that implementations of a later version are able to read an older version with the same major numbering. Thus, the 1.2 version still can read version 1.0 and 1.1 files.

The prefered filename extension for pyFormex geometry files is ‘.pgf’, though this is not a requirement and the previously used ‘.formex’ is certainly as valid as any other.

The pyFormex Geometry File starts with a header line identify the file type and version, and possibly specifying some global variables:

# Formex File Format 1.1 (http://pyformex.org)

The remainder of the file contains one or more data blocks, each of which consists of a header line followed by the numerical data. The header line starts with a ‘#’. The remainder of the line is a sequence of ‘keyword=value’ strings separated with a semicolon and optional whitespace, such as in the following example:

# nelems=692; nplex=2; props=True; eltype=None; sep=' '

The keywords in the data header specify the type and amount of that that will be read, and how they will be structured in arrays and converted to pyFormex objects. The example above specifies a Formex of plexitude 2 having 692 elements with no specific element type but possessin property numbers. The separator used in the data is a single space.

  • comment lines, stating with a ‘#’, but maybe holding invaluable information for the interpretation of the rest of the data,
  • data blocks.

Data blocks are written using numpy.tofile() and read back with numpy.fromfile(). All data items in a block are of the same type and are written as ASCII strings, separated by a constant string. The separator can be specified by the user and defaults to a single space, so that all data of a single block are written on one line, separated by a blank. When reading, newline characters will be silently ignored or used as a separator character as well. As a special case, if an empty string is specified as separator, the data will be written in binary mode.

Each data block is preceded with a comments line with the following structure:

pyformex-0.8.6/pyformex/doc/html/plugins.html0000644000211500021150000002263711705104246021175 0ustar benebene00000000000000 pyFormex plugins — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Table Of Contents

Previous topic

Using Widgets

Next topic

Configuring pyFormex

[FSF Associate Member]

Valid XHTML 1.0 Transitional

pyFormex plugins

Abstract

This chapter describes how to create plugins for and documents some of the standard plugins that come with the pyFormex distribution.

What are plugins?

From its inception was intended to be easily expandable. Its open architecture allows educated users to change the behavior of and to extend its functionality in any way they want. There are no fixed rules to obey and there is no registrar to accept and/or validate the provided plugins. In , any set of functions that are not an essential part of can be called a ‘plugin’, if its functionality can usefully be called from elsewhere and if the source can be placed inside the distribution.

Thus, we distinct plugins from the vital parts of which comprehense the basic data types (Formex), the scripting facilities, the (OpenGL) drawing functionality and the graphical user interface. We also distinct plugins from normal (example and user) scripts because the latter will usually be intended to execute some specific task, while the former will often only provide some functionality without themselves performing some actions.

To clarify this distinction, plugins are located in a separate subdirectory plugins of the tree. This directory should not be used for anything else.

The extensions provided by the plugins usually fall within one of the following categories:

Functional
Extending the functionality by providing new data types and functions.
External
Providing access to external programs, either by dedicated interfaces or through the command shell and file system.
GUI
Extending the graphical user interface of .

The next section of this chapter gives some recommendations on how to structure the plugins so that they work well with . The remainder of the chapter discusses some of the most important plugins included with .

How to create a plugin.

pyformex-0.8.6/pyformex/doc/html/index.html0000644000211500021150000002402111705104246020610 0ustar benebene00000000000000 pyFormex Documentation — pyFormex v0.8.6 documentation
scallop dome pyformex logo

Table Of Contents

Next topic

Introduction to pyFormex

[FSF Associate Member]

Valid XHTML 1.0 Transitional

pyFormex Documentation

This is the pyFormex 0.8.6 documentation, last updated on January 16, 2012.

These documents are to become a full documentation set for the installation and use of the pyFormex software. However, as pyFormex is still under active development, so is this documentation: it therefore is by no means final and complete; it does not even pretend to be accurate for any version of pyFormex.

However, since partial documentation is better than none, we decided to make this preliminary version available to the general public, to ease the use of pyFormex. The pyFormex reference manual is automatically generated from the docstrings in the Python source, so that might best reflect the latest development version of the software. The Introduction to pyFormex, Installing pyFormex, pyFormex tutorial are also fairly complete and useable. The pyFormex user guide and pyFormex example scripts are still in an embryonal state but may already contain valuable information for some users.

The new documentation is available in HTML format for interactive browsing. A PDF version of the full documentation set can be downloaded from our FTP server. From the pyFormex GUI, a local version of the HTML documentation may be accessible from the Help menu.

Indices and tables

pyformex-0.8.6/pyformex/doc/html/_images/0000755000211500021150000000000011705105304020214 5ustar benebene00000000000000pyformex-0.8.6/pyformex/doc/html/_images/math/0000755000211500021150000000000011705105304021145 5ustar benebene00000000000000pyformex-0.8.6/pyformex/doc/html/_images/math/41c544263a265ff15498ee45f7392c5f86c6d151.png0000644000211500021150000000026211660263130027037 0ustar benebene00000000000000PNG  IHDR Br-PLTEtttbbb@@@"""̊ 000PPPU~@IDATc`PvM```*d`pW! DN03-m P 0t[IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/111bff3a77cd73fc555142c0ffd7b4af849562f6.png0000644000211500021150000000133011660263134027405 0ustar benebene00000000000000PNG  IHDR:a0PLTE"""000bbbttt̶PPP@@@ OmcIDAT8MhPgi&xPP" 9!h]^rVЂKKE݋Aq!+U(B*,A zxݷlB{2ὙL&@֒`g;P? 4A'Oƀ~̗/aLMqQ@6,g+X5/OWm엘9n@%=CIV:}WRq.K)I:gBm*1UV[7~jdq6Mrx"~&_=ۑ' mHS>f:hS1=J@` nz#pU(`7Tl/M ?IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/55aaa8741e5126c197bd3a986f46165e15d7bf19.png0000644000211500021150000000130211660263131027166 0ustar benebene00000000000000PNG  IHDRt0PLTEPPP @@@"""000tttbbb?TMIDAT8˵Mh@vd6Xъ* RBB/SzK fI\'/ғP/PK|/J)ۅ! o?xTIzfI|e}陁+ϳb\r /Hxo*d"o.rKjb܇$ʍ 8` wKrO>C y1}AwՃY90m49_<(iFVÄۛ+R.-kj; U=;L3|"r Dž14(25u\*ҘvLV H6:9x$0$2^2cZ@\q:^Xh>.ˌ֣ sֿ(f0 ZGhČDtAI,A_I\i0hdR+^e=|Mvg(NԶ,):^ ' }_x^_[EwxInX4]Ϡ̛Y.HӰ^s1>lJk!+Tx2qRO988ʟj!:\lØ?Dw!vAKR IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/80c2d7b999148a9a1b9740bd873c5365df7f461a.png0000644000211500021150000000036011660263125027204 0ustar benebene00000000000000PNG  IHDR `F0PLTEPPP @@@"""000tttbbb?T{IDATc``PRrM`KqCbb4) 35- ?@ +20mʰI9 3z 8%.gV``X6͌|E*u@3>gBIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/859ccf4cd60c7bc6b8fa1afc9a42dc811a826d6f.png0000644000211500021150000000027311660263125027710 0ustar benebene00000000000000PNG  IHDR [A0PLTEPPP 000"""ttt@@@把bbb6>FIDATc``PQ`X')-0 &yd '; @t@' Sr"IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/3eac10714f0043efad0ae267d2b126f6e8af9313.png0000644000211500021150000000073111660263142027354 0ustar benebene00000000000000PNG  IHDR\w0PLTE"""000bbb̶@@@ttt PPPZdIDAT(͒?H@M -"KG%.ةK qs .]2tRqRW ]6WߐZIȫ? 9еCF0V;Y)Y_6E'^m>Ju&!L00&4(]]؊ø=O4+l3ذz.Ͻͳfնo ]Z%GHW--u^Ft <9SM5Prn/%La`RT˶vW뫃|]eHU>ַ<{M]j 5;@ɳXIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/1c2bf95e3a347dc8b88814e23e3609308823d8da.png0000644000211500021150000000043311660263141027171 0ustar benebene00000000000000PNG  IHDR kx0PLTEbbb̶"""000@@@ttt PPPVw.IDATc`ep 2 !(0ɹRLr@, @ICMéU/[S,?mC2"Pnh4́Pp!! 311x2la:V-`8%Ю6+66ʹ0Ľ2qىbC{w1Y.|0aC?Ωܵ*fw` a\c NQ!Uq}l#3mlfz( ۢ$3(9.AQy0r<^z~aކzof]6q^570~@"}nQsc$1r&%JV0WX9 K6GQb\%m 8!LLOrb*W1f*[˃f3ֈE23V@8{zlDzdnb^:{AIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/96ab646de7704969b91c76a214126b45f2b07b25.png0000644000211500021150000000027711660263126027036 0ustar benebene00000000000000PNG  IHDR b60PLTEbbb̶"""000@@@ttt PPPVw.JIDATc`e"@$mu Ȋb``!Ѐܑ) nz`}ȞIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/52e8ed7a3ba22130ad3984eb2cd413406475a689.png0000644000211500021150000000030111660263130027140 0ustar benebene00000000000000PNG  IHDR Br0PLTEtttbbb000 """PPP@@@LIDATc``P6a```H @r C"G6O  {210x3O``x:(0ո IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/1976493054d4291e9f39a772ddeac0b5e7779389.png0000644000211500021150000000103211660263130027054 0ustar benebene00000000000000PNG  IHDR4Y0PLTE"""000tttPPP bbb@@@^QIDAT8˥K@T[_EA dɠ.,:8 b.uP#8tp(trD\A: W-bx璇{/ojSTcc;wAWKا#l"IŽk(EOyYoݮsq7%aCKg !'Y_- SS# C,.15r|Ac}tbp -ɲd_1 :LBv@ J bP ♎z&(kFkԤʗ40ͬK/jj[3TMbk9l=``ȱfp&:ǁf=`C)184>DSuX1CeZ\f;Vj<.3?Q(/7`:`mfWIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/46587de496607a13f873f771a0964285cbe8f638.png0000644000211500021150000000034611660263127027012 0ustar benebene00000000000000PNG  IHDR "0PLTEbbbttt000 """@@@PPPwqIDATc`2v +`A1iK{\-  20& 0y \ RZ X'0e2 x0^0sY`rL4*%cIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/26eeb5258ca5099acf8fe96b2a1049c48c89a5e6.png0000644000211500021150000000023411660263133027424 0ustar benebene00000000000000PNG  IHDR 2s'PLTEtttPPP000@@@N90IDATc`PvUNg`-Pd`b`70Dp30D0 p0TQe0)4IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/dd5e3857ae21daa6c9c4521d45e2e09454c847f0.png0000644000211500021150000000121311660263131027316 0ustar benebene00000000000000PNG  IHDRz'0PLTEPPP @@@"""000tttbbb?TIDAT8˵AhAl6iDJ9Tۂ*A'OE!`a tg1"(V%|3&ݲx7ϛg͑q̅v(5Il 0WDW`\M1`8HW)tM4}[)xg">kH }d;B!ԛ.ŘwZ6KvB KߙM~U{qGQ}~,bZ-,/#.t0,}D fDV~ZU-aE|&?Ő*XJ}+Hݪ;1X"z|W'gRt\ hzb()WQџ `ʆh?lQlY:=]FzLq!p++tUȣhL۱_"csf Y !6&DGKM&{,O 3KGkb4y ׷HF(g/IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/092e364e1d9d19ad5fffb0b46ef4cc7f2da02c1c.png0000644000211500021150000000027311660263133027673 0ustar benebene00000000000000PNG  IHDR Br0PLTE"""000tttPPP bbb@@@^QFIDATc`2a``HP``# I1O-$++$PHa>| h 5?nIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/6ab77f0909f9e9426fbe827c5e805a87aa1e6fa4.png0000644000211500021150000000125211660263131027426 0ustar benebene00000000000000PNG  IHDRz'0PLTEPPP @@@"""000tttbbb?T5IDAT8˵TAhA}IdmD$Z[B=UzjIdhk/b,zXEh%гEb^E4ЋE<v6 3yy0^I5kDBp!+r0fY(c0D;J-3\ȶ ^En*6X Qş qkܻw?paFK$TIom8.m7 ,Hbm9WqX 7a (@ |m  _n>^  1SV34@pgap10rQ @0IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/b13f21416d84e13708696f34dea81026cda583c9.png0000644000211500021150000000023311660263131027076 0ustar benebene00000000000000PNG  IHDR Ȇ'PLTE000ttt̊bbbPPP@@@ ci/IDATc``2aM``p\l DpPvV͈ ,lg  67IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/2aeee926bd2d6bbe822ad1871a424e07bad899a5.png0000644000211500021150000000127711660263131027542 0ustar benebene00000000000000PNG  IHDRz'0PLTEPPP @@@"""000tttbbb?TJIDAT8˵TAhP$McҊh ;l*N'eCѓwGzp0Eh c&xibpgG(2^dI/UZI⃆/{?Ksꍨ]dITYY ;rE blƆz'5% Z[ȄqOr\ӓ[2iA;@a]ATG ~-]tK!r_u@CezD_;SU/QZXy1,>^99l !#+9ndXॐikY9CA'fhM3=sh-(#|2,d5K0}G_.aH'3C!^Lv%y?M"sKXJ+ϹbPLhJLS?K*?s ހDzLdYua Y(vE|k\{qO~qvk`ܐC>raۯaɣtNefvPitf<(s2\vo kf,AIJF7_"#p0 e#ް#۲F8NpIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/174fadd07fd54c9afe288e96558c92e0c1da733a.png0000644000211500021150000000026411660263147027505 0ustar benebene00000000000000PNG  IHDR 0PLTEbbbttt000 """@@@PPPw?IDATc`2v +``po `P,z!:,3o %IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/8122aa89ea6e80784c6513d22787ad86e36ad0cc.png0000644000211500021150000000026211660263150027246 0ustar benebene00000000000000PNG  IHDR 6fHp-PLTE@@@000ttt PPP"""]@@IDATc`E03 N °$&"H"@XW4_yS mIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/804a8d1c4e19e5a78fd601c01cc26e9f07e44f66.png0000644000211500021150000000033711660263126027333 0ustar benebene00000000000000PNG  IHDR W)0PLTEbbb̶"""000@@@ttt PPPVw.jIDATc`e(pM`,0f:pjSC> ,! ,@x b؀~X U09&IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/03093f6aafd62c55673071ae075bcc438f87e571.png0000644000211500021150000000132411660263132027162 0ustar benebene00000000000000PNG  IHDRt0PLTEPPP @@@"""000tttbbb?T_IDAT8˵TMhP6flzZłOOA}"b/K%[P-ͥMxWО]EQD%U"=8//)ۅGf{73Yw;NÆl$7xYo>Ԋ_>s;kB9 lE;ecpy26pJw Ǒ;ҊG!mt3E&m"! p#<:AG;j7 ݩ\lF" `sajKp'xh%k .B 5a !H5(Ӛ}l6KyoCa'b1d(2'dgB܍ȑ%zxA02'?ha~WrQw/M#:7N仈) M6«iXWNiS4gȺ( fض(, ]e)Ԙ6C5N|¡jio M%IcNjb4qj8FsPIIPri؅`;6 p߳M ɝ]d Z lOBIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/435f3a43ae59ed41768ef39b59d4515a9f150105.png0000644000211500021150000000032711660263126027115 0ustar benebene00000000000000PNG  IHDRq0PLTEbbbttt000 """@@@PPPwbIDATc`2v +``- k`po `PC @p̀7~dr0h3086 g2* `ӥU0{IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/5510380da19a236624ef45908d2da469f892b03e.png0000644000211500021150000000036611660263147027034 0ustar benebene00000000000000PNG  IHDR8 Cc0PLTE000ttt̊bbbPPP@@@ """SIDATc`@S"Qh A ,  H0)&$961y8޹Y#(6  0$QdZ&8P$nBؑeX5#vB>b`a;S|ӗ2pX)(QAIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/09ddb817441a1323731ea0677596957f28faea46.png0000644000211500021150000000040011660263126027026 0ustar benebene00000000000000PNG  IHDR=0PLTE@@@000PPPttt"""bbb kHIDATc```2a@D@',Μ91 >7\``P8p6@3bɍ l<`yn`w` {"/1w,`d n0 DO"EIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/e53b1fe25be1c679117fb44a6a886fe1247d189a.png0000644000211500021150000000051511660263147027337 0ustar benebene00000000000000PNG  IHDR(10PLTEttt@@@bbb""" PPP000{yIDATc``2`@ EUH1f .@dg`0@d5`\.7dʵ_`n' A_```X4!1a!w! }q2Ab p LR `A!`Ap} rl𤯷O(hk+t|y`L{B;dPAT@ ^&t@IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/34857b3ba74ce5cd8607f3ebd23e9015908ada71.png0000644000211500021150000000023711660263150027323 0ustar benebene00000000000000PNG  IHDR x%*PLTE̞"""tttPPP@@@ bbb(upF0IDATc`&`Sg``bK \ .0ppeKFeIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/b55ca7a0aa88ab7d58f4fc035317fdac39b17861.png0000644000211500021150000000022711660263127027461 0ustar benebene00000000000000PNG  IHDR6!*PLTEbbbttt枞000PPP @@@ (IDATc`2 1aHtd(b 8 @Ӂ(KIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/a6b32b71d40ef0cc4f92e0493a2f3db800b8d2a1.png0000644000211500021150000000132311660263132027424 0ustar benebene00000000000000PNG  IHDRt0PLTEPPP @@@"""000tttbbb?T^IDAT8˵T1hPrI5.mϊ(88)Ntpq)U*\ۡ.J-ufKbuQHII ${I>}??9IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/bc1f9d9bf8a1b606a4188b5ce9a2af1809e27a89.png0000644000211500021150000000026311660263130027471 0ustar benebene00000000000000PNG  IHDR Br*PLTEbbbttt"""000PPP DIDATc`Pv```M````S`d``l`b`ظCx1A ҁAY7&>IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/5f131a63bd792d941d6f8fd7da6ffb14f86989bf.png0000644000211500021150000000024511660263150027520 0ustar benebene00000000000000PNG  IHDRF*PLTE"""bbb000ttt̊PPP@@@ /6IDATc`N}wLg`P6a .ꡣc̽( `IENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/fdb63b9e51abe6bbb16acfb5d7b773ddbb5bf4a8.png0000644000211500021150000000030611660263126030173 0ustar benebene00000000000000PNG  IHDR 2M*PLTEtttbbb""" @@@PPPw2¸WIDATc```26aU 8 Vm@& pH]``Xz{Ќ jxMb ` C0 F3mIENDB`pyformex-0.8.6/pyformex/doc/html/_images/math/2c3354b1a3c611400aef7c077658d58ede9551fd.png0000644000211500021150000000074111660263141027236 0ustar benebene00000000000000PNG  IHDR\w0PLTE"""000bbb̶@@@ttt PPPZlIDAT(͒=LPO菥Ą Deb)&ucl\Xq 81ct5aq3kC!WN+,UUGƛ *=A8\kM评Kq 'p}D390IveWJ~9Mإxfe5$\ UwFdSOV];؆DZu` G.R]f73.F p_rG[sSa\^8SQܴ[$UGK2@a_V8C֯A|$g+y23>i|MQ쒬ukH\܅G]U/R5ՈIENDB`pyformex-0.8.6/pyformex/doc/html/_images/pyformex_ufd.jpg0000644000211500021150000012217711204760743023447 0ustar benebene00000000000000JFIFExifII* z(2izCanonCanon PowerShot A5202009:04:20 17:02:330220>Rn v~  Y |~f0100D,4<2009:04:20 17:02:332009:04:20 17:02:33 <-   ."0<D| /  V $\@  #D>r( 3fff666IMG:PowerShot A520 JPEGFirmware Version 1.00OH %%%@ eo[UII*"@R980100(  `}ޢAii"WW1ɌdouqlYPtri\n7eZYVZ^Is|3YUtK!Rx\}y{1GP@-2;017}u5ƦWV62aSnfm3NɅdKw!gqՒt&MO+/#gnGZOqr|Y:maGzjM$f')oډXd-X[oj|ynQ7XsRhGZ]dF]贋J@*;zY#K-ξ] *VB杙$!8%NGC] #ùGrj&<'ŌmG=lkG۲5,c}h=#U_9)]Y'U9$, #X-i@Кߋ;,ѧ^Vöy9%G`BFnXic㑑IWC>Vs3؂RzaioyXGl듿T`_Le    !#"! $)4,$'1'-=-167:::"*?D>8B3796    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOx! }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?@HCӂ@HF`.)vm&6J+L+@!)pPbUo2!K@ê H!C,#粼Xubp+ 4 L+L+@!t7(GxSWtMZ=gO[s@bI"ƛuUVH ]lD"W#vڬ|FHϡ82anhԄPIi(_kְH>|n?@Cq&OkV3DZH)ؠa1@XDd"_V=k"طd?@櫨kowxTV\h,22Jea-S?]Nך[2]`eþ3DԶgwq叧!LHh@^4e$LRЄ`%K>|FJ4+FP-`3ďRh,aʪ0SemGQm \`I>^@c"qtsS}!ocQQ4YA&gnsV-DFnaO9%v(3*J=*GRW?evGCx#PԶO&ou3OҶ'&)1LbbE!PM4NN/>$hYS;`y?1OE3t'U&ѻH8C7t_Y. 㝣JHcFi"('F:]? 4' $xiZ)Z)O nCz"iM4)p43?AyrB!\ĻֳJ}\֋u+P=%c ̟d#;odg5ΖiM0NsGk 5'v bWHzeŵ8#):7m9^'kk6V̎吼m&3׿9K1[#QXI'`LijunMK`?cG|Y;hE @ ) )s@j{\\LhHIb9bjdL{ _J.!gE k_cȉn 12?B{Љ"n-$|$nys)\>S#4GM- cxqHNGWX!M*G !Ib}+/Hvx  ?C@&Mxر1W<1ܨpoR Gl*շql3ܳ{*4cNE3Kj[XZǵq{16beO}j`[G>y?@PԜ.jLOfcV¨ 0i@PhP[.R]ۤt'?R2}['+A DEP*L⁆h&i3@ PKS csM&p4x4 (&h 搚i4hi@N44Bi cI@ &M8p4p4Kuu.Lњ&i3@&MM4i4@G\Bm4gY5ʩaϨ:{jnc@&6` 0ʼndiǶ H/Wip;4vdk$/zPN4D@!>U&"B S 1ʡ`f1zC ς[v%˰Vw>q KrMzk2:ɿx-@qsZVѫ1cLgqytwb*+7D\kBwg?u489#+-9/Vi'6[$d,T@hQ :IYYl4P$^I cKKН/o1Su[#iECԛRvWҼ%#| Rrk]-l4CcEqOT7@  +p#<|Eb#R,X/@TV PE**J*DPAE@JEl @ZFF@ pZ0pU0*#NP $"%d<2TPTJ*-H( -(((O F@ Р#h(<(Fx @@Po@( d@#{Р@T|P <P`PFT@@ r(aap br(r!EhPJ @ . A%`5-|e@6PP ZD`*%Q@P{(E%Z eH@PW`@QR0 D@P-QD ؀@D||A`bZ P- +@@P "Ձ;@@/@R(P l@ob#*  -@X  rPP %h Q( G0l@1Z( (@@Z o0 @@ hp`|<YP( `iB Yh D R0*%|( (,@ B4JDP-#D`D 4 @9y@" TR{<TH , 1"*؀FlK[PV PP'@'R @ #lQHpPV@*Bp -@+r@8*!XJA +b*  r)sk0iݗ,`Nս+Ok;|y_\Mc]OmcN6N~%R=,6^,ЗC+}ǒ(< cd|-HP@'"$PVń,PU ( S@QV( (Q+ ( , vTWbʬYkmŔ  [WR.d]}G֚T="Q'QQ6MXHݧ9/G9G5Z}N8z](znbWhH]}p(Q”şy%uc+qgfKQmܝOҽMJ3P_L{JJ^v(V,n x/( P@4(n!cp-U W@PP*  *( @ؼ ,X20ŕp{s8[KO|9/z^w)Y2IOn?Iv_[=C6FKs:aioヿE}zLR&:Yc% ]t> q馣cR>St%''=2 -Lw_;麜M_svxYc(Xs5+6>z6QktxRg`Y0J-x4lhQlG0Q *p e`PrP\PB  P(  ((p*@*@#E@F$ -XK\ SO$Ԛ&fo?2kT}-.:ncvdn:xycŏE#XK*%ݾCISjS/t|W\X־#_ˡHc|HuŒnzy/mV-V$%{ZN (@( `B (9;{f-\ӸX .p) (-J~γ! b_(Ǫ?S̓'-w{ETd%bT#1E  @ -YP@P((PlT  0`B`P!h K@R=ų/g>Q6ydEXG[].Բem9}zWM5*r3uq~c_76YK3 fikcKС)F*D6,MKp{zGc ˎ5>lEJ ) F/0lj" E<C`"Z4(@ < ݁$@P[#=oq9G+}ֽ5 2([lوR(`C%$JVLY1Ϧ YRM]Z0r2lЉ_EE4^BJ U @@PjU  /P(h0*-("(PH# ,M V, [ܽlNH $?5~dq':_#ŧ vV<4"ѐ7- u=&y~~Wj>d+%Ʃ8F@`<7%iyEP um&-Ȝ0VִKcu(+PCP, (@$U-K/ K' 1eV*3AXK?)^§4@GF-݁t{W,{s{IYoqA0#Dd)Di.-t{vi4IE)JWz>k)@rT 9$zGMwx@|RNUq8r|yrܛ~# nr kX߃=+,N_lIGEpZl، @ @R (PVAZ(_ O%l(ך~T2 ZJOi~[FU (0yiFy_i弹 [Ҙ=2OO`b#mhŁI@<#! @xNeJJM%vcIJ]^K_#M\c7YeroR49MULgU\%gw#(1G_n[QRP Vx ( D(PQSE@ <`1rWI4\gf\f %d+Mhe5VIZm\2-$~Yb?^ ܴޢ/^5׋}~ؗ"` |Fp Ke#%@Q(hHCh #bGPE.@MF}G?GS)1)6v>+[q:0϶K~QU}>)YYZ (x bP [L**yE A0@ (/r>=rF+\K#uf}5O/Vdviu<җ5C猭N_ܱL:~NLoH~s^\On q=׼$}g:\{M.?DR]ɪe P5 PbϜz圖Ic3k4Вsv=5:i6bοԥz'E|~(CI},17>f@1h@QH> |4JT @EQD]qH O k谼fսKQq!ڙNMT<[59OQ|+e[e17BUdn^Q(S==VC'S"_ n!TPXRA 6FWԴ(f^>_ŸaKM|Me/g,5=LR lTݚ{͙(ǨɊI=]U]A_.U5ܖ{ɦ.ODhK6iB*RzPj^ 3x/F=N(N:-/[ ouX(MA7m??V:i}Ԥ~OYVn Aߨ)Bjّla#HĈ @( L`@J'^H5`la9TuY\'^Qܛ)I81r+)Kv*ߊ$Y)*oc;3@m7l}G@OIo+lsYb: & &@R4E@hT*0~h1o&X߲>O>[}5L 8$ΒiIr\MkmAtd J^[lTg8lAtΥM+vt_Pcy*QmLGb#SMT2&p_I1.ηkd}Lcr%,#KIe꺗5IJRK'ut z|1K 6Iy>M:m<`&{" S@PaR@KI-N- jYT濖'Vj x yr7)M^V+Y|Rvil%wbP[Du"aMHذM#møý'%>Ri/'Ӻ87__oкLy r.-}$M0s+O˶SE[_|Ϋ$ -fԳAf_CQ/MFeѺgyJ0ƷIBuީ/NJjrKe:uRRǏS>z3~8i/*y3k\z}NG<:gQ/l"Q,P(BpK r P(`RE#oSԴЗ _78ܭorlT{`K4{q˶VA=^yS?~ʳaVEmXE^BPanP*9 PPc)nRI/,zGN8r/>CzWMJW[Y/fUńUF3*V ]5Ɏe- I)6J7u}y'Mp?^nIƛǺ\.^Ş膫.=;x߇dzɼIRrI׫sz;պ͍qoQ:^G,-d=zN|O4ɥ𕙣ޜh:f=^ʾ}NuOS#ǣrMDVKSI喕JK>W,\"N-l{p[O1bjz&/(GWQ?"",1( B dŭ$ h 5*BX#(۫j\27Rnt\J(M`0o`6x!*LۤԫKuN KlʾAضY@ E AB%(Nq^[ 68Vk. f ;xu6ifOm9'ͳLEĭllQ\ 5d1[#[O"b/Dz^7n#Ak.EwEqڿo'_Wwwy_&bW+G4l+rOnێdKk #UKʁl`R x j n@( @5t,'dnzZب$l1@_H*2p઩AgX7q{XCEǪ'qohPP @K/ F^x @-ux9>g+wK֫Y'ʿ=jYf{yv2cQ` j聐ۂI:~IOGmW+W{飩ʛ4&h(@DH6Xvp㹟-ꙵ2rϕrj[ߒ+Dݚe)Ki +#-?Y΋D%햗}8}OĴY_F^/W|q`OO(RYn,2y_'?ǥ<]BBWlOOW51~n_nj0k}Mel!-z qzVN1Q)㎏OfݣcK6܊XF).TmRN[ #{ #[@p /rRl P%P PjUD .P1Ջυ+_57HŒAAi,M )YyoDž}q xKQ̘ku Q}v~ε:\y] @-Aaŀ(@P !85_Iyr@|P~|,~x|5:$'Y3\ps,Rݫ"#S,EtLd$AJ&*F &MC_YI>sūOn?m4i 8aG}cc͏}< 8M y"xN7S)KnsKwO3iN5J:||*{ qi02ɕJowizo.^]W)"ۗ{qV7i97'إ?VOG2ƻCDUe`S1 LG(C< @< x"!@& r@KUǓ|&=Q䵽}3D*/ &EQ0l1Iw_ojol6KM=wK%GF ='aڴJI $P @P\I+mQkAЧy)GzJpt|md')Iɾ[`I改{Σ˲/4#S4ϲ G= w +.+l䊷 WtDžNKXupiB^ __ᓄu-4KSݜ ',S|yߣ>Wr./ '[ߑ<ϗ44_;yV()*VMmO؎q&5E7Q5$HO-|I}kq^<<$yz}ڸG̴{GcG@O !a0F 9 J%P?|(6A( H"*ysMGMȒgUeegnW`~$DQFdӢk{l0rhI5芲Ɏ.jTVZXl1U+`zfGY0fHm(&?NeEsMnuH)^B9@BV\F$.==.m^ҟӇO.n`47nLt\)q;JygP\rcNt~?/O 0VMNNّ8xM,iSFI߭X }Ǚ>0haX^KsY?\39n&קHKId #kOetݟN=w7 zm_dm|P7͸kLX%LJُ5r=,Zm:K趒b$' aƒqUY,[ D|_@b ED| -JP rDU@Խ;7Ղȕ+g넖畨ɪسW?Ti ZDWF +&Eo Q9zG^8r/T/z>Ŧ{g/]3,n^8綹,QF:!UF;rͰSWf / I]SJY>GѴ4q?{DA ( PIrџUM#ϘꞲÂ]_"Zgsr,~/c&R^Y6nL=B W<)1h%7WHFTmGx|35?卺\ jOuj5 M&M>-FQC38EϏ/3:Ɋi7G3꺇Կqx<\y24&ك^K˕vvR+,M_rqb$8ioi:`{3sok,uoi1g6U&mYE<}|^K=d8h\ړvhX;/$˃OKLrcY .$oMY -L j3QVIEY(p eٵ7e53Ϛy$\&BVJl3&بGldӇFmQ1VXefQ}&ii}L9IZxrqk`w1tݫɓPY<;#f Jxc)pCxa۟IţXKB3Td)<`@*QhN TPP`Aܠ y%‾(Wxj!4`|QS]-ɟ NytR|v휼3dt!>k=-'Jͨ,No޶DWh.J=> 8?.:PPb@ /t?g|%κJ?Q2eɳyF$_'aPϷ@`iQhtۏiKm=t2nw*ډn=_%sqٛsLrN2dq喓SZk効O';܌5G(>)K}\j8b\)oϽJ;̱K2'A=,%$՚fI'-i\(˄oX;wKR}jM'- %F8ړ~ tc͑SЍ|]8nM9rzZ~9\OeS|6 q₌P*ٍ@(([A4M>|PnA_ B2 JA$UU5g)c>^'?B{o+(JXdW6I_ o3u3]˲oK&E% qTRlEP- Q@ x>>J6J;5{Q5Vƹ-zbcC(r[Eӻ;jX?̍:=F*ۃygOS.e\Z ,NO;2Lrc+Τ|| b}(N[91ivWn>_` )x$-" #-ir?SO'Cv|'ZFXpGa~.W7.47{I]8g[&c?q͠uT=<>oVQOsXoK}.4.ߩ/F{C%PQ@#A [D{pP(VDVeZ *yFFyؠ<@ @Q@@p@+p+A pƮRI|u?WqU>+z&c˕u;+m۶YUj&4j{aLF2"*U=>G#Y9=?54ʡ)+L`rm(0wUItvISY_/{7ud!㋹=)A$3䜹HI;5G%rZ'\yO5ێ II-޶O${1tcxR^PE'nrt7EB?6Ͱ%7#N:tzX:}QQ]?z9޷g.D4o{PTeJF+ LXP -l$`K8PFA`@"T  , 8dP( HP FH7FზI^[ztKǦϓ|gRRꎥ"[+w'O{1|/ױAӷ5.v8N}ec݊oԗJ=>UAmԵS>Mj  nAY(K 4@Fc[eD ` PHū@(x!( D$((PhTjP<޻OIQ3FnHǺ* 5+&+94~.˦_)ўRّ={}֫M~|>5_,}_V"ԣ[|s^u,mX)?}×a%t.M-}D{s=>VE; {_Qg~n|Pk TrJOto6䕲biͯW%͒VK4iSm/dXQQ:x*4m4 =i{z<Å~[}FRK\AM5X6HYjªvFE`B@NFHPPaQi@ ` !`, ) (W@e4kpF.\Z _j4|r~_9%$9ԍ3]ŨEEcctw}ρŶc9:؃NF &Tiqo꧷TnVBѕr1[Q0mQJ4d7'cM>7 !($Y|*_6Bt0삷F3tb^~IqI{t\چ -3ȣ4>ÎԒ_`NJ 0@l1rfUQH@/  <#">J-t@%x#(0 HSPZ@@ up{|7o~8M$|vWrgr9{[!Uq[=Kv[Rteƻa֔\wo15FN-'}ai#+VۦOV諯4dZ$qo&K7j#^iڶgqOv̫(y8 o}߲IھZO p)IvoLtV<<1{cKG+ w*VJ)1p_ @h @1P F `b_*1`@dh=Tr TeH^@8KEv&RJɵ\|4Oر+5N-&٭Gf{$n`2RQ!αğ2 >ӻgq=}N=8p5Wh}?vQqIWdr~e`bPE@EB(K#(0U J,I`DF lrB)@xZ@<)PElT(he-3ŨɦȪX_yIIivU1nMQю1덢+DtfB(Lxn ibV5Nrޟd==?!-n;},Ok6Uյxt+53W7B|ӞGvx]ovdQ8ic'jM>PX'. ([iǎNTvT̟MM)RV3.=|Z 龛gJNF}Vi4T;t=DVm/2P ``(#T1t*#^@#P F8  lRTlR6*X XC  `hKIN8\eIlX?JCU=2rTߓOaAJtMkxǜ pAGvᾡmteV}Mitxmb٦:MTw˓ӌ!)7gn ?{$amF//vH3tϥj豨ܶl|FIgE.\inDl[ ƕcX\]Qݿw9bߙ~:Wh:&DRݞTw%v­Yj@6 FFUEAOh(FCX- @`/`# @ F#@+a\h', ʸ2P9z:&L2Wܶe is B3^|_j4?ۯ,ً\P? %gFk~6ngnĵdӽf'$1/jE3'ugp='HǣZ}4Ģךݝ {%絩(<;i'BVFįc(X⢸O4;3=/N`ݵfΟ&Qɑ,._ǩa(GcqU7|AP揥itUGuwY9 Q-,#RpH ,(@XP`n 4 0>CP, F2P *P $E;pIo^-eǯƫz Gja >9e){{{sQJ%8iz֋/Oz"UԵS>Je׫شQŎ0Hrŧ_&}W=f}aS'~\5"=8bU$F A 4ƛadUF**EO(P%K[Y9P`@­ `EnPؒ@<X=Q@QP1`x@ Ul (*(Р  #_I*|?L:LzNWޞ v5_I,SUQ,_o{z48QT66'`,Ya+oeӨi2,]CI+qkV_KӽGWʡ?1ǮQM;%)Dh( @!@E(2Zy U`7P!(2@@`u-דS?9N}ڨm27?:v+Ǚתç۲3Qn8ZߪƎ ?VY4D&rƧlJ\D.#I%@RS$rΪ`@3f+s&78^6 Y04^ f%m8Iʢ~ rDž r?&a䚨r:y7(J0Bԟ\qN پqdkޅFRIQנԸeU ?IW֛,W߱ˀ]NU~e޴?L,0)~DŽyBO>'W] Q0hˌsD2uYNxUKgEt' mENqsFͩbF7oC,xRaOZY^Y.xOJN.]4l[K׺m?YҥMe|2Qi}=_TB^\?[ {_JBZ"tGu_̣WMԱ`~i,cA1%n1`aPEc(  @ (P#pLle( vG6]_~YU}]4ͯc-b-*kW7ۉ(pg7R+I?Bdyfvy9r{)2j|~YՏdsk%d5/HSw+E;Fd;ؚ3XQG_ƙ[WmҤjkk$=ɦQܶ{1: MK-w?|$bGOlɼP0ӛ|Q.GD}'))S^}smGA,Kǻug'&8lÚxr,'8թˋP8)w}2e{"wOtiA×HWV9"׋"an6o6(M1_ϺOY(KS6?hGZ9cA97oncY5QIwî.0j`9JU7Ix&&^(#6Qr׫}YiUn~t-gYb{6QbI* *i0\r~'~h] _lӦKtyVZspvE?)Osz&M?TG%}_/&ݘ~h[C?ۚ'G@&>#~IǍ7E Y^܅L0 T, !@Y@V@T,X`.l*YV/Դ/j13Qޙ7*W 6y]k&1+z[zxq]Rz' 5"Βiv%?^F Θvm7œKdo/"$ c?r6ʚ*ٜcl&*&NXrIt&YE{%.TI[v9MvqJOܪ4D)+Q\rA^ŎIsegi⫦y'fVχ[^ˤ˥qwcpN)KLi=F2'{''x}F&3gjWϓz^~N1]HIwR1I:z }/Qji^M'o5.#~K,O ܟ/~vO~ش;I_]F'|zJwp3IuF/&l賯i_E4^Ij`[?=[קt.eFEK}KvB;{QQL m#+1#5RIJ/}ׁ7P:ǫTm;fecK4F-;w/egoWzkU T{&C`1`d2 P `UʀX` %`"bՅbm%seL j!ܝziG7j}{-8? 5>d gΒy5G2efa)b6Gr7iϗWZ~TIyWdn-490àϚ/4ԣs'z5ɮ-^B ,{B(·ӿ}6Oҋnc|7ӰhVMcikwrt.Eτ/vo7}0}vI~Y%F4Ԍ&WV/rܘ<Σ拚unZ-ԂH^ǩ5uy' %?T]CRcv^P7# *@X [@@X'rKwA'ߕJKQ]OuƗ._I/d+'PɕQY'M.N$4)v; oitX(˅@oeɷisY!%$*]W\9B Ot-^GI%3ǏZK $2XS9]΍zKzDoc91F+hţ44nОWLxPȷWdΌ9"I=Y{oގzlӬB/<ͭNNQrci RW6Y<\MK6Ewt|j)䛿[q{%=ؤJgWOŊZhǹ~Z,/֚z\n(\^;0F}C+^fLEvQѽ-$֣TR} 6-<;1$ɩ-tS=_մQvc;QY9JmrK%iw/ | ]YP/aMnȬiɩ(ۋr#nLRUia=fy$/O&6x& cFK˗8?ԛ4Z {|onDM*hx2/>65+a lzn-u6aˬXqA0|iү&W'{3fJ/b=|xq>ä2QI ک.jY%5nH8p%4_RR4&jKq#/7|ϨRucY9I+A眷M'ZhvSUͫQ/+"N<ܸru]4rOڎmgPs$~Ys+q?ۋ&$2jWntEο#8G?'r%6d_-k?G3ʗŚf[jMۤϓO'_|"^4,J8? Z<&n%(Z?RZd]i$HޑܒIQP0 +[C`b e䓊ˍ%n)c|N<Ú㴢Ӳ9z~u]1eRF-> ne0eYV ( DXS M=v ˒T/pκ\˫&|/f1~ 3tò5+mJHٓz'$]ngdv y {(CΚIˆEWnz95RFY\|u53cS{reݑlq=3ΓM pR]pTřz>>8IPX9?1aP{Y%] "x}9n< IE5FsBmSi\ Ǔ!?,ųU^*1ڢ3 uNHzJdY1y[o7қWzEɵ|+IԟosdeMq@lSogs[5(cZ8vk#l1h`uǒR0˒Z]O~'R_a2*!^7UK_WS?d-/BU)Pݙ}OpZ[eJQogOo%qtۧ/;],M49trÄe4xsCxc!&|ܒ_׳k&%%x9hD݇NCN{ъk&ki|%_fM,Ƥܟ /N|]Qa}6fޞ[dH˂0{rj f, XSNd~M,OPbOk=/C>U;٣5}`O!r*2@WS#@Z@SraTGzo&9ROnv̓mqJT~҂|ьvf5F͘I rEK+M:K^8"[4=]vv PS̷Q cI˟,Qď^W#=\bQq,TiŮBKct+.)\Y朒m{=*j/y>}LtQ?{.9xevLdox>ܣY^N^^\?XtqqjԥJK!ǦWuUFT+-2NܕA^a[c;GɄ(Zr*35oI*bӋ Jn.S1˓+ܘֹΙt>갤^u\O X0~Y1/G?9ka9Gh.Hg_w.'E*q~wJcXŶĶ eBO?(7$_zگoMrb!y1`8d(B( P G  F%@ YV 4ku MɕOTɑܝ9w6XvUFN142k0M;[_y}Lr"I-= Z{(2d18_iŗ,mJJ*.9?A$k_$a"I7ዿ_75^̢yO*0/^ϓKrٌRroy^]+"TR2su= tMvSnȉZPl؟djHr[ۧiM? k*|^+Upo{Sk>Cm숬sH)Vm"q6~5%bbF=cg>TIq~G&Ρm i,U#hGO,t2]^Kݓ\c?WZ^5{ȃָ^n7a$#I9T}O {i, [c ya?#0O)r~ҍ_lDf>D@[)B 'F P (x>{՚X ~s˳hoO8r?W]aydrJYӓGoە8;k_־|J)΢wEn0y'Ŷm4V͘} 'Pn.N+Sl3rQT8$'}MZmNUSynܦ؅ ϱŘ0 1i7`RPEy2 ["YhРA0@l Xg瞪}Vk,|Nk5EA, M) &cwٻ;}]J~LiT+p\KaM41RmN(:*ЦMM4ÕaY{\cf:WsWm42I%M".teeTTجwRMFc㕤Lz3V,)6ߖ!nqtm2[SNb[PP@( _VAǪ%|~l')PKbcVJ%UWS Qj?5zUQQfosKk=T&Ɋr~L( t }(VVxu<2*I6.Da )Eպ.&7e,ZeE+fŕpa8uI6hv~`\Y9&4SXK#slwoVu=csbR_ʉV1ƣ.7NI~BPW}(p4LnIp.2Z|ٲ:7bZlQ{5 Q}+/UÖOloTtc|FOS>0kGRӸEeU?:m,MC\/")%d.2n"aD&Lk$d5@|?^2m1}؟k484pxu8v͋Ip_QoQ-^(5/ j!.P_ۓ̒QP*/(ε/nIm^f&[jr[VpRY{)lՆNl'Kh Vw{{c'KikKΔ(v*ݶi{c-ԺN-f9#/sFN ł-Q92\ߺva9wgnsiIrcUi_0>Y-_ZTmQR[Yܷ6.b?s5 W y5s#bضk%5FkKJ:??sL ޺| NOc\6kvJ?9fzX=)t/u1zWFd˛ʗGz-'68?uWU%( # +]~: W޸g!/٫PyuN4◝E-6̳}>5%i0P`O"T8`x{Z~KK+[E3 v?R<ǥ֫:vUXE>r$s1ؕ"YB1@`[(@y@Px@ .-l`RW {97"[ł;ŵOlB=(MT]rdrB+hGݭK!,ђT5Odg?-8+=R]/-{9V_$ݯ%f_I^?M'9J-qzKN_~$-\{39pN\8mR採4^plrU57w=?Y}4NL:ij|UtWCyb,t殱 N_OM_m7MW\ɩptMEKkŐG5w)wm_v>nN]K_3wmeU4kjJq~[%H`T#8Ip.36$#&bĸLʢ[DIxe]e?ߏ:#W(v͐c_N_Geud{W=,>I0W_WVƗgf?J!f7_CGiaO\q*e[{^Z6dGB8R\󸣫R:'<ٟ̋FGDvKUוIcbޘa˦9"Vax+7ˏNݽZ9dl> [H :nI? DYoOxgwߴ%~ Ɋ[nK(DD_R ”@(ZU(Ph ?]Z&LO nX3%M3qvTq=ɶ^M,j2w("YM{/FdohYF8fIkzG]cur~OB>ϒ:\:qJYoFNƽ_^d鸡N2JOnuO>XݓI{n=[SWKs23ku=Fe)5žgM]&kK[ܛ][[tj8]j>jt:Hri螢*3~#R_ӺWI,V;(ش^&jc[k%l^ Y$5osdtݏJ_'F8:1h#lpGgܵۡ%~J[i/?)ϫ5c:f'kN;hpa_H+Kyl`/{` O N`@EP)yWǪg99Bx,9.r_KU-^ *m|g/Ukw7}^n3珕}Ne[I}_ܺ^9I\XKܷ~:V+š);IxzWjB1/'^d}i(J8sew,o%hyf1r~쬢kB("K->(&ǢN2iG9־P_6.E]U$3dto n7æʻ_;m9J͛lZ}D_݁%^ٓO3Kj1+˛|zz9Ck5lN7ҟ|M;U_?\zX٣q?Iu[f^7<ڌ&$ܟ9rjOnөֆmZfPH[$QY'g;l5r5cӊ7(ܩ*KbXTDlŀ K;X hB#[TB ~K`K @l040iwx`|'R94&xSj\|MNiN>Y?$,S`^c#L&ђ2X挔!|,+qDg,&KC&i*tu2UpNhra='sujˑ/<ϲl0W!=^/_wa=7 -wjW c>vĪw{oݘW- NDv4V@Gn`,X F@(D@ @x'dEA(E@p D/Ja(Bix'!Pr4XxfQWo׉$49pPqEOi>N+= 1D&K3Zit4'Dz\ڥŠƤws5khJڈP+O|&wc#^TAzLbS aƿA[UUpO-Skl ^[dHd`%lXK &-X` dKܬ vP@Q`,R"/ @,XB1AT 2AC (CΡOЧs9t}:>CΡOЧsO`1 9ϡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs軷]cls?>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡOЧs9t}:>CΡZk?>CΡOЧs9t}:>CΡOЧs9t}:>CΡ9]IENDB`pyformex-0.8.6/pyformex/doc/html/_images/front.png0000644000211500021150000000036711300456345022065 0ustar benebene00000000000000PNG  IHDR$xbKGD X pHYsHHFk> vpAgXIDATH9!eg7ڄ-=[DYB޾s# [Fb""qehĘ/ rGחb_&zpV@E}H _J k1w/<iJ `Jnv _yETU+IENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentD16L40d22n6b25.png0000644000211500021150000006612111247035000024470 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?i IDATxۖnW@͋UU"1Y Dc h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h 1bF dNb'9T#v@;b'y99&@Ȝs v`N!s̄ `.dN;L h P FJfAȶ%29DȜy9ɺ#NN7Y{7 f<݊ 8WS)1oGVk%~<8 `c82 v%k!fȜ1ru)qdN00^Qfh"m7cdU\PMivg&l2#sn~aҴ X̭-lz%@ kʖ! 1>""#"9q:'x灁T kK*w?>+p>hL:'x-u%p#Gͧ(mȣ"NP %OI0+Ɗ p̈ "c>q {ZЂ :~w>o}O, mWs-dN }_+D&L6x{!j`^Mt\ Ɗ1bo-}fv-iKODw8Nscń:b,;]:x 2'Ow\8N-}6 `Dg+Ws٦,LEsfv h, Ӧ KBs]_Wؓ;9x'Z;3*ɥyk u/>VD_j۔L'[5ZF ݮ ށ5x} 37#bi}#hv°xoe\IL= 55G:c-092'37S&Y`'o^$>b=[`fdN\0ì+F&L= T'K˾}2k08۳-0-2'sʘOzJ{Njbdayլ<+M0ͧcX𔢸zVk&}'0 \F:M:}618JkG䩥J8RDžF|؏"s.{LyưiWt<{8xwhl$b̭`)zLf"ǢRO=T[@72'ݮËZRZ҈zam!@+2'݉vsbhv$Lx2'U%T3I{xi C@ӵlj|j@2'*.$gAJL;-F}413 [3GڃѢqa%Stmw@UdNnҦHUws0&r%|G7ӑv ֭*,+"Fo?x}AaRfuqto}lOP pUmPgLEΣmw9Mb}/ pjNQ0U[ @9oEaS ~O <+@iaUqA#zȜ8]vw+ z Vy V=Jf?z=2' 󸄜al|:Y'W9vh*C{g?.լ]^tc/q'\C OFϪL"`DߑjiOW9P9x֭8b-ة%VU3N?Oq\ "#'^O31"b>G=kQ=E䄨mZآ:{Bg!sp|к>CNxq4<(s.{LSfNJmQh?ז;:2zmT8ƽp}-,WJ:Xg/Į /=QgSeg|_[ wnh4qȜy$Zj5h4tM%kޣҁ(Vpݣ+G sf^ZV[&&oQ8POŻ_*&C{9.Ts0(]cg`+GH ƒie7햻cx~O'9$97Okywt8vN&56LM"k==ހ *&4(HIjF5")gl3lyýR*]LjHꉷҘ}kN=6U-ۏ][-nhհKwQ?P(s9RYG w,ݭBvj _6=4(|) 9\M.t+R.4NqPmתiڶੳ׶Uyu N9'N4jp%sƊb&z}Tse0=9j(LBƫ %Ϙ;:<;nόLbdN7һ4XQ\s齕ˣW$Ȝ+97cl%OD83GvN9'l6cp@ ^3f|zsd#sFWKqyC(bȘTkDw+0ٛȚibKy n'6o;-\o>v`dN8Xؚ;ƞq]{^OZ|zdN bRbwvOD{瘕s_FJ9#TmةٵƯ|<˼_*Ȝt >Fdmg.멳_-֊8WN["$?1Ɗrk2_Qwػ|'oۙmOW MͪXz!;XC@0|Ҽy>#f-t`v鸌PF^Z 'WAJ,]rǩ]8ޮ v녷Qy95Qf=3_>nI@5ffcf,>Q*EƤ0j>zX@"ŏ<=ɏ/02'0mz֔>=ǫHIW+=*JZϜ=Y/MEa+9uZVe`_za3JGէ~Iwf#t sy FqT:~| #Ƅ5Iw#2' tY:h`ONcwbQ5_]L| k/ـ1Ȝ´YBt ReXt_WK" NN%ӽ.Ȝ@pf+_yW#"|Sk: /J]yN<΄k^rGHA卡"CDžӭH֊+W):v˽ph `VFD6ToW1^vjQ/+T+m3F~W|qo;K6=4h~OPȕ)Ƴ%yFMWBjg^ۯ٪J`k_sY㩙Wk?Z9""+.tΟP$-t~Z1b_7uN2Z(ו]͏8XuT7ƘF&._UgCfG3qI|']N/1{F'Ln7Nߧgn&wojmHGY/M,8sVȮoT) 2'P:'3Ê:Wo%kz (C8-dfхmqemv=2W̳]]] 4J9 N}:euօ~uLt s+ݟy#8&s8j'vRlbY'܎P.3ApɻH2cs7TzBiƈkm.(omXp›hO}#w :vgNpi72H]+mڐE*_vX IDAT[h^h-C.Zg_OvHGG+.U"små\VhuGn[>=MqcB:Nϼ s sk/#yÎ'敎bc͆U׺[kM*[=(8cքPZb;N qc dNxZ93-km<9mJWwՊj/,OꡉٻA@nQlzE$6e;~Pۗ|;qtCc/xWK] Vtd9֬ U ,dtZЇ\Wn.vsS- |cx^7>r5 9n sѨ;P81ݽ /i3dom/&S֥͖mKԹ{{}LƵ;8=?'`A^}ro;׀a_.uq/\gҺ$s{v9줤~ W1A^9@65B[]yń:T65_q׹Įyjn׳=zXvsK4BV :i]'8knدÚvXm"8vȜqvX1 lo9Xo3OWeguW| seUuNeCg>-J >ƈ|؏xk, vx~iw#wnm~4kۨ͘\[ޫҹz:)W]-(BƹM{c)6|}[@l!Uv62hChOᬌZr'uNk9:B;MST"svmvE-lmy>H@"b, f/ٿ 1Y egO^qjN6s:",zzڼ݌k/n_<1uN`,]U?n-;ӿ+ݡފ>ЌAyZ/]GVcVN`^!}u2i,ǪHٯ04SvѶSnWͷ$ӭ]^ V "sTd8X:3(]gzQK_D-d: e'C.Һ s@GG@CPe#\^'=|:8Ήz؊U80:9Rǃ:g-3^i+̳&&I:<'`>-î!褃U3Bi؂ߟiZ>?4؛L @'2'ԫQ6#c`gLqgg; m[_x_-S^&?pM`"dNh&N6zrtWcĘu%¸a]'Ei2`ygk*^bgS XީB4M^qྌOFQ^bu{~Z|om{rvZ^igRɮ.Ӽ=%d|;~g4FDC,02'4rTsy-VǥqkA)Yk5϶>oaLXs];*9Ezλ~|c˘?}TD"\GHV;Fֽwu*vϜO_nv9E3 c<*:#q+0#2'Tg#(Wqیz[|yቜ ±Dh+Ưk=<]7=Vuq1z[\FJ:ӵ5^_e27ɫj&tQt_lN`jdN$ۘH(sh5Ml{6Zn'W*\%oFUڑo_=\4NZdN(F_Ym56xS%z-knv ˑL@8g s@6oLe1-`d@Kᖖ' nw/-T~SKw~%3ws(2'dfItv|YR gNQz9f6z\9/J:N)Ȝp`ޯU|{4Q\ٺ#ϦWSg~s{ȜmOjY&w)*96T[>9^P;1m~ދs@3}v.rl{2e[Ҿ3VrX^'Pxˀކ;(j(7uZ8Ts{RGU҂CKn |ڳ-ګw॒|Uͯ¦Y{50'2'|Lڳ}"h| F{ku~!>mzҏ0~Gh <,Kk:Y7s\RVowXcm6 O|Қq;0jx 2'+L5vV9iI,y:v`A;ͧN{mbnsr=+<e |⣵;tAD]uo3U<ܩ"h"Yg|Z!2'k0; ͔{._<'p+ƽ&:͌m`K?^eldNOwv׍d86zክ)_oȰono\Q*Hy1i&K9ꉩ@wu;ɺu|3h[n.r,c͡S\$oUhmBz-n9oy$]'2V<c9(elYͪm3vJ§{hf3^.V;g>,-?>n9{9pxv].-mQ{+^m]N:;J7E>xNykc^%_NP@8 I&{gJU>:v9T;](xnwd̝>܍C['ON-V :UE/&tfEqQ:L^Ѥ^6sSueR&S&2'qSُR5+ߝ-ý} ϐ+;gj^ۛ gozh Ȝ)YSsk;ɮF⫣9<&I4޶ۆӵY͙( 8dNݥ˛hm1am vNE_di-xymW2۽7Cj93<'Ȝ\ FLJňM֚zU_ꌝ[Fk+<O[}P3{3'W >ƈ|؏xk, vx~ͩGAیII^=l[,tf.땵\jwefj|cx pfy||k -2`Vu{A0Vs.L73ޖ `4:Ղ.tӃ˴z4nsFvm[Xoa:ь9G2=M%.,idN|j' ͜6hoΜq#[m$v)b=*̭XO$[#TLw6ʌ:vv]ut֢[_i(DR곪 ƫ{Ao kpnGjIvV__D#"(AЌa@ǐ{T'ܲ\-Oob/]Jp `CV+8)5=!v)-^Q(;"w6_̧@3BTCfac L6{c9VMi9qgouZ` 2y+dMM2ݻ '`>-Ȝjk6nY*[\{֊{o cd.z&&uLN퍴 @?2'Wҭٚzg4%/Ql|MGuB%K~+@DD} S!aFJGfz{`6'x@3̅:'JIJ 'Q#XmB2d?hrq ӌW2fʏ)U-ur&o3`ŹXqJ;cu|Y-Ƅ ;sj3HsCf{x2̟ٳn02'ۼ҅.mY>ht[<7>=F=J^t/0f+uNӇ'z?5G'6icp='{M8].U>'[AֶOk ͗t ,W?~DD)oLEWV|"U!\ aȜJ˗t?x1!I'qеp_}tvΜr;m^nLE_ym ȜEʬ͖ec8zyهmv9W'tltik\3ӭݾs} s2ZW#%'J]Vh,Wxxks{{8\s!tQ>{8cw1VUf]h~twi'p'ngmJe9~53y;S9QsW8RckϞcj/z͸7vf6/&U9 !m{{zӏӇ7A<0݋4xV}dN/56`Z)|z%k֩q͗Z*0yj$?u"F:Q@2'GzR'1l #1T0/#I:Smavkk#4{9da 9nn#U|j skWr~_S.yBsG$˷9=\D^o?v5|H>)]|gSrlnT-EFgIF9 ^Zvյ<^,Xf~l1Ϭk@|e8>+ƭi P^1~:*'x܆;((w6zGYsobCKn 0ujۮ* |&o]ϡ=NsS }@g@$EN*ѽ=kDçhxUz}>rfމ 6>X1r%N3RgE ik EkSY]yP$px92'&ZjzQ kα]9اuNTw^PtF_N8 F|h.U3uxiK=NӘ<_A6pP+;!Q|8@Ȝ[|ֻϲ't81b>b>% Ik6N,0̟v%}m<\3g#B<_|6⭁TQ1-s Ԫޞ-/<(n6c_Cָobk5]4a 7syXdN.]i;x}85*s~D>vtnԤ;iڶo_mSc#m0v6:Ĝ2S{7&X2#,X9G3⨧HtP\yFUMrcEɳqe?V%v96*V^6sU>Isݾh򐣇c^ IDAT6w\ը=XW/vE۔liSA;~o{1{$p@;F7@w>Łevnj#œw#&qx_>mVR(*ρ㹵߿nl&Ot3B}ܽ vƯ0vb gz;rg)gF6ܶ5w]u1]{.skǩ8fv^a 6]oV{DmG1ׁW9ώ@mdNY$ӍOwR2a zȳ5mi9iFةoh6cȼڑs7,ytljuvPOO2 srsvXؚ7}`;dQ:K|sjul{029Gh9~ [ͮkU - MSq;!oHp _F3~SSW㔦Ȱ1Lisl#=T.Ziyznj$?']=Tv985w5Wk%5OIA3ᱷ_t%@bcc%Lj;P]LVW݄ҁMݲܓژ*z(<[?tj1Q`^9q2bEĊAam^hn^5ۼ[k]47V;Ğiksu=rGLגTy+fbgQ ǍzʪL v6R2نc>Iu?F;V5FIPuN`Ny Z9_^f\Iw^==6|PY뜙2|D>^nx^u>OZ08ȜTRaR^9l|gN #w6vOb=ˍ%9wgÆ8Hkؖ1>Dʜz>ª*]l=KOU[U|6v8r}WiPb FȜn@u+Y|(TpbxKpu|д!M h K1m9So`Wsn j-շģ;=DI|ҭ&m@dN@p;r2>1珩zihO’`vhyu¶V{4ö:^{jDgy LѼxu-ZkFL {f5I:M^:L;aY9\% PNm;6˚a;.r|"xn?n\U>s7872'0~@dĚO③1ƤF.vH.yvw]%ZdHMm[|moȜh|:r,m8S>Ct%ēgY,5x o~>E_VDygfeS_U荱KGĖ%Sk3w@;{{.=wƊw6aNo)m+z}_EhU3̓G` !!6L]wu`=͍p+<<|-.}?PNyİ ''tZu> vN02o9wV'PL 7Tz]NA@;dN OjfŬ]Lm& i*h>!m]^9`E-|^Z DKi4o'@>ßSj[k;yY+qQ6/xI -&{oϜ4qx\NP 7tVWo%wWSBSNcnshk}ne 'e EIϧzb;```/IhuL?>? mWe~iFW Fq%OlԞ*{\2{U=6/}ày\ܠm$reo{-lV32+'lͅ1\ ތ{]*oZ+u\cĚ1Ph-PMǫgm3b/#4+PdC_>ojj_.Gyz~^Nמ h:'P¯臄NS;Ԩڻoo?>T^Uo_q#lhzk3ܮ+i_da'#z^Dq='P J븤Q{j^:^tvk-&^&yɨ#'ا2paˣOxNB<;5q(H33 w))qԵEh¬d 9Jj}/ϰCҕK=5Ce 5|żf~y6:;J߼sQjP;9LS 7285vjgFNף9۪:}CКqteGݠRQ.9櫗*g'i`FdNඛ_{.#=K$ϙ̓eS57Z`[rTӐW:5hN=6=O 3"sM7,YyWDȽ{F"yv8O}ލnQC dN`a׌IHT֏ks>ߟV«`/l͋5I|ZuO|/n!mԸW ׌wR(tGz֊1vw7U-i[ֶGѵϺԋ=\b 05 '19ox]g_|-jxal_8q6S_VɥHdN@)G]qכ6֊c̳̹'g|ύȖ*GɥMx*2'0Ba*{x:feZjh+iPN6읒>ߝu:57pQ.g3OwK#' [{uM;Oxfk>vuX [em՘b^ڵ)w<2%K60"suȜc/漱& W9ݟ~ϜDOfmrcj $f Z#H K촜Ũ0z]OsJ{PwΥ/ zrK=sfv9:ucX(ކ LCa+5+d+1/nZg 2'09m5Jsicɂ':>yfI^HO~c x/2'E:$O>ݣH<LJ$xtg24sۡx tfbm|'6 qqɭ͗ hDE'BZ}QZ11˞*c&U=5gvEYwgjsM+˭fkdiF H@3Gaƈ|l=G[RŗD5۴͘=XPcY$O}l8սΎ f>-2'Ӎ=o-|0yv8mg9tBJNrNH? G*kfE=R;ܵ9Ϗ* >k|Mc&Z?^)LzŝƯ-{a~v]^p4һq9ο$]e sLRPV"^t&;W9GԽ/~l-{q{ 8Ց97Ґn2FUm8 Ym4pn mZ sISM;6Zٮ?Ɩb~̬QNg[vÖT3M$bHQȜ@#bHw< qfm髨 s|\9b,_$t s|^6a^og3pNWyooUѽW)@"* zsN'`(2'P`g/9ch<[wsXDa6,d?s"E snC5/|nL/U/n(nWƬ rscN78 P 0pW♥{6 _gh[q:^6@C1lyv Iysu}֪^v47°,r1'`42'poabm3sevf/nmE>$R}fWlF+32'p:4!m҄A1SM)B̀ ܵ,w9Q4Lg^]( x2'%:}4r3ڽ3"Ram~G"sm`kh:n4O$Jsn7 4) ø9 \d/gsGM|86^.e>-W Ì.7/"[xQtM#WI_cf>c6\b t23#Wz^Ě b'f"bgVjʛg#s7Ӑnz9rg7k2b1>H!s]9v꽓ᚿꁓ%e][ GDg6@oVszQUkZcGDD>V5yw2,8Dn $hBt]:hI8 ~P{r sk1568Z]l;Ju]椯Qg[a&- 2'0W/wTګG儨VLM{o=Wx!2'0R1.q|&h&&|uݽPbH2y 0s?gf PAQ$F:y%sn^^ٸ3 Ȝ P<>yJ0ҍ3's2'S4<5sC% 2OØW.eY7~?'pCzw7%mpW^]Wa?K漷hyd5rcw2>w 袸UxI^**sONj6^r&{F7o`m'kTDCauN@#it )MK^WK)cV*ui﨏%`p ("SLmΒTj<56|: Ȝ^5c){dBVcymgu̓ Mq='wg[V]Ƌ~Vi9 'Ʌ{S9 mDc jgW|uܾi}nJrܨfAD-@p}}]wNˎwŽD)N dc4P35Ϲ~5"VcV?mÝG&V^3Np+gs%c)pǸaў5;-jv{x:qpH)oPs+ v9 b.U%m0 x{$~kw}x\@J`,c/TNu6tZ{὏;z5~Y {Amf9x2'\aO;vus@nH7擩2[ &1䖞שYNG\LZ!s/ d^=]usW"mx-b NG%L>4~-jMbHBވyS)' 9}@` \x0ϔ]OmK kQ+r-DB:^@ 2dN…Xw)S&Tm\Bz5 5[ @d5mBNU6659K;rMȜ~6U IDAT#ṱtP3t 6zg3ufFg& yPԺ!~TjҨ{0NӥhFsHby6]]p2kJf*p0-ӡ y&5p­.~^W3 ;Ȝ4\VNJ5i#G w'v<M|ZfGjë@ߴ oAc\ZTt`@1nKbň{x[uphі^zj8!"VZe*(DmDo+г+~_E.nM*] cҁk?~poCC_|9js߃2\)dNo%sFjQ Q;ML:O |Ns{m4Mm >p^hN5â?0cLϞOMwU`Z`pVY].x5YYNٙDD>CXa-^Fحa X6-%P)%:=m TzN`J,/Lүf ltg3Oiu#ps /'thd 9bvc#Q/L=3>Jm=.B+/sN`B¨WIXƿş{L>*r9s6G_sN`ZmaA7Ap1xzmEIp1f@kOB Ĝ(3\H\M 9)ss3q0s u3b.0}K˯_W~޴=X9'@a$'h"]gq5(e='Pl:\w sN`:FrezNNwtT*iN`bjmNw" ܇&;?nj[iN~>t?-pCXQGNDmhN2tSmw9uL ps q;v3'+9'pom-e kNZ}WfZ4'dT iNu3-rstfX˾z&6i{X 0Nm9x&Cs<6_DDS,9{De|i&nm63` sNҗ9Zm2'K'@'dNYsx)0 puqN{1 M(I:vSs~Ȝ@$?dNc'@a\Y29U${ɜ@g e!s= 8؄R 9n@l2'Їx TH:Pcܘ0jh]-cȌ hZ2cn3N2'ЮhsH)=Ґy3v#s 8W3o6dNE792'М2ɜ@[ f p%hHTLCdN9f s8?i0 7h±)d&s8L ɜ@Υ)d&s5[QA)@dNZ1.R@8!=S T8r&@$ݽ;=s8p~GM @U̞ @ o׋ȜmfK8, pZ6|ER.65dN"ۣiUKZPƞF4E[[h}qLYM 8u:'sNB Zcc4 ('3Ny%F 9a6H!2'0ŽQh 䳸L8"s9L4J!=v9mg2'pkLgbX(/cL'@dN`i3 9͞;""w59|:6?x4DE2'7m6eڅ1X6 Sv-$pS[f^ #sV& s)ɴo/1GP!m A'}8FK9W{7czmwYzbtec93ݽ2+@s@ONj88' I9ϧM')~2I, exF̭F 98'\8I;VАxl80 GOkeP#*N 5 2T`}Z[ (cr8'ԦL1s@Uļ8[BqP)6@ y$̊l`‹bзa$Xej$mIO9 PE`d)prKTO΀@x2'saQ'ȜtP5"Y\*j s@1]̭2'pG'KЫcwcJ ;Nt˵t BdN[ؚ:속]$3nUE3=gU< )} ) r2''rxɀ78ӆM |Wå n p.nBm/o쟣*-'bbh" Fhfqp ^Gۼzeם"k%ɜpU/۽MYI8G K$lLu5g=.?dNJO5nR`.<Ƭ̰L Ks;%' !ɼotɜPX72 +-)t:&s@I7Ր9G.ȦǎmP`Db. -ph%s@הQsáW  r-JҮ5p@vKGel`^qkF/] U1 O3o'b9 cƐ^'hţY 68˙k{fjl-ah#s@&[Rμ V#fZD+?!s@c6j*y)Ȝpl3Cm *2b1ԀdN8@xBV~I$BV2' -ysK:04zQ0u ɜS5Zm Jm=Ȉ 93? h Ck#X9;ç["덡oȜtjmI'ÿ!׏q)s-6dtve4l&4e{+QfΥwNB7BsЗg[KӚ7S-]z}dA$^̧-!p&YyzJ>2'}Yim+CzJqB5o-W@W93_ "0|=O?#|o׬g[! _nd"&Xf$ttmIb6o9bz>[ @^bx|fx/&sТ8 CZ]h[[; 6yH#P @+FiH'+hck%69` @^f:S]A3dNj:l/=AKdN4I<2mY尖<` @&Gzqo?wOwkxj;`ȜTb]'9ܜ 3|N0,/(pFݩ; m9IB/W8g(TsW:BJABsr4g;}Yv=dNifWMᏅT:!s̆G=Wu[W"sƆ,إɜİ9p V52m[4En{7N\tރ~Ȝ0]Rr#{\N&ݽN49..\o4vֵ͆; CJiH;c_tt8':4)piuyմB.a2'~>md}Q\3#g$F*G+Sj6 %|^:` @y oB>V$8Ls W~mIwc:$sPҍږ :5UU2'ŜN}@Vz"h @F8K뤙-v9fO2'Y1(>7 -$`1+(;@AsO֢m%t8'GڽJW҄#'5N`_@όspNy26{(~:<9WI1 r7SYt#âuځ }s.B2'@CH9sF-v#sר @(2'@+e0(JoʬZClS r9,i =ci]9~N א9Ag4MuQ^׭&??ި-f1 [y6z-ǁsI/'2'@nZLL4dڲO͜sYh8z,a…< ӹkK̹ TIuKnzc7W ,9rx]lR7זk1y0%vb !s&+1 Ndn$s0Y\QYz{yS\^8w5u)~N5}TFXZ_#.A9v2'No.M!p[n5o%Z rFm?sB̭cIgp/IJ$X$[!l69kE{*ֿ9/G~#ӓtvEjt8''ks\V8dNeV>ϖUpS!\ g>q9ϟnwx!s,XLXn)NnQE KrVNFؐ6s~ʨ+SpgcRFsExf9[ 5ckw<Rsz>qHǹ&sVM[5~A{<^f;{x~sMRч׵)z tpNh-8r(ԋ%f.}CNSgYk6aRJC.=~nu5QsY>qNW7N=rM2g}3 s]K$9+fh,G4]v]BwBɜ@OT+Tne9$;'əO~w9n 9?衍d7lBX 9V^+x+*eP/}q}N m3> l=sN=L8ѓQrj9tl[O&g bQ jB gfw&.g~9mܞ̭ͽ)52'Юj&BPGF:|}$mKdN b̭ZRnc;ҍP[R'Z 細hK̭J=6d~kaCn?Ql> ÞE]6.@(w׉k׶ͦs*˾z`7ԱTY 6w%9Wnn2 l!s]$8q}%?sǺ%3mﳋ \~ H̸ms]#}wcÑ&p ec¬A~);|ir=tV{5P -~ ,3v;aRG4 *RE>-p=dhם~ެAE.h !s623k?OwJN$IYjMb?ϛnz T2'pADdj=^?:>h':迻7U*fox<-GԖqrf|Y bD,WCzɯ'N?82sNwMJ0U]JW>x*{6rdNE$+_9u(M.T*{?NJ)tJ)9,I)U&pJ,%gB̖:]Az?/mp%us0@_%m7m7|Ftp/@yY9ҺJ,s5ٿ]f'; ytc@2'Pubc]>tvbΜF2'fiÍY}K3sBG/i3 :  5iV5FHRȻs文 sŬH֛-͢1tXN[;M<8~n'se7r8[BR3AS{7|(Une/.TzE_B9ܞ%ϴH]h樓&{zLs':ƮgR @V+gU'Ks0HlENV['ׇ:3n#I&Nz 8'sަ/ "rD5E ,Z|pk;F|d|T匥ggg+mڋ=tP k/V֧|%}IDAT_?KU|X_hw/>Bq]~X wg[bӫ@$2'cZQWD|{ : ntْ??][eòn\əo}ϭ-9w3%Ǩ9~ٹt-ڡ ŦÞ7pݯ'Яazض>)6lvuGf= ٮijЯظ7w5|L[ovyz^5-o+w΂sRfn-t-%K*Tb p@7{o6Ƙ[ }+TO|feFDܯM6ٸm%Τ/=~БN Μ+kO0 rlӻӵ1ڥ| [[ 96S%`76HF8~vzì:ȜЇ5V0FU~n6 r.Yzϳ?$OY]qh9u_xr FPLϽcZڌ_8p7-@sdNhר҉"wCC֫#h3h.9B_)a6(Zg !h8m)Z !smվiwtcm ÿ!Ji`dNhh&ZZB[4˜'ɜ]U_n47G'8[GNt2"pvo{\+2'uqEe͋Kci9jANypTvv}wA)5}TFXZ_]:<7e` 5{{suosM[ɟ/~z;g?nzfn-iy8#{>|!u,\``9BmVsnEњ4yZ-&^n#+d3 T~"$ sЕ@ْ?S!&]Ahɫ ɜPU_eEUkW,_ӱe틊:6]-B?dND sB`fR`++߱WaϤ?Sʌgͦme N&/H nO2'ij7缆je t>XQS̷wdNI>E58v\ 2¸룆vB񵈺x t| GpuNjmh]0#Uzٝ۫N8/Et/ bvZ8H +:v(neʜ9P'nuK|lX_WNtl,GX9.l$sLƢr+~'9Y1əO'v2'\t4ȹf[{2 7ևCyϥBl"sµn\RUf̹44jȴy]rkBqRdNЍ*}n 5dڀ-{N68'sw'Ζ\c좻8J0 "> {6ʑ9ᴀJ-@D8@KdN8'Z"$sl{K8@dN8MRU/p 2'T0NudjWu p%B+z.fCJ9hag# wr=7-qNBխ|Z.fm%má*[bg7e*'wq+ 3 5SC%z>Yj qNNJ?70v qNz*d,_IȜPjr#;?TKOSan9<NdNPJgl\6’9>5zv7y}cs  gKr/C -㥶^}T_9Ӯn?~4Tr:阣(pTA愪(sSNnl?AMw PmG#sM-p# :!6@]Y픽&@dNDok-7:0 P)J{ \9=$MɜP>c)ɬgȜK, 76!sBxۧw蔫utiɴ-9!JW>rGі>rJN s?,mGɖfÿ!%i`L~ɴ )]155 ?_+%j<ИaɜczQ#H]~-yign-DOJ^&{Չi0hѨ33>z#dK!= s2'O麑9첮[_ٰ;P2:y#_|6,.Ȝҕ,_S3X_ƹS!gW<dA WZ4q/hṋz ?ݽe)qx| u*좯agKH#jZϟ)Au8?o袧0P-EXѸI`x!\:-]kGΌѻ%m6=c9!V(j}-"cpG_M>9D3D|>׋})%BnkϟM837dNXRΈ-sʉdδz62'bDY7ZY7]z=aw_ {(dN7./] ~Y{m W!ꠃ <6 kg׭PYlEg ZYHJ3iH6aEUUݽPc0|[RA}xr_rm~ yVG9W-;mǜNiZ(kv?$ʶYRNv&+wfkis=Kx9RhI+a/1AΩ|7l`l#s!}A)K,k"~ dc\z8B񵈀Klev-xפ];̷ll&s{:]q?G^nLʐ9]GУ%2r8-~2DiMVo,5zz:_Éb[@!-X{"gF,+ҐR1%2v.9BWUyOxV[Tkz'W9qo7n=|B3fѯ gP3:}:{a\L愫]~m>=9Gχ/,\I:_?朞'1ɜp"?|DSYEm[rUIA ɜwenREDh^2'Jŷ?M-4E8qr'B+ V®/D4}=dz6g @2'BDi@y0;2ccɜPXu@p]VG̷w@y-AyL[9~ dN(I7ge-wtm(3>ȉ@2'pxJJoKH7?u| $sB1 fO-1{fcou}@X2'P3ATQNK!3q D&s(7Qá,xʙߞ ,0C_7;SV12 s}90/h%f8 -@=zx??6v`9`;X(={o leYG8h{R:cW\OK@dNȭ ]N)p抑o֛V)+pP)U-UJӻpR=ߤN%sT=~ RJ%Wug*\ KxAԆ{\d{`1&Vqݽpq.GQP߭=V 9!5,P TϿ$zG=9!w &n"g3[& 3 У['m3 9ߡN֫hi_bG6{n[0 З [ pjL-yFZ!\ݒ9:8ۍ8 \C%=M]$ \N ~1Û[ @J'|3 P9×LidN8AO=*m2'@ rzo]u, s7`fb~RKM8@hYox{WdZ8L.[~&$s4&y2'-88'?,m@.2'@6܂IdXi /JWLÛ Р~JP @a К~9OtR =tBG&5dN ݭ>ҿ9Os/AI]w \F.'Ȝ%n5s릞w0~ ?p5 |sևq7x)EO@uUOcgwҟ,n:Ϝc;W{mܔ6{)8'@V03NBZ[UɜNyYdN ϟMZ [dn%sdKr<5}i?(H k>e?gxܫ;N@f}_p-m@dNV敦HC_\g<6? PaH0 ][Zcx9jZ;ɟ lK Жj= ɜU#} Yۇ@?Wq6j (GzD,?UL}[ijU0LϦAu2~YzOf` u `Y=_Kpk6_?“9*asqˑx~9~O 2<(TکK R6HW9*Gh*Տ?q@ì!#Ito6&6mIIXCl3*'t8'@^9|@:!spt <ɜ ia;'tB`i3 (Cg4ysn\v l 9Bklpqfɜ5o(p@dN ls; 3SN  mދ9zt| sY'dNj^3>TdNp'ٜy8)bK!3]N` 'g> M;ӐRRz V eUavAZΜ"u%jsBV {-^8-dNlq=HOUtl`#NK8*_Kx6M愎\N)/uaXooGl' (N+ Or'p ?aJ)23UC8ƳR⇧ֺ-@Vݽ ow̧3 8'm>r4 yqN^@F2'GފU@̭ %7peAD(8')I@9u`0q#st9sk.a\ݛesEdR7&=iqNr8[h 8'@N^9L"ʈ D` ^0 iEh^8hdNxϭ6  48'@ŤM 88sGj!sDfg\}@2'eoKj$sTdZR2'@s&P5 (iȜ s;VZ"sD1G8ȜOʔ6ȜiyɜxIi8&ɜW{-2'ޗ53h p@dNiɴÿ!N's539qhP6 8'@~)pݐ9r6 EsL tG8kSLF8ɜK^ i3 @dN}vd>-;`#isK%s|֙^ 8` p 0c41Q`Dx!md$s86X sHȜ@,P H 4Z+ɜ@/Mɜ@MȜ@ˤM{ɜ@S Lwo@)'dNJ9(ERdNJ9(ERdNJ9(ERdNJ9(ERdNJ9(ERdNJ9(ERdNJ9(ERdNJ9(ERdNJ9(ERdNJ fIENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentDemot2Step01.png0000644000211500021150000000767111247035000024725 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?ikIDATx 0 AH-UD3w=3 - @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT~pO/` ?'N9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9 KJfIENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentDemot2Step02.png0000644000211500021150000000767511247035000024732 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?ioIDATxٱ 0A%3SNֿf{ҜT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @=YkN7G^sPY;' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @EsPќT4' @*GIENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentDemot2Step03.png0000644000211500021150000001010011247035000024704 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?iIDATxKn@@AW&,ْai$u]/=9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*9hN*r=g,'x`]GOǤ9a1[*,4'w<|LsScnМH7)- 01ќ|ǒ4''pt*4'F0 f03М@lYoҜ, 4'׬ NRlKsfTiN3SҜLz\DsC:?w= Q $ pt)@Bs96#AiN $(p lqhN!AghN^c |Nsp( 9(4'' lqaiHPX``&9Aa6iIP`!0 $(\Ms [\8`WzŅiNiNx-.ќ9"4'` l 28Ds-hNc&iNYgLZМP*jЙ9`]:4',ajLzМ 4'L,Ӝ0 T4',sҜ0* 4',kќ0&ҜЉ r: @1t&iNp: Zҙ/ 84'3` 94'{jLFsRNӜYhJs @ag LsPhNʐp9 &9XhN0 &Js0-׳0< l&Cs0 ҜMj4'C 0|~9Mj4'4'גPRJҜ$IMMs 59hgo4'HM9 !g 4'{HM` R8DsҗڔG&Ђ7@S hNI$ \BsT(@jhNҜ+4'Bќ4'cӜ$4'<ќÓ4'&0? 05UhNaHM`9774'@'R(@s\ -Pќanh4'@МCs4"5ќ縡xNs"56М;Lsl#5Ӝ/8As<"5ZМrC ЎnR BsM pݤ&@S1z,Ks9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9z{LNs<ӜO ;y/O|4eb ~wj Zݜld 8'kvN 90x6O`v`8S[`nk`hO`jvN9<9`fd`2~LSf`nNm9fFfX`O`@vN<q9VTqhN1`nk 8'Б 'p=;'@OzvNr e12"?\m-@uNm;'@uO ;'Мw-4gvNU[;'x a<34'?pj 9-# #<spG3vNxFsЀS[!4`1;')~Os'` y@MvN'Ԥ9S[(m-8"tsГS9#OXQx s0 <`=8Ÿ`PNm`vN``t~Oe`ty#Ox ss0%<` vN'L <a9G0, 0;'qj s,9XGН<.'t9©-\m-9k9ȩ-\ @i~O'D9GМy iN˩-sj 91p~T0;'#O8 ;x 9`<`;''l98 iN8Ky3sY>l 9%ВgvNn'Ź 8;'\( y! s< }(O*МГ`ms@O>l 0 `=vN;' #O9`PhN`js|9`>l t0 `:vNGBs'Ӝ07 ȼ-#s:|9`>l h <e)Os[ y!s@ >/@vN( x 4'T<'is@]>l @| ;'p-vNyАP49''y <öd6a[s-9}<`; <Bs)O^8·mx ö|ga[sy  D(Onnkf.Be98( \#Oj4'p5 PPx ö9|`UvN?XGќp'24'0( 9A-|`RvN`>l 0);'0<9(OYhN`V`|V<Ƥ9u(OhN`5`XV<Ҝ'@/By\Os(O+iN" p ԥ<4'PќМ)O4'W 8Os<М?ShN'^` 8Byl9SiN'3 hIy|9SiN 9'PjҜQ@5jCs< 4'@OXOyҜPz4'X' 0" W??bGs7`:vN}g fj!}hNE0? LDsmN`fҜ@3&0; #;t& t`![gUiN1O:#M`yMvL+u0`! S}'"5Ӝd'p$  LKy/GМd'8`!wR CsK0naXOjEskV64'OXDs+ {hFӜ'LKj9NGs8XiNm3&@ =HMV4''=iNd'5:Ӝ9XSМ?21&D4'=#5f9~Nm~QOoo_ߚr %;? FsGyxnwlӜ`6~eڹPs9%9r`KiN~1|9[Lm@!nU9aj4'̰VmY4'Vm4'Vm4j6` nU00ڪMAsp[m&T98ޘjќVm@UV[ iN"[m&t98a[m&98ϗfTЍlϔ4'c yP85@M#kj4'Q_^"4'*Vה'9دpm^S0`&y01 km` o54'?6?'Isɔ'CsԵyMy@?_|-_[w3`۶9m As,oڼ`[8Xڼŗ<@`Ij7 !\$r`/sNw&!g.oL{hN٩ͣyGq`^(ɻ М3CgN}`.h:DsL6C)ONsLAm<hN4'@[<"М IVXhEO4'@e Fs'SO :4'@m~e^hN'sӜ0+ PX?ќ5 r-0 0sМ ~; 0ciy/ p:S'iN^" p \Pt91..Ӝ1SLsN& p42 pI :4'U @5 `, ,kyp0\OsaNbΤ9XZ&<8xdR4 p%9 p9~fJ' ɪ'ҜYqќYeg>Nslf3-Hy ,Om'ќ,a'Ҝ7YiN`=jp]?МJLgh4' a>dܤ9L gQ|9MAyAs| 4'0'M(Cy,NssQPm9Y@y`AOmB7>М@s6B[`hKm'4'А%*L`Vx<9>&3ќ@f 0 g k 5 6'@S(Ž cE } Tb3-0 ЂjPSo PF3^<ҜPƛ<FsM Fyԡ9Y p@s'N0b3-0EsyjӜ@P8b7<8&ЇҜ݀ܽB4'pM9 p8 AmQҜk,̀I9x<^9L ,s6hN`' ,`/ S@52gKEhNhHmcO`8 NvR@PF|ӜЁ/p IsByƛdx @sBaj) QP_Xv4'<@p@1ƞЅ愻&&>8 ?pV<, o@Oќ5uhN"8`.ƞ0m&LN|(9+<|o'Fs* Spzl>9Y& eM1ƞp 'iNf6(Oxd^ %> Io]4'Q)=NtМBmC)OIsҟJ'\Ӝ4g !x9iKm{8IC6 O֤9xLyIjd&'0#4'M`YiN sV|4'Uo 3dz&_4'L p/Iƛ9)@m!ќ&8eI}q&A'eiNFpV@ 7'uhNN6N<@sr 0d I(/0$YA{rG6P=>ެҜ${XdL@ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es?5wN-^IENDB`pyformex-0.8.6/pyformex/doc/html/_images/README0000644000211500021150000000213111705104257021077 0ustar benebene00000000000000## ## ## The files in this directory are part of the pyFormex project. ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: https://savannah.nongnu.org/projects/pyformex/ ## Copyright (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## pyformex-0.8.6/pyformex/doc/html/_images/WireStentDemot2Step10.png0000644000211500021150000001374511247035000024724 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?iIDATxzQ_1M+4 N4'@Sќx'T$8nМ]OqAiNޔ'd pX'4'R5#MiN5{BxXӨMiN-(O8JpDsE|&`GrU=xؚ7Π9LmGsƞ 8Υ9dGj @spd# Cspdej Is(ɂ'@iM!4'/hN2 04Ү/PW'()O*2Ds">)AmL9R$8fӜ JmԠ9Jy2(Cs0 Mb4'(ON&8ќg-GM4'U{" P&@yl'&4') a ЇnFsІܝhHsЏ QmiNRiNzvej? jLV9XTY&B4' R]MhNem'~'X`qʳMuiNvaVX`/ƞU `)Ɍ79ؚahj`3e-iN AƛҜ<؛ zw ?M/2rhNx|G);A|k 6/˪OxSp'Trz&_hNxu^m]z3@c;wv}v6x+Og0 إ<7x3\jiN8ji3-ҜHyop68LsfZN9`i y4'V<&gӜ0G 3(OMb4'7<&aZ60ZF&hN(Uj4'ufyL ;Zjy4'JyMfӜɣ6As@?wAA9o6Es@o~ћT9;37c4'堠<@s@C?K{yp o`-mS` sI:K{?[i3ϔ'ќp{'g+<(56d /Hmsc.oo^."Tۻ5gtlQ}hNM%ywv9;zh44'|fJsgg j  fI>ehNv%k,@s)ӊ 5 NK[>:4'{,K19XLY &RhN:P"BsPPT.(4'Ub\\hN B@sP`8@ r-@`6Ity&*Υ9\gќe-OV4'Xӓ+ @5;^9HNg-x Cm. 8 &p`*[m4'*Q0 Lm%u9b, <4 6V'9N6ܽ p5?Ot0 Ey'Ҝr -.?gќS'px[m#4' 6)O5Gj[=hNc hNH1OhNHQrQlOs©&Ls<=iN8`xd7^"5WN9Ij8d#59[hNÁ0O֦9M` ɪ4'|&5Id'KҜ P,Fs= l!V9ٕÁ ChN肮&@ :<6ќنМ)y 9k3 9`ʨ2MhN@mKs`狼p-hNЎX)&@74'LByU\~]Ӝ0-@4'LkT#М0 s `Z0IMa,DsBN`i?Zp_].﯈~ނߝv]_~; 0E}yv4?Msg69giUj`Fͥj`^xnx>' rsm^RǜJdi`^TќV˳l/G $ j`axŧ,Rm,Os|>,jR4'P_pMiN2/m珵'4'P㥛?`OF9c(h}֑,.WZOF\R ɜS7=֡9OTќ6[ݏ=g`2)!h`J?Zб_J)'ܷLK7E1QOAiNNyW Rzw=F9`y].n$>:9`a#=;Ҝi72XONhNX,˴ٹhNs2o>c Њ,\OiNb˴ٹ8yo~ MsN Ds\,`: 1ެD|Ԣ9` j3C|9`|3O|Fs'.^AO-4' x' 6;&>>Ҝ0˴7F`9& 9ojs X^YV9Kƛ"4'tFm.c||`"z"8&>)iN9'>Cs@Sj ?iNhGp* Bs@ jJ'9 dOCN69;МpI @[&}П4'$ݟK7,ќc'9 @m2& T96'Pz&ҟ4'T"8YvќpdUjҜpRМ* YhNx'LsNjП^rsߟo 9aB~lCKWT?hN򄚜CX0+s´ϐ}\ hNC'9a vSP愅(OJp@%<+jҜ( P愥)OCm@'9?'+R9ʓNӜ'3w8iN)ɜD?ZЯ s p.sN{ҙ1930o_hDs;-QД9ׇ$ F ɧ V9藑l4'0*-;_  L@9I(̝~>:ns>>'9'0ϳy9xr9i,/f&"fDƝi\ u3-9'yWs 1͝ic9ɨMLaXWDsKV3-P9;sbsSp1꯵>ZsN <=x89'f.?6/ l $@  Wxz&Ј9'VC<\&sN獅,ќ/mvi.٭8&1sNZg Pm}ݪMc՜1|7@9jxVAM`@7R.W/{y>yVg rhVmSМYl&0z?[c pol6IiN|g6٭8ÞMZ`vlkK)R6iN}ޞW]}퍅FzN|tvE+FA 0I1o 0& PہGnڳ&cm_g Ĝ͏Ӹ\|k| MFdÛ_g Ns͏{ Hstrq='fK7wsМ{ Ds|ڼ(4']=[`kFvӜw{liNRʐyc tKs6od'' ,lڼg4]mN+X̼yc@mNXJyϞ-МjmN- Ljڼg 49d'Њ&6d861N9͝d'p& Km`86k 4'0Y=[ MsPҜ@f=[ DsO͢ < Gm6";4'kg T9>͞N 4eWl*4'ЈӜŞ-pN6$;iNjspx\9w/М@ڜҜ@mhggNseN`# fvIBs͵^bR9&سќ6h>9Mg |9Ѳќg&سn4'HmRhNMꓝ@ќ$愅Y%Ov4',Imr.{, +FK;֤9a jسiN5Zz";`5&eI:4'LGm2 М0 hd'@s&ry'LOsNl2{0+ CQKv4' - 0 }3d1.hNdUf9?h-BsB7 6 hNڄ'd'NsB;R6py' MsB j0. r x=[SlB 9!LmBUƢ9!-dN 6!]`Qp" CМpԄv@4'6z9%=- {lBd'Is6jB! _0 ќڄaٳ~hNx$5a :9!5sUhNg МdUR ;-z&,l!2&,Lv@+MbZМKjN8dFjxNv4'6N9vrW!8ddR8@v 4'z #:{9& ; Gs2 NќMmg9ZpW!Ns 4%;.I&{P5 tIv@F&= iN.59kҜ` 0 Ov4'1j=[x \\|Ms6{h}Ly9ќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9HќhNR4') @ Es9Hr/OIENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentDemot2Step12.png0000644000211500021150000004747011247035000024730 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?i IDATxrJTGw{\oE.ukʼnmRT O<T8-3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sPZ̜b3'9 @-fNj1sP4mɊng-zLw=.˧y~E=XZVPN) 4|v55oWW德h{*E)=k\`CEYF[)n\tSBMγ -[nz(]I~\ϵo[_u~ZnNu|b7{[zj!y~M}NW7@uJO?1CI6+}{o]oJuOu+Tq]Q]B̜@++.v,niv,}u ^^M̜@M۾TnαeݎխR\Jkyznt^>'ʺЫQ7XwB\mJ} m-{Fҫocܥo@R^?<۱K'۵[YŐJ7t;Xk9O5$֋5//|c`p.3)]so,aەxoy͗pJ:#qMNoKsxYۭv|{4u)}mtiJۦkfN vo[wQQ~&ҷ{pOֵ~*[v pZ IjKտTz>to,}5]g4T>-~`otK?m ʟm]Zᎌ4Hd\Ux۲-?bEy}m<^]Kk4V?gi0st3[n|[qgV4ޠWWX}$}7U?Vz[-;pB94azǪ_@^ƷU/T:yh&6^_-]B{+ 9V}B󆛔[n?i㟫>}kh[[K/okj_Vx@fNn>W_ZVy{ s=v]捧ޤt9fh4m~ycq#I.@,TxVw@&8iHymt>])W[~ tTY>7K՟U'>'PU_>Vd>XM~=߲3sv3z4@|s[; 8[oX ~ߓ1`>mn cV _X]h]գY"y36U - z-X _m󴿫zV_oyfի[G/(7@|X#W'J_z pyr8 r[m{Uomwۮ5,}|e*0OorU{CfNЛ: [yk/4'϶=<p9ަi)?'7D7-2G!~4<V_ UKx=߿3]М3$ $}2h:^='fzx}\KR/gw 7sFe}թK̜@[+fchdՅ۝Q(~1skMG-k+6?a= =ujhvjGM 7oHatŇ[\Xxl'9C6g( ?ظ؟yp8G6ؼ)2dKvf5ªV> k7\(U.#3'٪n *H{{gȶ)p5j ~\," w» /V8C̜N˫W .C81dx2^~]wB 2s^bU,dh&û =$v9Cy3d8vok?l.K)߿6Ͽ2Nj p9?2:i2!~h!~9no_e.~JZ@7cfe {,TvMHa,C<|=~*C+'+2fW,cU#V2gbOQsC ~wK;U]c<(c,O1g 1^~1*}Na21ZfCR-cʰciWH>h"CyiNz5F# Tvxy)z,SĘC\1H#~Zǘ41/D\?1bpkk:^\*91$Ť$IX+M1A9V,[ 9bl,hUqbtIb,&(gTk?F$9cL$ma1Vߊb0s7_zp+u{I._v{cKn1cW$1$yqcD;b(%@bfN`TrsËiqOC=;W9.Sl+Iy<8ɝ1"$IK] 3'jkCIXa;$m&1z%I I-2mW* ̜+VWMmNR?`I='IcJv(tEyx~N0F$1s=.)I|(cѼ|Ct!\$I%rfN`JkD-I:& #$Xw )"ߟn $ɓL1J'M0H1ɋKpv>'di:^iuM3J-$t3Ͻ/psIV'&O̜ˍ'I0D]I:ɓ$^;s&i&O̜V~XaVkq6I0%f$¼ttO|3'|_|OޜIڅYpIR9̺- SkL[&ϱ/>ZH&LOKI.)|Z&L}IބپaOg-UK}O[{z8ptV/^z8mynvoJaK{߇"$C)$U=SұWӞow^0Iv<ϴUK/Bϱ-00~a^Y*5'|+3'ڽ@5^h4S-fra>8RQs9C>QramD6_Mag8L SD9\=a^yy@) mcn:y\tGi aqd 3tnIfana ݼ pLߓ4K0yz_7a‘ZrsR1sbgvtmv˞jw~t7U|b<ѮS1ϖ0UU%b'ʃkokig~kbyV0>F@>3;Nu1Mny>&[6Ou4al Sbs9O'Ob]96/Fk.l;sϟ#i\ǠyVUHჽ{d̜> ~f$H{kLq}jyFgo">vS̿]$Ey)R4J"mE_i."稭JHmv19DVunW`H+|Qy##DZ] [3UHۏږ_´%]k^eQ7Ei2dKdETM#<" 'r|fE+RotleVE7zټ+p"}WxJ%ҚH\[ ٰD0våǕ7E-Rm7/Қopk=wn[LS0s> ח ~6ժQHQwx.$n;OVRG!R ,X^45/ݲӗiU|X'Q'xMR} v}F h ,;w6viRU4DJ wTO5F|zK^9zAu*w qe"bxHRyN/ү4;Z6ʓ*yT̓\NCa3ώz~Ձz|o-d"or9~bi_ץ4ݽRsD/6Ljɐ%|>FT2J0O'>G]"Mӿ/UL)nPkk/. :8҅| S.$bTX]/XT#o\; DĪuZOLS6$O-ׅ{xCJ'ʄRD ̜NS5]Kz$zæz~M9S\[V:owWTϑ~s*3'|]ZVrNъΊ&yC.} m%jȖ* ̜0o VkAVPVyG]MTŪRNbfNҶŗ<ږUqCqUNC~CNwU[u]j]/]8ộ9a`NS8px3؇bu9B~Cqx 8>ޗx,'z̜0JzF q5;D!ECYvW e<9ѧf  zgloY<1s˜^_yu`OĘc:V&VjȖ-Л[v[ib)`Q IDAT^ږ/Uj\V9[>Ws  Y]/mx4W[y9!7Yȭ;Dm_ݴJNlot[gKluv`[ni1DQ}̼ӀL̜ҁf=ʧvk{"qb ;vWyE%sliElcNawᒃSM VbiQf+SER(z2^-֗V>֏ݺV6 s?L^UؼBЊ6sW4ѷj^@)ۑ ]/lqfN8 ch`ۖ.qXMM6/jg۰٢D\GMvNw_ـ#̜>ț7dkyv[,zk=kW=v݆Iȶöj9h bExn|,Phyw]ls=m]?[DxSElw.nޞilJ!tUld2s<ٶI/%(K%,"}x</9ޮvu3]^H-lo_S+xv]<^+[522sy^κxFQ*ظV+ >3RO/mOi>ٖ~ g}tv:W(6շ8fKYvkZ` $l/s&o p /0izHO`tLnNA迄OOo<,%T!%2J-L06MwInF&~FeڍLCOTcʎM [*烫-.&[&n5/s6}n{[?Vݱ>mX!O% 4ڍi5w%ΐǤ  O7V@JרB \amȢbg!KǺnAB>8 j 3Žsۭ"ȣ*!dY Wж+,f(!﷗Yl \9awYC^#y /÷1subQncqf|k1HαBF✣t9 Ys{T ?s1]'ts^N!K4d1HBFޜÅ?UT;;| 9ad7g)k7sFO_[4t?4D&(!}])"|șox~%ywOHJ%El!Kƴt V#]Τ!7;3c0!cC]!/.p_t9vf$Ιzgٙ9a("(87b!~뜉B9ӭ_\;3ۙ3c-g9}|3' byD:gu9!'ʙbݼbOߙ+Bؙ!g1Nxw"3sBzN9,O:o9;GB~:,k̜ہq$gY{uv!燒[Y43E}Yᜫo |f愬J8-Vr5JJ{kȹ\DY]&ybj7ܻGz}?V_#K,;p؁6ڥ,CTsQ%/|fy6Y`ro@jQ)y]@.Rϻh:Qy(9D0sBȭ2>GMb;@\C> QsD ' <ů\^FM3DCJF(Q8Q6eT Lq9ANݚ.q|_2g uD=fq6)qѢ5-jNp?D3D9cqf&jiMqvUy 5sڈi;ѿsPz)j;]VE1L3_/7ɛvبӊZMѠiWGzD9/x.|Q/\/d;6R?j$O"O?j 6GܱKi21sB +V+Y&ϓ?o\Q#aQV}C2yccՙ)QZzn17tځȑ(+~ǞiYn׋^[ڵ6SD]] ٘9aW_Sqn8t6玍nF{H?PQncCuJ `u7VW.m5i3 K6ZZ϶H[nplk.#ʴJ<M1Җv'm y H[ dVfNXaJC]K[O[r9V򛫼yim~N =weoIO/5VڣEZ]W>msB+i::Ki4̜S:bωwvz.{t:ٽF獱F == O̜eb́J6m[޽ۖ[ưwmŻ=Y\vO{ahfNݩهm^^+Xi#Qqvۯֲ%NOo{Ť ),p oB%q/|IXi 8Ż;pַ"g >EQaDfNot~cwS}A8q$r4 yJ&{xT&0 ?9昮'g5oā_rMS!ߦGX"qy/2pfN6y9'' 9WS>s1W!pmDe9N9R/s,r5OSfSN9seov}2 MLcN9=bf8+3',[~3Yjyu;s)v>E$c؏O#d1c?1@f83'tw&ad3N9c\b9ƌ=ιΈ&'9z~n稟WtG=Jx=$s"J=cMia.iؿoFI'`愗bqcLs{i.vf'o9$bWu=br{柘~~ e0r9b/d,v !1T]7ļxc_oNb;FIpܥ>.vdN~O.lE쥟vؑ6XIcLJޠ#sr83'b ܷ ;&?AObGb{l%2ܤAcGpfN˲jY7|kor`xL΁Qy3M84ZM]5Vk~QKV7%TbЗy豐]rO|c(_|;4yqnax |3'픑`ڼڼ@=]}Q)cf"%xsT :q{n] . pkn.߮[ަwCH9wdów?t3㙆ވʝbݾ*W))$\3'pI<9<7c/7y uTks_d1y$ U9H$z;}'^E2nUP{ǞP^!)"KӇJ;uf~"6Lqsb4PKiz ?q_z4|E)n_>*op7/lN1w1]sLSܽ5q_q ř9ᰧhjgo| @S=`?w|z#lx0['gOV 'pϰeG^7ɯYF U^%| CB̜˺SGޱr)rJ7kOO2Y釙,=> ΋׉.B̜]U@Ecmqç>?MǻW"C_S1sj{tYܕ?0t%1%ze-*}Ǟ./|_H1s:ǗΓKb 7ʟh /Ώ=؜Lܴw>fOʍ,}BLnC/Wx=gwg w3 3'Vat=tÇ/S^mQ``̕ zb1x3 5Zg猡W |:B-]\_h3oSKx)1xi>'ò|g.:0z>Vo¸,U9à̜ m 7%ɿZV0>fw5V~{f^-: w8T\e/PG OfNx>h 9u(z=u]C9ǑGAP$g v>nY0?o!Y A2參 G= SXgcǣ YHIl{OԾ c揕} _t2M-c/?$d]i[ZHR1sr*s0.>ĻF+[!ʣ0h A+'oaY> ˆ7IrqFgmaqʣ0Tx5 9[*?tgNC WZ௅1rm!F~.Z-|<<1sFwg˹aERp1\ka9B \ZyyB m _hhN€w$ ?\i!3udk .R=h.~Z澅!8A Мkk9''gV'h!B7Z~b|8o 1PO-pbӄCtq3'2ͽz ZӴCtz4́?糢zww ^d^24(U l~̜|'4_pFtqs42tѿstgi=*9^p̜|O9[hL]YTpP48%zK4)z7o9j{_F9vL6]m^ndCYkdO|] !GvmrFfI3Xd}x@[}>$6V43-w^9G#k0iblH{եduI#|N<0s‹&/O:Wޝ:GkdkHsGdmjY8N<2sB38g1{Y8jVҷ0AFn)w/̜y;Y,ZE#,~޽1N;ˑ<('ozwrFrc 4v/'kdT\{е2Fbb,42Fw<"ii/ g:9;M#7oy{ԏ4\zqȈ.Dk'bD<>RD |PN_|ۗo9~战oo_i3iJrFƾw2zF^_#yjgvok#%o_l| w ы8AC6 qH<2|#}oqw}P⯗l߿q^Fmob+}:)4_`q[~zE'YN

@P=x:N HJi +zNi).Iphl;G7QFҴ9isi5hg6Q)ۉqNNZXkj,>G/qv`Gځ"̜t5٢3[VtvbkIWfGvj:q;)vQv 3'}l~RMb:e;MIo'Nډfvc֎ҷ9fSk/(d ŪoUۉ7aǹŚ}?f;*vxFh't4T;>v4Z;(w4`;Y,ۉk:1Ig/1]G:(;vj'NCG wi{ ۉ (:bſbbĉ;:ێeHQ~׎8IG͗`ʃ:8eG?# ډ"N1p>t4K;or﷣,MSܟQGh)"渻 :7̜tu}~; N'~9͟yrʎ.lL5'h;F1zGv.㦣q~Pe~ѸM+>I'KS:JG7Qԉ;z~֎~:wu6u-3'ͽ֖!a:9|ňM(uy>(>߱k:̜4;B[}eG1VS:ij-:mwƎ.dg椉ufc~wG1DS::]G1VS[:Z=lut1@S_Q `椦Oo;0ǐc`-3ނ3Sp}Rqq"s¥7?xm.1Շ3 X Sχ%U N8!@>dokj5tbNIf0` dh8E꟪ }`sT;Y`sA/j Xc L+m݁`ψnL!3U׉mF5Wfֵ;R N87kNuT{lƫ WhbD.ǪmdwbZ2WH8j5|yyzF4O r×LUfvT52g!{$BlٟxTߨ`?4:qU6R8 ONe i8ד:GIJ[|ЉnTi7З[z{4oNփ%2SuiT*9C~ T[H(3Tac'`ܾ#r8.v+HUcv+Y6dLUnN c ,?Oȷ*ՊbI3TYJd'Xs?9#LZ'U4Td>*El'>'`KPT=K s:TML2S> ӆKU_wr_vHg"2SqyI^NВ*N'o˝o he(4뛰oL&UJTef0smЧ MURIդ.Xx*]29 I,Jۦ[c l-\ێK};m-]u%Ր \V-5'\ѻ\F]ݱk5W`}vVtTs.7>:Z@7U1riw0:LmELXK>,5sܠS:.>'1^`Rm(".\R -{c2+3W.N>'O8U XsIu|>jlMAQ745'W<*/Ui ĖJ2;qF`cBpnB+1u[30DX'x_5|I7|ID 5'^_6ySnTECG:IfWz×NXs;4{{gn&QfG*:Qf~D"#ÚȇyO+cS͎TtbTWTcqHc Ge*j/Nj]f* 5'?v$3UIGdI*TۭKZ* V VG|n5%jT屹Hv &UN8LՉ5z9|NG|A-e2KJ_=+Xl*ϐ /gkХ2S==җ2K&wz![2\K|>PJ ]Etp@J9k:k4ۦ0LudA֜pQ{9}`nbdtHuY̶*eRs6e֜p]{>TE[mWTLU>>d6וR 69 :5uw%͵6ɝ\CJN\C୵K5J`&kNq573p< l.Ý%j'xR|Rtb R?R([x-xpl'V12KzOʀ|(?ÇTxuJ< hWu+;nc/:qEUu qj 'gjX7:7u"sN'd f RU%ۢS` oTab+9U g ,vMT2IU/z1pXpRnq\T->m5oIf0+T%85']û֫T+~ B&ϤRކ5'cqKRTBz*N$+gTn98H\fg!2S-lTBH(3ճ2SGguKⅺ'O^>x!tbgR;q _"kzNxi^jI\i{_tok(?(2SDZ R&~\WTM7 &U*j23T@9|S`R*jR,s™ţxLyR.IqjBhT_P%Ys™sҩ#7 JU~ V̧T|>- 愓ɜLGj+5Wx:Zo\7k}N8wcRug S5Wro״IkloŚ*v85ԳqjEfsejGAs55𞜭j>6jo &U^R+RT:1o\+0D]sЇUJU_ጩzJ!=k'NjƬUI7|ID }Ń_zɩ<{l.jLJ}oykӫ|6/UMSn=ՉM2S-jvWQ9,NG>D͓:Nrfz!6M'6L$ElّʯTZxzrV-`SwWftbTWTSŌ;@ kNxQGTO1TtbTO*qHl-ӶqQfT%nT n[`͕*N>'{RnsWi~L\9X)*xTA֜F8{<)vaLV=`'ۃRN|x vT9[ 鴈jR5 &Ug!L,DfuT?˚B͊%ON7C,7ʴz":N|Ywf_l->Uf?(ΙjS|`*X{cNaͧMUW:mseH$3T | gV7~|T%5T5uTM2IUei` 9?UI &w}If0o\ZvŚΪ}3S12gork`R5 &U`0 '+2}]T.XR+r͕j_kKjܬ9TkϜ.KT{kQ@B/r/\o2SrN(p=r*sþT:qT%5T2Sa.)ThnPJ)9vmIy#vDǔrtyG*uGl.aN)Gw+pi!z`-OT{ Lu}JjŝlSjfc愫ݶ9{~zlQ<>9R|N,}S5?Ӛ+9Ֆ?^ٳl] a CJxjzk[$wp:jTJo״{ pN֜@)M!᳐s7W۟ى[ej*3U_o'U'e@!R_;Z6vR=Vd*C;!jgt[kF'>UsYg _-}ymxqFsevzÛk/:qe1yki(U'eᜭ~XgL5;ҫS,;YseZ]_R[9Cɚ[C2-nu$sf1qev,#<)[8OƝ_|{XsO,R%&Xzř̔ىó%*y|x{2-8R'U/J%KN)#UnTiV>"bSb|>sωۭHU/s&U`RUʽLUJN5p2հL!ݬ9"O,> &Ѓ}N|S`R}׷fAf0T OߠkxL+M2I$ԦT&> w\aEK5Ҿ_2S(=3վK;\WLtc Wue]T U+3r.\vjGev^fBv6b:^r[b'׉pa9Lyb8fjCqjh}62+3W.N>'O'[՘>c_S:'MV L5R:RrŚ筠kw×N2Yskjj:u[F=ՉM2S-jvWQ9,NG>D͓:Nrfz!6M'6LUJ1~˯TȚ?yR,9UՏSwT tbT1Tke)A;M|k.1/x!6Փ~ U=$3U^s }Nx+ϛigLUH$3T n[`R'd[?~&WA~LUn:Q&w_ ΐ*(Ú ;>YjLzAN /kNxCߩIyޜХ2S==җ2K&wz;sEك4?ܠRszy\J9S}Se6TC0˻Е}N=w0'[fg:>J5e6)531ECYsn[ל=?=(yn) i{'Չi͕jK{YT.G!C~[%gm\[.",QRUxkmTksJiU tNܲ-3U[Uz;e>)z!؏R; ZA}\+2Sؐj`s53:q]5I_O3Yg _-}ymxqFsevzÛk/:qe1yki(U'eᜭ~XgL5;ҫS,;YseZ]_R[9C>'C~4=7Ӥq.U+x9ax(4gjMf'z< ~2;wd49R=pJ7;[QS2'"JjTdf LKfT_N{pl-ܽ$g0֤mC}N'2S>5\ʯ[vxԭ'vVJJVmR5 &UgR݉Q}9{y|s%,U VL,TM2I= V";ǚQ`Rx&Պ?Rz%T)b;{kzYӉMj'Df 85'imx6XS-3D&/g<͕ 8@NRrVtb=~[D7|T@+@)auIU_߄}`R5R*3I`hχ>u&UhL&uSZI` yLT%5Tn`V6]N85gkvX{ikR{Wl.TpLl᤬9v޽}J5E_S}m'KkwɝqDNIUq`Rm+z/e6WfǢw\` gd'׉pa9Lyb8fjCqjh}\Jt1pz9dxRaRw}\R *XfseUN'Yx>ySZ^\`R5 &UuN5'S[(ʹSToqkULz@qNiqǜ6Uju}Kb\2S^] kN8ݯt8l LuЭkwJk5;:Ĝ1h:o6TCLXK_l.dElsEك4?ܠRszy+lJ9S}Se6TC0˻Е}N=w0'[fg:>J5e6)531ECYsn[ל=?=(yn) i{'Չi͕jK{YT.G!C~[%gm\[.",QRUxkmTksJiU tNܲ-3U[Uz;e>)z!؏ޭ?vRzEZse*C;!LPOuN<}b f >kS w/76WIMU[g6WfL&j֜!yTe(ON)9@Ɛ{^j's:ML(U'·Xs} om/EzH3=GsesUq85'ǷeWWO*3ˤ`R5 &U>"bSĘ`~ϧͷ9qeۤjLj[`Jo-x||NJ}Z*6XZ|,n%k*>==9󰴓jÃT"U $6U3 :Z৅ɳR=YT ?+3ճR-L,GDT9/GrR=x3f$T`N]Z޲ރ}aZ~sR-7W^Z'J<lTqsɣ:'U}} 2IդJ &Ѓ}Nm<Թ:TJj0 Ok]&Ѓ5'\^Ac0R`RջݚeZi_tw}k,;ԜkqbaK\R+3  =z+ը;} L/Վ֊j&wbC[ Z#f];&U^2Iї\qi烥&}NX#tc_'…39Ջ^L PKEyoLfse*u焓9Iq{Kj RsI5c͕jWv8sMW0>(jKVщMvj f }a`R]po^S`Cn-UO:ɖjkN8/9'3TM 98\b7H5ʜ'`R*M2SRIf'^>U[= BpnB+1u[30DX'xVt× 'UUae.Iӫ/}ټT55O-VjW'6LX`ّ^EDw85OZ8ɩ~GyTG4$3ՓfG*Re;@ gk>sY[rgOݞ]fӉM2S_SN39McFfoR=RӉM2S_ <^s VMJęG2S?R5n^e6WfN 8!VVJ{ۛ_f!3wstb~KPlvRbXsyY؅OV0Zݿ|"=lJUR;a5(IRl-?"I$3T,3ճ~6yR-.kNx `7+fZ>9ճ (Ӛe';eݙ~ \p~cWloՉ{=J';a2焓x'Yx>yS^no\`R5 &UuN5'Sr ^Gs*Ii*#HU-3T7.-;l-Jݏ9m 3Վ~T;*bseQRx֜p*_q=zuEi[kGkEG5rjwjwLuD9[ 'cuil".͕ї\R5 &U#֋•vXR}N7*V]#aE^6χ9]^f# F愋{k#p#swCΪeUI &U%.,;[ *3U,3UV'gŕR 6̚.m㤤SsT iW'*:مS|!U'e@W֜puܒ\wh;ڪ?7|eZ̙j|s>U_oTT e S5sDvaxsev)S51n2ڪgRR>g |[' +yŚk ]$wRUoLUǓ^3)X֜+yL5;ҫS,;YseZ]_R|Ț6KƐt_LfG*'Z t.'R`s=hx'e?gq{v0֜?!;Tk~d jқ)/gKNUoO~/rKR'U [ּ-9wT%2SuW[ M֊ gXV>'n"UyTM2IUv+,3U)Y;!s<>`FM0jJXubVVP_߭Ӛ ÚXtK;V<<8JU)RЉMbSe>'ݜ~Z̟'PJixtn:b6`Ru\-3Tۊ}K͕}>Xjp&zw8STKP^f0Fҡk4;: d9z?h>:A'…Ys™. a:"U`RLULUtb N65v-Ո+8Rs%R|୵ŏ\/އ5'R}'UkU}v&SUxkm`r9b-8]j;lTmUIy÷U\m 3焳jv!TJSɪT/ԉR\R|ZKj's¹U3:smT=.լ+%Zy jy7|ID 5'^6SnTs\$3bfGz! ŚtC'a3e*qjpʽlT(pB9|nj&lv2S}7WN'}ؒ \%7U 3 İ70ǹ߳I `RD{8vkPZxNEWIf0= 1?Xfg!2S-lZ1p]֜2ToZ>9ճ (Ӛe';eݙ~ \p~cWloՉ{=J';a2焓x'Yx>yS^no\`R5 &UuN5'VTe<)0N'U`Rk߸5'U%e*^f* 96 6Q;Xf[H$3Tv,8mu!gIsGQrwڥ2S.W'-Kt)TegeNe9v\: tN-3UI &U`9ᢎyThnPJ)9v=r? PԽ*Zχ9]e>s¥m߇蹃>2S-83)Uw.;O)ʚvۺeF̻dwKgH;M\NLkT[|{eϲw105'>*# l-l܉[iQ* _F5Xp9Ys4ڪB\mWf'nY ϖTT2=xPJ[_փTroNlH5n.֚щjXp™Ys3Mzu6x<82;q=XU͵~×iμʴNF2p?,쳇UNjwݝ2S/ d !C{w9NL2;XCSeti-t'/>T=9'~ ICw,=w?fS`~ <={U{)%d7WfNjA~Z1s >9JV9oIf0n^e*%k8}N{jg&R\ K!Ul RXJ +UPs}{Zs{Xs >0׾~T>Ig gh.Vd>'}~Z̟'N :1ͯs™SJ"&ՆR(%"'2S}:bs潥J5n_TpͱL`;py֜p> T#-+5Wr-l+uo)>aeS>{rY{MJFD2SUxkm`r9b-8]j;lTmUIy÷U\m 3j{!TJSɪToOщR\R|ZKj's¹U3:smT=.լ+%Zy jy7|ID 5'^6SnTs\$3bfGz! ŚtC'a3e*qjpʽlT(pB9|nSnvK \|7`$*}ؒ \%7U Ds7)2+;UDӲw9K &U=dIR>@kNx f;XfLzVZىIUyCo'㙩,ΙjᗝlL2;*~\}NxgϛgZ͜l X/v;^NubJKUS焓x'Yx>ySnTtb=d^:n'ΩeRʹSToqkULz@qNiqǜ6Uju}Kb\2S^] kN8ݯt8l LuЭkwJk5;:Ĝ1h:o6TCLXK_l.dElsEك4?ܠRszy+lJ9S}Se6TC0˻Е}N=w0'[fg:>J5e6)531ECYsn[ל=?=(yn) i{'Չi͕jK{YT.G!C~[%gm\[.",QRUxkmTksJiU tNܲ-3U[Uz;e>)z!؏R; ZA}\+2Sؐj`s53:q]5I_O3Yg _-}ymxqFsevzÛk/:qe1yki(U'eᜭ~XgL5;ҫS,;YseZ]_R[9C>'}C~4FSn3*)~HkHUթwpkNߧbR_m2Α1 IUtb=?U 85'cPHUOzOdLsO\12;m#v}N`pL(Lu{ &U`R,3UyĒ >'sy+η[Vmtb KTqe oǚ߳;}֫T+"K9AsI.-o05&?!Z\tD?92S= :Irc%mYTk]x.qNlrNPw㌗/3p>2S}L}V'6lTn\Xs!T"Td*jLo:If*P֜@3u0BS`R5|&՘J'~׺L*kNCM`Rť*w5˼ƝԉM2Sx\ێoҥ] \R ͕jk 'e {5+WQWw,Zk;i_\MĴχЍpUG&vLzej[ѣ/})2S=ҴKM8#8F67T4N gr̫;13TJ= 4TCޘLUꤋӳ 's>*ո|jP=72+3ծpYs3'pRw0\ɩR;Қj χÚNimek5*JNVI୵-ozk愳L:wjl22SU%U Va`s)μCΪ-`RE*M&R}=E'F.K}se*OU7|ID }N81}^TuNTjԹǥub%,'\};{-3ނ3Sp}Rqq"s¥m.jK3 N⪩6R5愫ۧO4mt|zΐwbT%)}i d N9Ր.5Dl'7h!M Ě(it؇ huR5wGk;:De>z!>c,V;i"iԌ#%2SёW['5':j҇b偩 evR5Wf_"U~X<4{O~x\_R}\'KU"=J'֐!ٟ~9'~1C "NW,^aK$3+9Us k$͕6kwc ;oI傍8lT Z] P[`s559b Tjk{d*UsKO5vִl _R;HBpV /u;*5U 6Etb24Z3RU]>azNs} >_JRW;oTS:Ir]ud y*T?e*/e*3:Ilfk.L֜pzfo1~OB6WMU; 19< qyRs'+T{>N*$rşiդjLzBYT ?< \5'́?0׾Bg%ON7C,7ʴz":N|Ywf_l->Uf?}uokx9Ş|҉NL09*&Ox|TuTuUfH$3T | gV7~|Tedۛd6Wf28TM2IUei` Բ\)QܩyRZ*RU &UKN8[ RcN*L㺾_1Վ \vTpzT.ށ5'W:vwOG^]QtZs:QZQ͵k]SQpb {s7|*Ksez,z/e6TM2IUCpe9NN9#N:uh<*6X;0Uf*9v;U,3"ub깵H l|4|kxt xMؽK^%R>'.pZ|7c*FuUFzIl0%@)M!᳐s7W۟ىmʖTT60q'VBVZbѥj3;q=NL^ WۉtNF p1նlk_5׼3ɝjSÛk/ٝLDj޶ 8jvWf;YuwL򻾤Wul|̚龌!˘TW[͎TNL5\_Onz2;4O:CXsO,R%&XzS\L|1<;Xr 9'>x/!~/RUTRTߑ2RL։_mu/#"6Տ[+&}N`3[ ݊T2mR5 &UۭK\TdT ~/^ ÄRu+a= DK[ZA} j~rOk.`kN`w𰴓jg0L %ՊgRv"O g Z'Ttb̑fLxr9/-^z!tb,?2+3pgkOkcN/IrTs&R5YE'Xs 2>8xeUq/щ-aq^f*5'PJi84z &wMiJj0T Oߠ13no &w}kN2I$TcAtbTѼC.o+aSRuQf0Z+hí Dk7ww-ՐK~jTkT;J3 ;b2.T*Bj2Z X(q_h pUvf0dվT]\>\ c93A:u"\}N8c^?L PKElT_N8=p2<)yoR[Wj.ss,2S*'\5'Ϯ;s'Ո+ko.qJ͕jKo-o#ۊ/F[|~>9ᔶ޶\ֹ^Ӭ#ލY_OZs%j$r7|[=͵ YsY[&pR׻V5v6\ڪ*o0xg!gC0"S`gU>ޞ#&Z6vT {×Nsg uJU_礩JF{\Y'VJNXbnډ@&kN8؁}m UMSn+ݨT:IfS͎*B@6 pzϏ98p+LU^]%/U<ՉM2S=<&暽*`27czӏTM]R-ЉM2S,R;@8Z\䇤1-v$HS~jw'NT2SH@>~}ܳFc],>"պTۏ{>T :t' 8'kNx#ؿzY9ʿ4%U=+BVډvYN*%Ěρ?ÉyW[l.V'g_T 1V !B_ܕ\<ʩR pQ֜p~9S-u'7Wf_vE5WfuĦ3SUgk^_6?=\&X_iTE'8g2"3{'V`>ySSzR5 &UEN5'Sʘ`Nl!U`R۴ki kN8́R2SB&㘬e'p65GQrwڥ2S.W'-Kt)TegeNe9v\: tN-3UI &U`9ᢎyThnPJ)9v=rv;cJ9S}Se6TC0˻Е}N=w0'[fg:>J5e6)531ECYsn[ל=?=(yn) i{'Չi͕jK{YT.G!C~[%ޚzD=͵-;qb8m5*U[%kHս N8'kNq|_[YȹO-2SU^2J!+zۇN!R l~F'ft:)pf֜?!`^]x 5YSU5\7|։˴NF2p֜ΣRy3R+%պgR\<|GLjџsL3&ɩʏT%RYs?-z"3BlT z./k.sU8;gk/GI?&]\D-wS`sTA9Oϓ kz^o欣ʫ&Ϥ2S=Vsn\XsL`R}הI3Ӧ*z13no &w}kN2I$S`Rˋ}ECf0d/Δ,bT@[ ׶c;[ZT^K!\2Sm*Nʚ.lkWTX+0׆T; X+:v܉io-gk᪎Mt9;Tz8 &նG_Rfsez,zǥipF9pblnЩi~fvR:yt06z@h.?͕IgN'}6-Uqw+J%ՠ{ne6Wf]˳5|gNqeme?`\Sm)-wd[5ը~K/އ5'ۖ: UkU2%͕[k[$m6ז 7d g;oIu_R[dfsejJjSyU[ LU MVx{N\LUF/coډ@2pnUcN4u[60ըsK5JR5Yy _ d]/U=*}EJ!Tھ l+7"պTۏ{>T :+f 8'kNx#ꅐS<. rRiK&{VT%?$UJ0 5'WA@P\Ry0%FJ['_ oٗi&0{B`fwe6WfrT/\}N8}T!`橰:g9gT:RwǚN.s?m9N\&X֙\NlqTŲZ8L99U͕ U`Rk![}N8|擧* Dd^"n'Ωq 3Sa2gooqk`R5 &U`H m=Ӧ 2SoWL+6Wf\+վKw` x܎֫+NkTZ; X+:v{TVsc# NZ8vcNfO?tiTE撪If0^t>'kNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNzkNŒdIDATzkNzkNzkNzkNzkNzkNzkNz3`™IENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentDemot2Step15.png0000644000211500021150000003055511247035000024727 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?i IDATxrJ$P{_yLQ"Y*bbi+3_՛}.u? @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRTø\zk - .Wgkop_[`m7'=^>Uocݾz ߌS`3Nn?B˻?,Rp˿~.]}Ѯo7ߌS`3N!ù{kaÿ/=y}`mfmGg?~1[ǜ}x;7ߌ?~89ڽpw o)Z_Wo.oHoHܸ9ck|p_om3;7'^KeY\"MwdMw}?&~,˲\n^]7e]7}?aX:ޟWofw N v^?^fw o)8~8?[ {}>/ofw 9at|k???z?6j}nM%>^`m_[`817'lE_[`mNʟjny]]k ->'0Ə4\`m_[``Y焾I?._<777C񳵰ϟߌS`3N񳵰ӷw90 ofw or?xw ofw ol?6{6ޥ7x;7ߌS`30ܜ0U]?{W_om?!ۼ΂poHpf zVpoHpr k}vp_omIpb |]n_*_om?-XܜǙkޥp_om7'tL4\6xk(ssB˄ݻ4 om_oxޚG _omٟp>Gxk6U3O"?^xk6xW_ -ߏ _omo;e"M-_omۿ9x7-i(_omۿ?}N=$?6i om_oP>^`3Nx;7c?viH?xk6x8)7'lE$tՃ.)_ t߫p_[`m817'lgnJ'\`m_[`877'Ek^]k -9a//6xk6X7'2"u? }_[`m˲9oGWi'k -9iܼ|fHOk -=7'7EJJ_[`mo+ssr!?|.k -Ui Oܜs_om+};FGn \`m_[`;(\`m_[`uޛY{4p_[`m ܜ4]zM{_[`m,9g]jGp_[`mC_nNXaO_omܜwigp_[`ms 7'l]y_[`m,ss[ޥѶg/\`m_[`877'l]}w_o#?=ܜ0_= _[`m_]&7'l,t=|.)쇳rs.oޥYg/\`3Nx;7f?zzG/px;7ߌSpp>nN)_om_om<.k -}nNs.Gw o)oLܜ0\e.Y.o6o;~87' kO~\.ˏO[)ofI_om_~1pw o)p6nN[0]zUS`3Nx;7㏴N {~nK w o)of199aփw3. ?o#?i9Hqs6Yov o#?o#w?6Xx6xk'愵=x7ޥxk6xgnNXeσw][6xkN<9qsBYo6xknܜ14ޥxk6xwnNhݼ|6xkB\w o)p*nNu].˲\'ޒ`mfmG1 ſ ߌS`3Nx;g9Rk -9a+6xktܜӋ4^fw o)8~87'#YOX7ߌS`3Na)9a܌J oHoHY9a.k -MnNب)ݯҺxWoH;;a7'lEU^-k -9am/ׯ]*m_[`m?N y*퍏_[`m?ps*_om_om< =/R ^k -/7'4Mݫ4p_[`m; nNxoQkk -mwnNh.|f>{k -ܜ3]jGsp_[`m ܜػ}Fp_[`m,nNXkO_om vK? -k V-6K[ߣ^k -pnnNh= -k 9a{ oHoHg]6ί6xk6N {|z7ߌS`3N19awi{ ߌS`3Nx;G'愙Qk -9agGw o)oLܜ0z]ѷg/V`3Nx;7x?&,eY˥+%Zo6o$ܜ0'?i>iw o)ofy9aȷ/.=ߌS`3NCSqs~/ w ofw ol?vzzyIDATߌS`3NrsowYxsrsff]ZQx_N 1w)_om_ 7'lCֻҖp_omܜfӻ om_ox897'M_[`mWٹ9ahgxk6x'˲\>ԁ[2{ݫ6 om_ox -縩xk6x7ܜڴW_^p_omkgnNx-x_LA8xk6U5HS _omo~psB˴W݋4 om_ox:&Ji om_oP^`m_[`hsBI/k -O ~E~G*\`m_[`;?[ o>| |*.k -C9gD6xk^}Nx_I.r -k _U<}NL5\`m_[`/焞y|f~.k -Osj{_Ǭp_[`ms ܜ{~'\`m_[`kܜі7fG۞p_[`m yNz|c˓k -'9aiZh팏oHoH愽pz{/)9a3=z.ofw N 3\.˲\Msc}q'w o)ol-Lpz{^kﰿN 3|\e.kF*>c#+o߭nF99a:]\pߌ ]pߌ ?~8=?/ɱc'f쇳}Nة' w o)of1 >'lcί$~G$rskq6xk'Uv_ʁp_om ]CssĤp_om;;7'5{N._omۿ-[ =ӞǠ@A8xk6 ?W<~(_omۿ3x愖eU._om?<92"xk6x'ܜ>6͚/^6xkfz w o)ofZ|>B~^>eY<)Nx;7ߌ?~897'lv]^wp˼fmvpZ~xQ9~ǔx7)of#sw:?G:@|~G ~G psB_9z񫚿p_|W*;_e|ͯܜWtt{|k -wnNxci^MwO -k -{kgG}yi+$~G>'4mzNnzc<7ߌS`3NA~sBӻ鱺|,WO1Z>jRQQQ=J_[`mpvnNRz,7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs @7')nNRܜ9Hqs$X_IENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentDemot2Step16.png0000644000211500021150000007610311247035000024727 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?i IDATx*Ƽw![F,-3U6 ,Nc:ovE 腘 1'bN@/Ĝ^9sz!B 腘 1'bN@/Ĝ^9sz!B 腘 1'bN@/Ĝ^9sz!B 腘 1'bN@/Ĝ^9sz!B 腘 1'bNicf7z"("X As@-bNB& f7`)d'yNlP þ]D]Z+{\0ém@ pA|ZVC $xg*  x\yhI `5Ĝ""|YT>8ʈ9rj3<4F$w-&G03ށˈ}94:FXѴM4Eo0`־MMFZ1"b^bU 2#̓f0"P9ORw'&A$1'EGd// F ~g2k$L,=Psq< ]GH Ո9oތD1'氙98+ΘPjHWnUfv=#Sh[2u;fMz CXqSpHu̙٪͖$siÓw;ӚfH~ yN07WӼ>1݃@TBa<'Z{ϲuv}01'ȘLmwi(Z0Jjwe@|Y~Y $Z[`9@{HΥ=4IsΜS݃0ihnoT[f|fX0Ќ֩xf7<<Hm?뮺$x[1(yaψyIu=R@4:Q_]+ DuĜh+b^")gxsjMƜ4 ÐE~B- Es A ݈6٨~sB^i>q  Txj+ '9@6'z|[<Q) S7f& C 1~ sk@wj~<, 8 I9-!Z憚iVɶbI@'Ĝ [9˽[%fs!.4bNIo%TcTDψ%d78WvLeh/Tl$mbNQtˉP\&%T{SWe1Xsk8$䖬9 / s[VghH,z-U9I=IW`-Ĝo9'ļV _*P+ƈ5b-X9!~(gXIS?Θo]ၘ:#w'^iz=HUc\ovh6n|iŖ':z<[Dyv{Z1'pD4ahG+O\~N*? 茹o4q}}ݍ*^M*9Iy[7Up=1{4$ovթ@!bN~ɦ@Pj*mv|^o^ Ř[ o7ԥdn9rATE= yN=|s*3u?Fhf;o^8i9x./*h9HybDE c^UMJdQg\" Cy{JydN|'] JDےK+a<ߢyj@HMJJJ GI̟1mFATuKA"Vͫ `f74Z|_?wf{J{!hu/{(G]nd1/kC^?2_,s+ZɨyOI[Rp<'Oqz2I30|<9X6?TGԾ.k)버[<yN/#wШmP 4ŢwbA :rP?M3 ӒD*'ĜFzG:W=SÍfˑ[+"oJݢrʽLGlO/VY)(wJcȆ1g՘}t,n8]U{T4s7ڇ9US ΢VѴ|u @3Ĝg`Vц=U~D?{W㏄Јejқn ?|z9Ϭ֩>U>ĜhH"{7IҦ qd2"P}jMDbj \$5bZ!y;]D npnդ噅$fTm/!sn8+ɂhƜuM'?~[_!zUՔLu%ʿ Λmj#ֈ5ӊsʍ\{w,[ 8KI%tlRnv} +O'GzU`=Ĝ0ߍ5/㎬w*e֊(hX^8zNFFHU\ Ό7^D/FOv<'ݽ`w|SO8?=-{XlQ16B6p1'ؗ[=?Ĝ/ɩ ڒ3G7ER^茹tpĈy}"{ Xdxh͈9yur8uMQJkJ2` kmxy,O谽fOzEyޭ <1'ޒ|9'Zvd _şDs"m`>ǽ_|<sk)Of?1!ғaՀxjk u(ϛ;EsЇ33]ϙ([4vvdLݘ!AiQ*m7| $8ĜN#U99 UI;۩8ԹzFC! @S# hrt9 1'=Hg}Y1L@L jd`Oqr{O{:("⇖nD @o16WӺJ7YA6laa6ys9BS^ygoi'zRέ("w" r*طr{l޶YXjy 8UAkT2g7 9F/rJOk ^9 bNE'ӎ:ЃDإ0|@ ;(ϟM6ANn @cĜ\v{ =w ѳd?4E @ZQ͘A<5}N7P_h#el/s+U1ۤeI人[^Sط۩ؙ=xug6g<4rpKxC-<9pԏD>=ç}ijNaة7۲q7S /1U5(NƼ}{Qp{CU?^8݆$s "Nr{ZØv[/]=[^1"b^rܥ_ 1&,~ 1'ܩ|ߍ5/X4r4!Vto82ڗY5Kxp[p^`,sۂm#=se쫲G $g- jz=b =Ғ[ѭy <{4zQ:bh>9%e=˧g-oo;x߁j7U>#ohSR[A9c#c'T*g*YNj> JĜK쿲nd\dX+ qcNO >Yt<:hUr=mj"mT=p-F x0 ?#@w##NT*kp>j8[NWb36Pdhr~FwdZO]OU| @bNhsLȭ};yf.Mj#j=iO?PJw7TGDQ/Ś'^dguZN&sK Є<'`Yh{WkPJ'>=>@mZ*QK.D)e;4Z84& *OYOLžltVD9(C39&w?G]^ⴕC^bNT5Mʁ`~!9fXkZ2J޳.X20GfBzr2נ`w[ ld}wFhJɩagf;e۽4F-p<>C|VĹu~Mk3 =ĜZcÛS1ѱmsEo6CL07%Qszlvs&a, \aZpΛqƾixd[iorF'P:g3I㼭"t*B/;QCc!r/qsef^#Qߛ-@k#jo{p}[༶U Gk+bGxmΥkoltnU/c_Suj[NɲW=9n7""/Xh_8Y;{_֘rGwo8vl!gv@*c]E])9 [˼i[Vy!HcΜy\[mϥXyxZA*\,"}kmtY^N$qǩein) 8Wtݍ D;=m% nn%p(fVOF nq0zqnv,|x' 8)'ԏ3dLEؤU]|21'M2gY+"6ۄGy60SV85.XX,dK39fhcEG7)nR+ISJJq4zF<‰9NsqD185in$+*`+NhӶ{fɩq勿!N9-tbH$aCyΊr﬩֍ɗ̥'Ϧ$:!<'Z0mmY{Hd^fKP'l˳`'u<,Ĝ ވS>*= NvhYm'pĺ!ӓh'OPT m(r6m~ڌ?3Ʋ/mpA4DBOo4+l5bN@9op#Ψpt=^[gDt^"OYX H-\pEbWd6m[@-Jqy[i$9iL--(h m]򜩇<#s7?cY* 7x ]538xa`)PkiھmyqAy9aAb1\YŢ4r|r%mѹ+ou,skyo bO9k2 7N=hza)W{ GG˕y-l;H2btU8簽` 9%fU IDAT4Y0@Ruk˫CL Do-w4!٘p(ܢi=Ck^]os6=䀒p z >=%R[ tɴezgӰ%|`vIehoĄ,Ĝ4nK5/㎬w*e bGDt2F-DUeV|o$6Ah&#61[|l$: ˪GBuQ[yY7|;3]ޠө&"G瞥N9Ɯ3=S'!n̙M~[opW8ҋJ[tac4ly{WhQ}k$9݂9C{o3!;30}uϣGg8--NiMx|np/k7woO}EX ϴ<,5{ܘ"Q0<0ԩ9nF^}mzVֺeEuk/)T#z/u{ ׀=~TYFDROgu.*T\ل,@`7W;7gz=<1'<1Aɀ3s޶:֏ טyU]Y #UΈI:g`}֚}nzb:1rWzgZM>׋ 84φ^̹wn+83Wۨ=ߩb➬@V1隶`if̙KugX#Aԛۘ_r*bNQݪ]͞,sk`YϏ6+BT],N:YlEQ2x%N {28msAҝ|Y{O%YOEMR=(SpC KFxg*^ N+?45r> _? !!x4=.C{.ЫӵuN3{I$Bw 1'_;ӫf#5o/ycf; 4m>Ԡ{=g):Zjl!+F4DS^bw {,Ǎym:LodOts?#ҽWxD,l@bN$5.-A8)aDh2-<ߍNT-#v-IF/Sjk1\yNXg I`ts`d]ƜP,GUŜ~EURQc4mҝZ+tc\JQ7#*4-ېkX`bmJOh#!{^e"{%A1u_0s<: {ɫ^Ci~P? BЎyj^b3B5-}VרF7Ԯ0dkvKq1%Փ&8oZ6pf怬5GnYSߑS\D?fU_y{soqt& [ hA>)6?RG{]UsL89i٫'b eE9:4M8s@BTFcu-_.^+= e w6V8NJٓ`c_`Rݮzvtß-p+dfpNE|+ɀ3! Ld퐥]|>ޣۮ!l>qwUy VcO1NN OIQ/ aG݂/7Ym:6lv=7xtܝj1=ڍxk90Nvzng@ bN?faOEEG77v{7.2\\6 ~xu\k%t } A7îNZN̙xHZr iVWsnnsFzj2U7{t>1O;`/ՠI-ҀF\9!ơr1oY ۨ<(:[z̰3.b5?|͑mkqb&4 3s5mc̉eJ6&\]g0EwxyK(-d,3|&sk6~$A @cdvvf6rYWҪH*4~.eаƜ86V﬜k=[$"~RFFDfĜR8@9ucMȍ95fV5yzK֣0~.cu k|V^ZsQ-sx4o!*pglt2?ӊL3 {]3T%w[Z}荘C\ &FR>L_gth>< LpJ;.'jH}oHx+/0[ν^jb!3U 5?8[F|eS~»?ywהv'ݿh}{,tݛc^6(K#f?+%5/kL"ĝ}wș]"b46K׳B*l0/j])9Is ͑xMr!Q[ )^#2&u=/ T̙h ^^.7rKn3oƻ}h3_^5ױrkXyNkg-""WsL]{cm]񊼤˝/WqIz_)v8+]CZɻ٧B,9{)vU_+9<; VY4V)YdY veKTpl= *L}hx79':m4b^b_%{F's 4&o }b5|fŗ̵OWܓE׿,Efj}2wTEkE&4C29*3\:DړljWrɜu%}+jE%=˜7r4+)ԡ'Uį!2/s6h5,ǜ%wLϼ-,jSB1gnr~iߞ>^4O49tι]9/Kvh{a3Rg *2mUsm3cyΊrgNbK_14am((87Fk\s猓&UZFa(DV'N= $9SuS?6ZX @=&nQ8n0L8[6)w1 t_7揶xs匾sއ=a='}RcFEhfpǮG,L=ρ.V=C䙽a^""Slo@V늗aGt,\F @p$N$A9Go_+vl{a)L=v^\v7,9F /߃}pӛxP#:?d_zSm'eopFSmb[7zsG y]YOEw55 '`w]>k3fޣ&[9SO]07ϙ[CIKJɷsIlP*34l6 0ۍ yStYMΒl۱o-7<0p_ױ3VQjG?ļ5eZr&Z5^`S[~VMŽL}|uFǼ1L<1'6 :ϭ#E'.֫xm8#n8Yپn2oMK.kF6 8ED^v}bNÅk{8X1ywzrW6FYѾ r㣭+-iD^m=U9:>~a=S=J͵뱂+81ۿY І>{ݮ:'$>F@^o2Ҟ/]fƼ+M6J<թFٿQWZݺߣ˪]78{RS'L,ALeto-wh}\+8)6RDGP%H$$zN=5Ae0r_^m&D֪VM\s^XrzΜ_{_T}= UDV). '&|9t$j۸%w+m=N.+,kX?WAtx(E,!Ё/SV{6OXaeDľ'2ax|Uo$Y)&hӵov'wM @ 0e5/WraCwӽd?S%]_2+uЂWE{5"_^J,c`=' 1"d#GCl%' ˪GNί%wns^V9_">q|3~}U~]tMcVXJmS97M?)"parpAvlt9ӡi5ǜNE'*wZlU$&GzŜ]wlfɘ-e>7RόSVLPM{HNg r60VsJr{:_ V9cAmƜ^9;约Ĝ^ᒂV7a䛸bΓdNX wK챉3Vl׭CŎmq =<1'lsi fƜu&:k,+x޲N1gPt@fdshOk*͞)k?fzNyϏ6 RўASut8RQ+K=wEڪY;)ړ5nWz yNW7w>C2r9)m>s"x#sk:{9kOpWX)sN.k…דY_<`/;Z "99{a_a䕜м/4^óҞpnm76ko~6n&s{J _p@BzPWӊ;l5$M>h|,ZLW<210#O|lWhwWDE"@yϖٰVskjN.-Q:^ޙ{m+7|{P]0y*%ҋ|0Lƻ].Ĝ>TŜZY\盅]Îmsz۵ ~ĜGd~Stޜ'z9%"b_9=c5[  {b2)͟.Ylgov7 :"c# S>ø#^poy}MyNm;61.<3cYIGWd9S{:3yC/'!_?طUX$UJρyN`Y}gITDI^ssn_67O5:C] ߖdmo 8tO `*?L7kD8o5tBRcUf"}T d ݳm[iLݘߒ%pO3i?%>.Ɯ\hӘ3 -F moĠ~[rcf}$f[O̬{RNoVyNxCyMwc dj'_И{#C7.!ձE7cݱ^{EKw5 ~;brs0:{s5CM=vi .z/.H}$B!gw퟉f5Sugڗ3F̷Fş~{r<=vZcp0r-!-;R4F5C?@;_;0lI?so5fզ 8}MJ(z,F9;ۘX9tZwX2϶W!OXyYɽԭ ^}_Y75S@{v \pP<'hވiaHg9Yx}{2LBOm_M쏎Ʈ Es07#&9qo$p9n"1/SK=EP17֛.}k'gFn{ +X4o.)Bez$bN FR)o fUz2rtgjbh J!ڙpzѳe+%WUZkowyQObN`)֊Sj,Mw#m՘t:8"K^};MozSk3U;Wg_(}kE8hP8 Fc#.*ԋ9 ݳwlԋxQfx1$nq({9NkԞfWP{/uF#o/`'B 1隶`)a#tEzbNٻbƜWm7? bh1!w~ѮRtUЀ鮢'3[BNY)nn#[1 [qf8^+Ph;*ܫnޓ^ 68ȼ..<'g< ٤/Gŧ=,Y]HGc{YF*sΫ.Yr]W7婙Ӊ8<{xCx'2=s w&\!|gM rɽW(U]'mBhIW3bN@#a]yEIQ^Fc9"l#6Fdqtb`}F=4}a!`8w,"KeԳ樭{!/Ts9;WgLQg۳໒iw֊HVe>sk'ӺOY8=ݯDn +)E}nJ7|ލywg8c 8gFI%>Ʒqv ^Jٹu,֫aiK<#W^;AMG}(H0k C" 238Y61!Cث;K-Rol1л:6Nrӊ{"RNؑS S?*az,حɜu=i}9/pCwP'wόh^wڟu?y_@1'SZߓ&bbʫxNJmyWz6pi (GѕџTQ=)ZEoǙy=߀0g 8; +0w\E+niջǜQq÷TЭ eغCn9|.7H w.$KoU@<'Ї{gY'f|9B}&kAz0,{~ _2;6 1'Mm̓U1w\\W{gzʽ՞*ؽD37j{i2',5)FÈ6 skGFV圌OK`3I߈&ݽRv A[{6U]!*%52t/$RĜ@k /,9d% ĜcN]1MvI?sݔo]S\Jܷ Z)=g۵{J4E'Z}!kYW=}N7j ֊Ftk1&>Nć?;s<~R%y p+ 87@oh;%1U܀YMZ1ߓ!0Kj|cjJ֦*9_ƾdJ3J~xBPذy oyfxY91PJ̭xo1cgo9flhS9nJ[!67viZeԜ[!bM9y 'D?w @ Ɯpm>}1g观%UY[tp_[zNsF=Z8ϙ*aWjߞ% slOWj..;cFF4І<'P_3UΘm+-\QOKmR6}d|ps}?nI7w B IO 5޾}}꣣ЇWm2;TO |S;=mR4ձo-P˘?cRIN5#27_ޠ-+.νchVљ_`5o4Ĝ]uÓ^7\FAsqZ1Vk\1\UM֓7bKS P=[/rL̙cES@{\IlF.']vCHMNE zO} /^rUݍ:Ĝ@VT՘mWn͘[7?#"_lkprw S%M9xĜw;o4>:5t'<1'P$(mؠipBWW\͇{4cNGn4ee5.~ɹ[BbZDS9PQ.?!XZ(w{ 1wr/Y_S/5tE(?S}+Kt ʾs<_1YEB[nUY=:bKIn_&u(u_.st~Η 1'&HZn 瘔Db.g7tWSӅ }ŷnRN55 :v$gq1G7ҖywW%sPҒg[kت{SwO ֊Wx$sDyj:a.SF?F+U:}uvg:7 ,yMh]̭2U7N\ozm8xna{˿ țދHw1o痦 JF՚R.kߨdJS<-|\ :Ķ7+Aljh>=,rm_RSp'???c c"`gkZ4Cƈ1n|*Ĝ@OFŞ.j_]EF^)F ' >G !U4t{%졲yv3>yE;{d7]8z8>?pV Г{.1g Jd_Uè֓[wHfy^Cxsi%VpM!Ziy,09n-FMjב­t /ڊ1+/ru!ӿ1'MAPuT^8X ?gysb3sϚt{◍TwrQd27&ZFنHN-+eo9~nZ'έmSXy)g4[u_md4^I " :75yn\+8\ô6xkjxv:Z[*ɹ9{u1]Qɻ?m<7cO'-,<'/?/5(%+iKviOio[iOgG\'wMWK#zDZ8ae#ݡh>m;W2ŕF7AVܫ[Ka-<1'Nѧ}5G/d&rTRjyZ`a[ ӭnDqr$BEsͯę!y8G.+NgF)K3eږn^w-'z\" 4~?y_MpVZܮ16<癘˪ɗ:@{Ĝ"f&LKʗSƪaw95wv|np*;І%tD R32=1 ne*k]s YEo^ u7%²/2 1'PKtW, '[JW~|bEfjfjLDmwTVQ{mo3)>kqn[ֈvf{awľ@`׵[nN֍ψg#p}kky }M;c4P~ޥ폼2v}'C 3hwJgﲊ?r1Iȝ=9t䵯C{_Uq߯VlysDX Tab牓őZy_MsG ;i5ѯ n0^m9аARyhax#sktWǯpk#09xO)٭l|j&o|o N)!9wgk" n _I89װ:2⅀Z Nդե'(N^ۭxP{OU@=t]J]Zܷ 8\nqSK=k#| '(^AϡW&v3ڣ!myzVD dl *1XQP<.}~)۩M:`W4Wywu~z=bGO$Qŷ9j2WPV%GTuqo.]5Q݃j޿_xqfdvsr7#M{gҤ<Qt_mldH Y@"fk}d|DiE( F|$gPi^ͺ{^lѠ~_֓oayΙIκ?˪spL]/tzNpWXVGs 'M^FQЄ;جm/ ~1NS6/$N$ʀ7qۧoO,vݡ^ 'OgļN@(pE׹zo/ܸg8N;DtKI/+bNh>#! 6Ify^s?#xݸ/Œ; <+Ƴb;+˘o boՏ͗ MhwTY5[ݺIjx>Dvc^-r~Ƹ5*B@򡥷Ê}y]mQqaIDATV?נ&.4A v֍9E \Zt3#Bn! Y)@6 fE$u1&IR }+Nv܆[$T4Ny:-֤Oѓov%tt+P5i9l7Yot4Lj}&. 8|mĜ@oMmsj߆0rsowms?EMX;7&bN`pELhDѾ];6tr1:rPӱwjQrE{#`='[T̍'Ɯ=.ؐfz_{G%s6,1)'Ͷt6J&z˳UVJ~J촕T~*'e.lT`t 0?UT2g=2:_Xsޖq,a>{u"z\th仉001'Fp0Y\a7Ǯ!S';Qt[{% ٍV*9rT~CǥrqrMf9h@IձTgXNtłN <&硩uۻu:s.Ո9Ta#}_ۉ-%FӞe-vj=yz;9È6K|N`hרA==h%wFnVc='0՚93ߜU!Ώ*־PQrʴyUk. 8x\ѕP;ڞs%yGa8ET(D GEwKx{vA5zN`,';[N-gJoinwFxbH9ek^nsh/ 5cvgެ?#ON !s'Q)+#-avժ+{om'8~v/y[&JޅZyCJ]t۰'YLܤԑ'^A `}d -np6 KP3-/2s%Uӊ:Quyfl{x]{bN)?(&VI$/8zd4S沪X:Yyx^[O!jݙܢo>~'P]d~eDr^|oYz-~{LI%B4?Xku~{:_n8 '.XoxG6z 1TKD[k]zP9C ҌT0 hѤ$g{f7uӂ<'ތ "K@@o\sY ꡶a-l@~-pc.n*bN"OUo#.@ rM"NO%O+A EMOق7MTų-޾RkC@91ȓZۑ%""wcfn>%t Mue>%eUhBh*ƣ" m-9ߚުSR\ǻ(֑%%ݴvN;RaU@Fb4# /SS= "ߍ,aˢ59ktwˊj@f_9EM Z5UG%DpM>\>4dL\ץŃhs澁Ǐs65{iVuh3\#SK8@i+O,>7u\X2B_sJqd'dn-\7+lɮ|;74z#?nԧ\~1c !=dZ焂 J찳ڄgܺBP.246YX ;nQ }hdVu;亮<բ--7%P9t+gI%Ȗ9Min*eՃޯi*Ő~]rl-K@ W{QY2pIK@ۥ{. ջJ-Łb`zӆ(qN8ݾAtUd1 :|k([ԗߎos6͡wHaBBJKZsx٧7rd#i&!s])\>7qA&y{>!}AZ/: .lc='ܛ= ' ƯG:Oa#Ypf'lf='+gY1,Sh }hڙc[튺c/}Z8 l@Hֶ磰DZU3&p~*jo|t*JnN感%y>[ܫsANshL2'a Uqӟ%R^dԭGHTh`k<{=:dX uk\&׆T!uB:\]sd&kvෙ[ J{9yejpvT ÖN8 ֛{㾓g=/n2O^Ay_Ӌ+Gugb%~ax}2'[7ЏׄW 56SpUdN`JJZPU' ~櫚TN ٕc/&ST#N'!P9yvfϱJ-MǤwϺ|vWb5l;\8'|r _Wd]Uwba Wk.kW Os}, Lui?Y;5?0Lw~DPJL9ƪ҈]8isO8dN$cULdK.ծmuvSjdNW lJїhk@q:CY Sd`9\.]9vùcU8\9^zJ&^)@)m8**0Ϩ? ЎӘ*en;NAJ͟gVmo/:%gIp~۽(E/~i!~!njMm>`o:<oʫ z w!sw coEбR(s|l*U=l(<<=UϕjS̩ƿjV~6@*dNxrb)=I#7ݥ][5!bHt#7_g,̭~lۂ^wse* w D8ͧ1 \AűvoϹ/B+n, ujnS$*Ow%8n9:fbror!|*4…?D O-m/9Y~ K196M=s3R[>sWiuݠoS`7uk4>tBj[7ieqSc'0ZOo@85ett &sSo7mq+')}՜sy"K|ZW΋(ub 8'A7ft |BBhL^xϪ)+p3 pW91s+(F٨.] T;8'3*Nr2'L<2)Mr2' 'Pht8r+`ܥ 6F a`Åɐ[!B]k( ̜tnŁҎ^@1 L*f)gJɴ9@idN\8S7LG Z%sj6m5rOɜghoDD m sd`k89rm|Vo7"s%2Bôix@ٺ{e.Dt868c_ 'pO9vIXI#u/O T}R`YQM* o&s)5s**ѫ==1a_{Pw#sk>eNs}U ܚqN K=^ }91k2-Ks$XkS ;Nΰ~@vDlFfDlFfDpŏ(: 1 y~ <vqD@ͳ;"kĎڇ_vG8vFDF:v[DƎMio1vOy#$Qk#䦋쨒*BFYw i?&xle1#dM/7oDH{QGH~QGM""dzp#B6OpчvT9QE ]wT+20uel"=ldjG!#;+"d];QgE V~r>`0EH~l<nj72!>S;jSQo" Hǎ5BF|:8쨧+^? uARa=xGfJ<pPo#CRnRo\EXÍW>Xd$,))p}=tDx:XME[|< c5wuFjFV YMS[A")2ZAkj )^'YM}`jN‚XAkS"VSD7ôFرd»jPj f )Y IzCb5?:BWL‚mK#VSX"Y*+%)Q(y$,XMa;lPXM3*XMˌC%sXl Z3b#+h'I $)fRYy)&1E(fp̌Zּ{$r`5 +htj^WZEVS< [)XAkn"VSq`)$l#xW/mG"Svcm:H\f:*6ҕ_˂-''BZ?=73kQ1ٝf< f1\uZ2 9EWS#og$"BCrL bi#3OFhi֣ Wq} j-}{MBjj&HL+'BΫĝ,\\S>5_k.(❠oy #:}\!.Uo `D {DI~~$F0L%*n G]T,DGG3>I8B3ӧ4.`D`rR jGkB)WV&a*(GV}LBC##$@xUog/k)Sw$@s1-^ٿJ5 oo:DSg S#`2#91!&0#:*xb9BC _P\(#:D/KBh@q!+P|E(tK w  o4BCT\(~#:/P\G(t^{B(PD(tR%FBD*PD(tF%RB +PE(tZ%^BAE-PBF( (Q#'-b(#c^ЯJЯ J}JR$PЛ<JHUPЃlJmKX$PЪJI[PВJmH^$Pp7 ":܇߈PwÕ(Fp < lp yoa:||(FeR`#"|ov g!v#tx@#8ѡP"C TAvHZPA)PH!"B5I:@uD)|8* !NBBvH:p0RlD8Q)p"uNC \Wp!D8#u\tHRC)RDZ9ŭuHqtH6v:p' RvD)"oWhC X!B!B[fwH9:@ТRYDhnhڥ!GwHu#R D@_C th}!BOZ:@wЙ)#"[!E^}vHNcuuF{0c!!!kJeIENDB`pyformex-0.8.6/pyformex/doc/html/_images/stent-cell-full.png0000644000211500021150000000344111253711161023740 0ustar benebene00000000000000PNG  IHDR [;{ pHYs+IDATxےèEyHMۖ {= Hq (44444_z¾o y_^i:@o)KcB6c@?aL.|LRP؊6+GI6@'7/$zku=;iv  f= a]+wMH$.Q o͟k5ɛ( 8Dƞ*m8;9I˯ Iuw5E5{oyv2L\Ү 91vtFս|!aValnF&9Gc??vLƞ@"4߂?PB/tz2[:ޜEmgpГ.SZ@jn<8' &5 xi&t*r~{6ëUcܵOp;"bv7ǯ$OUSĺ9> ,! X=5d8oS^BKziC-u6q9f˶*UP|9Rc)쌺Kzu>o=vߤ\y$kmŽ_&B6H 4`K:"*с7%Z $56"<coL\f2IIԂZDK=;A")xuUnqUZ($b M4c5au5>QTD΅4c4)o_Q$48)qJDj'xE ֢7}[nDH"J3!*,o(OMV{ ;"1% =irx|S,tF=ۂ444444Y ෋(IENDB`pyformex-0.8.6/pyformex/doc/html/_images/wireframe.png0000644000211500021150000000105611253664076022723 0ustar benebene00000000000000PNG  IHDR #ꦷbKGD X pHYsHHFk> vpAg IDATh EtLB| :yv>5M_TJJD)s9GG8|u1q@ ҊKc(沦W،Q xրCo(Xrn`&@QRk ܈1P@@@#"Qql/m" #B(w8VJ=Ƙz D@] Zvyy977WհRcW_9M|ݭV1EǷZ-cNL,˪UWȲLDh1`_섧YqasssHeQ é)#~^ b" }jj5|$ր~?55O!)T`0===G#ovA1K#Bq\ *DV.uH2C]XXth""9C# ݢn'I233U[Q2v0(%4\5p@c\ bL"1N}R@k3ADffffff BXSױiV @qrrGrttm^6_/lG$V9}nN#"i˗/z俐G6v󩩩~= Phl!L"@ͺ`He~211?? BK<Sc5>,˲W^I݆Bq$I&`t<Av055Y&sj`rADE-*cvvv0τ7(T*>K ҁIJ ǠX"? ؏tgq ?ýn0333 ۅY1WWW $4iʉSh =v 9ex@sMai`(FFx+3Q! w,o ^02˲1fOeYiՕs4M&D@LQvp8Ea668R!2$r'VŬ*((Gϧnpeҡn GUЗBei>傘̱h40}^O69װ/Sɱ/aP%5 "Ņy0 3sv%(qòh4Z`zOѰO 8wrgqqcϒ#Ixm"sښl4b5ht,,E3{)%\L 7G;OM|8::՟i,˘ի5`xD1)#zѠ"277w}}EQnӋtm88{&#1c{{[DC i_gbqq:{ pBFp ɀ(?,˨}dYS >2766V 7;E0:>2pYN4cHU`ĉbFq$I$Z :c O$3 Nh8Y^z$8ׯ< 51_ f~a(W{FYwm"#j7|j߇nV1o!Dz⢓, #1+++.//&LllqHE-Nsaaa>޼ysг EqB4wŋ b"E:L#4cq0z WV\tJKNMM9E:l_[[#3J_:hpmYih4p vub܈.0jqpam=GGGZb"M(cRE`6n{uuŗc MMMabZ,,FcLZEEg;A_8 <$奈vB<8Lmн 4)2meY\\-ALgF&6𖳦 i( b'\\ujGGG]vvvb =Z;"/@E,Bkjc_e~w:2VkhxAϟu'>'==-<^#F. Rp D%c0Z$&)1!Py؊oTiaիU'&=uAXb@ai5tc<Ǥu\2֒?r I|1;c;ىfy~~+}we^Z(P0W@loocGJd ]>y|#xzo A85ސ?9n#A%JFYb$XNcuNw '_:p*߭Ltr[c4uAdd19b{{ie1gggq>H-Z!9۰ j1~4Mio٬d^%%8bg.̼t>bvYtCrh%nwyy 91p~Fzj`02Xt W i5A{0 䵵5$RLlnnb3ꑦ?zB*M PbU2>|K{ssqJy#G!zG%"dpt `cff> ]s^eZ~7h{f= ~˱wh40I ^8^Ufc:2GWTH0z2Zw"n=cc8p'gT(: ֥_Va"gz=:G(yQU:7̯Col+Nz@ujafY[[[g6I&% Qk:#D}BҪQyh>C3PX=FdlQzppPXL/%PW*&NNǩHn4~8Fdb_%KFgKۅ ; jt01j',@O1I;+4:88H??a^8<-YDUlŰ2fo&>\ J+tI#E@TDdFĺn?FF- lnt8Gdv+܈TR~K9pB=?ŲEp6PH;(kh35>[/Ȳ w ^Ÿ(e>} 'QYРM|X@s j.H˲Ld.CpV!AHyɓ'OPx/s(`wzzGt|ov b[raMw2 ۰D5r e9Q!ng(#)1ۑ[ӓnjp\* nJ5hD2 mn֑˔1;;Kv"<,RU\ G l}}k))1=8Yh'^dyqΦ%32&ϕ݅M!Jˀ̲l8NMM9qoyxǘ"n*6HikPiN q-|h6&E Px6kE*1׎'ԝ̃6~)"—[BHNaȌwss9}x:e 'a=©rzzzpp@EmZPa)bYA}r3LTxQيRZF艪dž裎0 T+-qN"Sn bUnooyg*k xXbOR(+kۗCɩ5Ɨ(<{ bcww6sيHqJȗucH(GGG~g`}ԯ^ :4GH B"s"{#ytfz~wAmIYD  ńXI!ޏQ|H-CJ:ϊj<@^ l)^X rP+@FOC gi$hn QvIZEd0*tGkO[(o 8fDe N]TBJ;vqqy&׀x,`S`j5v;uYRfHvG+X5YDa>QָS:9]zr`Ђl,G:hB?(uQDa&q']˗0=T=>>X\\d{bcnnnX_iΫ*>?hb!M?>0 I|8# /珢F C~3scc+=yew*b)Q() |~c(c1O?"z")Uz^|&Z\#0͜7Vc{C7oҮ gn8*7|aQ|bGEZmff&FY }/" #RCVBtXW‘\.U쨝>UST[q| ˶bټl6 Ӑ6??_1eR3+3yFW/]:wL &L=?{'c ԨӁ{"[AW`aQ-㢧_2 oo(L(JW/IT*H?Ԋ'UD~'_R@SQcjR' e. HFQx4q&*WWШ1gggbsh  ʰ:WYIYC5e~x19Zљ,˖x\qssӌN Sh+++ suuEa4xKba(r(n(4޾}(:kv?=@|4bB ؇wOe?18H>s!T/,/..eJ(B Ki?w/)&@q NGQT0_^e41-*84MQY!Jk@W9rat@tt:z]֘ÇktZ-lЏe3`seNb_ee +H+>Q@^RuttDP ):|dp$*2d}>H.ws@E9k+ Q5M25 sugggBP+~QY WϢgN[v(܍f,."SSSX:\aaT*NZ6 XLLᐍ'BDQAS fums^1R|#KO( W*~O'ږ nߙ BǾ6Y]?41D@\wDi4m PteF>m|b=2L SD~8 9[ 4牣U(>UssY$5[,e3J z4V͊mMiR)e2`9v@pQ8!cб' :Bal6cm_\\\DQY2.pވ/ Uz<=|$899+3L\y'O``|kSh?"а G(1@vB6~_ \1=9ѪZ1~]]:.nհ& Twv): J$=hxњQu]M|98ewXBߌD9KjDdlGQn )wM QU}ۃ_:T+6W^̯#ٸfYeg>W~|DNFVWU}( 4)+LXt:˹*ZV 'O#E %ƛ0M'u3l6QRiZc=/opމ6AoØ6J8i2GC+ǧԄFbhpsmd 8qh4z}뤣V mW&ƪӨZJFku^8 3Ń/)ua#I8xf_ ?Da c;|w6xJ+I9I׈9?sS > ZЮ˜V%q*m]`k/")..9D($DW^Q`Q|ovl,iE[L;Rԃ HJ\Q3}e4S@7W AmCyiD@RD,[^^o* OBJRЗFAbOlKJ'-666`%q?ڦMc6rΔ* ǭ~/^...*mM S )"rrrj!!wM++Q)|[Zɚ/PG A#ńm¦NG(|'XF٨ m5|)6P ZZBp4<Ɩ.|;V|x봭OM7` btZ-Me_SSSttRn;&/c|A!gVQѿ;)N`YVb@ZtM$MS$,vbZRxrj[nK+k`|9_677+)sYOEF5g&\vG+kkyP}%_`]a="v[D՚AHǜ$* נmZ-MGp[z^zg͋p85X,j|X'#0s!hIiQ1Qv )s .!P4M8mr8e_wp5Wizţ2Is )]nE/^"ē8ꃨWNLH9tiEIhTļጐ<GSI)VW?^ΘQ|KWsc08=>saFQt}}u1(~峄vEf>ɽllly~ˠZGEFIe\ &Hzww;.FV|8/..ͦj3ٌMƧqA !6[gvvc#?(M>}(?j!SNNjcʲ rVXa鰑DOSe8hN)cm Θws/_u+_6?]6NSn/$ Cb*݅BEiBWk Ay6VWWhp7?b5uU뺢ky"  e9a`)+ {eeT6==R:{(d~P8OM5qpe͍_@XhvԿ 2)Ku3Z$w zفjs{{Dt٤{2s4D$Itkk4M{A f"RT<) \VXrH0fd)p#I\F)O TPdPEUJeX~Vx@=|p^ޝ|M6f> e΀5Y677Yc&2 A$*p c!)"˞ԭCx<'MUJv|VxH"9HZ.5+[0ayL0$vBUFs[TGp},oMa,mVz$ׇg8f4ND'-} z>e1 cK3=Ą[eJQ>N,t3*@b%Ot졎ے6SSSajZס;,P,d|aBÊT 0aFNNNvwwq0,P./4cE~@%+-o%TOw @cl69_{(iv :ꉌ,M44t:FRVg+9q8qBNR!4هZuϲ+Z.wlc JN󤽡 /"cԀю vyyrOMpmԡ{GCvL bL/FGHA,U;mZ#+cb֛H$xRURiݵ H"HÒ$yu. O8A6iG1Zg*~%0b믿c1il]N1ϼ4ƐkſĽ$KX__GNKz,FD eia'K',MD_ͧb tsC>[Ϟ=I[ɢjkkkp1"CIm2Ɩk Z](LI NVce9\ Mk@ypd3X;jK3*PMՇƆwSA L|X&g5eT1*]V6r}}%^ˎfN6L&OćV"Zȋ]4 6EcP`onnq 2ef1&MӓW^=577څbV/^8::ùTv!g]s4?M>0`O16B'''(2eӦ@pxpySD׻9FD6/4ȈG!׈=3kDQn AO@JN3Y TTP'Zׯ,밧|˦3r|($ adVo[nLv[t06+)0q~-58f 53V$sDB+?nӧPB=޸> vj1* i ZlHfE;xՇtLh}'БBN'/tC|uSDչKx....D$IAyDL%n9*!3+&T޹\-ʨIzUURMk$)bvC|("p~!,QטҲ$!G)87>6VeDO$ 쭭-Jܶi C T0yŷ/^ ,B V7V_MO)!*bDL8/GllA*T#_/S 3#bKIA V}2` рd^^F{{{ZGzgR x6$ѭ-\H9u,Љd4E5H@4Lr%+"IF-cs"q^o`Z)}L'CۻFGӁEjRD`^9-Tt:&L $NHu l@T!m) =ӧOYTݮ)N|i_hN_\\PDjcmRA}:N >W*H4=c5$IlT%6BŋE݉9==}ssK FT > XPSFvMI69N>LP"qmmMT t SJD׃COϏ`~xa ͩ(@qY]]`*)$CGtH g28[0!x%'!&RR`G=z> KKKƖcne*/"K\7 ComAgڄ:4lu|s|G&+Cc (%Ƙfy~~cn<ww6;fв_Sd,f$ɲA4Mi0 i ZjJ2 fff ht|x;}:6hBlrd:2AGۅTTuj$5'h8"i8Fxp1|>xӟ\#4tRFYΎBqm(D+ „f'2j}8nxwNIVNZ ,Z ! 3DY8s,Շ4M嗇 # ]ԛU z:u9 > 51;, _d@Lj,&560>1)n@@@>É\%IENDB`pyformex-0.8.6/pyformex/doc/html/_images/cypher-stent-unroll.png0000644000211500021150000004752711253711161024701 0ustar benebene00000000000000PNG  IHDRb[ pHYs+ IDATx]kWI,ImwOٳmxcIu{{;3e ARUefee<#JʬȈJkM__و+bUL|W|EAYBkt:D4ALDϞ=H)fIDZj8 ϟ?<޽#|}\kxkwww?w:#UٔA#q9J{Ұq'k?ZZj~!i$AO:۷!W=Qj>Mߗ!Iy="+۷oq5z_{J)$5?E:`~R{ iE÷lb>`0x<&*@DQ<±'&F155E4Bס$憔?skZ\Zs(޾}{{{J[Cj8q嵵 d$c'IruuGDnW)577'/ ҋ/pJK!%Wq7VWWK usssȨKKKhJ),-J"׽5рwjZ*xi(x8]L<VWWR<b____\\ _֖hY__gvY|qR ~'7Ћ/c ) 9N.}Le)!&>kᯱ#X9:: jQJ ñW' "aE׉p; $1v }<%x?~4zO=,:-ەJϟ%+I WaVZ'IvQI!.//wz\.JECٱp1ˤ|_p j(ʚYk7CǏIӄae`6i?D&z$I/Ɣ0Tŝ6i,kƎ^Bbt" DdS]i{rޮ,--PHDBj{{{zz: 3qP88!`!@Yyjr d`0Zcf:JS(ZB,>mƫWd\jsZsŒWIպ"q$P L3C>"dn5؂@N"IǙPȪR׿kaҺ͔ZtT{'jXUe <8~rhS O'ajȯ4嗇 ~%LĿYsuuM6PtQ1qqqa䜷0aʹ|d+#ul@. +o}~~^xzz=55e84 #X^Ra(hl(6 ;H nǰf,T60$©z˗svvmא$V>rqqAO/<>tJZW-P% $Pr'LIf\\\lȋqyyT*N 3_j8s~~AN7,,j$K*5Wn['&w]qc|w0x9"BDD3]'WLN~JÔR eh:ˬ+GapyW!JfN]FZӑ|vpMx>ЃkkkDJ`Prֺ鄓G0N6^/..&ʙ)&7&J2.uT/TJꞅ`ד$I...&D;`gH{iޮà&3ОE mx73hȶ^ dFm^F֔iYR^sd̟ڪT* kz(, %Iq.yii L*S( $ɤݢd*r$_4qwV5==`5Jt Yx 3rsg߂}Q~x{|`鐶\fjTJZ'I_b\p^+J[Ԧ2`0Ck/?FQ.yb6 PIAԂlsoķ'<9M0533K(I5 (E|2>'.!cqڈl*lI0LYLtsVeHmƷv LEȚAhrz*cLƛckk?Cd+#_E#ܖrPüLJ;yzzzT'x99hggs>Bv1+* CCݖāNֱ [*4:XcBĊq5;ZLib(> #9O6-4ZښrK ɥR܍F(”8=::3X?7bϔtc 09./Z%XgggV ;(K mg[JmL#ն|qWVK#Dh ΁LcwD{ǵe !&˗~\lcc`jC\7tUhff$>V)r# /$ݮt)n;m"+M8Q\CRSݩŰxlМX)@4㛛ik!)fff }RN'Ix t7,,ǧq2֞9lY'/w\uxxJa t{!+ZRw4Q1yTtSJe֐Dw 6AE& ol<ӘN6JLH?8KRQBzRaV%.z] :o !T3M6v{{{ff? OuzzHWYfsmGGGv[k)T0F!ﲸHEHiyN@0ksgNj[kmR{,]ROqq  @{lnlx֕aA5.)[ij(4[BNF}x(^a i_ x`des~C(4 -=J6RTT|801?{xhIT7ZH%L #k.0Igm,B")!?[2g:ځ^YZz}|)Qc˯^9CcX`Fj7oBFg|͔^TIE@l%/K830&Bh46u:-+|}x,Z6d3;ՓK6{9|Nve EJ ͦp+C[G`cm{AJ+&$I~vqydiV Ay1bv~~>;;*$, \3Rc<F368)n# k>aM^w짱1 7;̑͏1F3d܈/oÅ+LDpI~y(K¸ _5fE4bD{sg:] & iƫf}ss&HNeG#{ ;J zaHH%o\5h r+jؽT(@Z˖!ÆJDcӜF`"1(J!uYy11ky!&x w.)OBqH,)MkTqw$I8-wH ȝc<0vGL/;7Z$r'|u_E=|u8r? LzCe^L|jʾʚsg ggg˗/6b㳵 f4z%jO{XBxsEQQ47ٟ`mVN6q))-v>7;kf%EkH dd8Z8Bc-kpaasʇ4>ILo;YWY r:= (z^H=ǿdKG wvvTd6ݏI;\]7jtX-h8}\úADjg1`ӆ!oOra`c6a~_ ^?1Fvu&uf2^8c-zIfcxtZio=^L0/LhNEҒtzz ~E U7/jzgT|)t4R1F^iK]j)r"%P`9CF4k$,cgBtsxT&Y`!qqg*;YH) ~;sj``ь5&dB!`"A d1Vɖ(E[t,hi "枞 o| 鋢ΰtzG7\ecuqMkB_NN~Ū N>C,-7 ,94ɨN"C8؄,"h{'z=$%N x%J,f7P" N`R"'E`0ݻw޽;<< 8q21XFh' q||Y=^DR*<a<Q>篮''hp+gX$ooogff،@u1C )צfק"[%KgJIV*"NgnnNR ۬~sT۹K;1Lr=MVVV\UV8Z"(>œLqV}ZՌ7X[R7RNÇ lA'`v\*?''IRUv||>???55upp uHfdիW333Rkek 8(uu;IrZy- 0&w+1t $x uIFm]-RTMXZZ>BXCD#Eu(KiWR)<\ +C[tvy@XBueZV*%{~~Ѩʨ*P{5YeE{M1bcvq:Yfr^__4o޼JG #O./Rjۏ^@`u/J*-XF~<83LU ye{bj/MIFc~~>$|^6}<#Ύ3〽.3 +yGR6??_*nnnM #?i--2qqjnTY`:v19(]_YlPE+1d< X%fT$+lg"(}2ī!_}lYj|uAf""۪Nib(-Tzss=nQ+x4۲͜xͅx✋<$InqqΈJbW~P#GUaB_bjuahtN[m]B~ggo,Ha RZjODJ)"0&?cHI5l bUV{ )9<*(SJn!:ǎR IJ,5LJ!fNiVL`7إF "Y\Jbeea MdO$dW;"wLF]HECu>{ -S":::bʿ셖\.0J(Xl,[8㘽Γuf}#z&! 50266vvv(c^pvvvyyI+X/oXfT7r%wiFs?}d)K,p;Իw? DVK U D)@@գ߿ Ŷj٭&1봬1ocfnB])\=1\\\ $VD,(%PaB.#[jч䷭Y1E(mLZ-M@jP0*䱗@NȀȩ ggg9e.2uad|#f ;v)FU8p8Çń n ! i ?Y??~G cwIrrC|m\wC Nbo.MD\`{^JI;9yVVVwBcx'ǻwbC A >`Nç+yy~~cl:$y*M`=Q&@2(Rw;i`br*NDZ߽zw}y@yAyi -7u ET@W81;;sb #(6;N/nQEqբ@X `0|C܁hCD*E\Zk#qK\>88\|aB\ rF|Y@С؉[[$O>o w2G|5Pr/Jz|(0<n;vOd!U Bplo' K>.;v64m!ncn!14*#*a3rFn~/x ==O֬ӡEYj ئ#v'GEz5Q ػM`6bp1dW6 RW"6ʢpxVM_ IDAT?>.`&JVW|5fTbZkx%Rl+e@{hRe=d" d !~ς Frjs^j ` Ņ"xyyyos"l8OD3N@YlQR01gܫ$R#i@m1=Iqdzz3B4Li,,|9_ȐX.Qo:gJVr-YJ!gggN25, V~DsP0={`0GGGGGG9IAY#P$띟T)* b:/62x* =$;kz(v.Pv$ a+byv=c83Y߂_rrrrrDRMK1Omw2˲$I^qQ.H!8ǩORxܐ!)+ y·Bt:LoH+<5[p9ѣɃAs' "'^ ( |L D9Ȳٛ Xrg|0&"6:?? g]-I^2"D36b6!4F!QHx5,M#|)Ĩ{Ĭq%ERQMq0!?$#Y,~3֓u\`0OʶۜCb<n*JR988oZH;!ju30 iRikklY9tFn' ":;;}kkknyЊB2ښ8k!$ET Cx {@gჃHvOlNicjj f{Cx||l EoB0q<'<FK`0jkk+-uY xMkY:j4IVb}} w<2VCC N }5w`P6T*c2;x֟~i}}]B/7|lp$H~XA=&KLmCFDd ,{\!JBfQ㈱x|=JQ>DXa6hZQ@o9phRҏ ó`bt`[[Rp˻xe F @) ?p84r>JYNEBx~ozV4O 3ߍOmA+Ffɉs "WY46 F,J)۲+td, <."3 Da8T̪.j\.C㿠si =\:ޓM\RV]L&V.1"&FRCH֍e"Ѝ 'W\N{g?:5ÐF"~&zJkb|?DƖE$$\,ˊ^SWxy0nX5dyW;nG]Ŷד;IGWn[*&kł);{{{C/d9 gOZ+`?7 SDy`wUJrUͣ#8b<m{B>Đ_0E$T&oYip T{ZC'hXIZ C'I=vA>-kK!# s)$%I⩪{rraw _֖Ak2[RR\ #RArxD]dnY:R3\N7[8|a 4bAYZG(bbP`z$Ila2In,G+q unA+@cq /Q*FvSggAIʅǮvyy)k간EQLMMjx$-:^'%RPj|+u D~Rve< f|lVUHiσJjaM/8noos/ !V~ړC.f0l$;8UCRî43? ύSm[ΘHQywwW)\xulwuevvv V*=!ϟ۶$rgU3:4+1yk7F,a0\]]UU/Jh$O"#qLl)VU=J@#)OQM "$$c$2lOPq/6*!>O3inx=:`0&KF<":x^WJY2 ym,@}vrϙ4oaca!}FIq {."XlE+t A??B瀰 vGH$I =#lY\ۻ4MD(Bǎ|OիE \azsvwwtcNr^wTfhny8Jxyd3$h2XZk# $Kl'ܳpRL˕e*evgIgLJeT0T(Lq_Eߴ$HoGI)5#! Iϟ'$k;Up@ec97FylxKy }M0sBVfF#'6(Hx>di=p\ADHJ~=& ׭$`~rlll`$v1z9|f>14YplOe{qkNVʡ(㱪*у;L{43OOOQ|D养)/N| Zk-R0V #w~;Y]CUeeIc׆ey4@O`4du h YC/www @G2U"I`Aw=_,Dv3dƥo QNeGOs {U1nOq ckJFQ[gs!/e;;;8k7_jI)}(c#>77ǿzfgg?~3 f+ճ3;H2N!?Ti-7JcI6FJ^;2#R+: E b+GDr(׌,7K<;((5-L[6}eG Gfxm6h׮hKM/5u8{"p?<b0 IJeqzX9i@Dj4`̞]-x\,ܖWWW߱Kϼ'mҖIZ{jDmGgvƝ h3،L |{tt牼0CN唬IgaKҝu.b٫\qF6B ƿ3i2(esqto+opUNc 2o|~fc{YO+/,70v2pGCK$P){2FSQ7r~3S<( jeeE)yK1r;;;9pǬW,Dr؉Å6hTwʐ uNeVwt }Y@'HF6:g˭-$1] $1n7ϟ$T6jq+< !OP&0HJ sz$FQ[UܼU$ 8#BO'de&Q v#^:$h.|!HE=^6'Gh6WBy鼹6 KتwѺЏJKDKKKFuLYPdh%F(>(㒹ON)J!,?, # DSVvq;k3&@k=b^JŜFCeiԲk3sXLYvaR)XH]J10gYk h1SOP#2k2B1Bg*&qQd`&-VLZ-,l\ 75\:H-˹pPڟ$<#/t2.Ӑ|\ș~>9E3QjօiYbM68.02Rv\lmm={ Ʃ@wK=)qE\FW|{}}](!R8&.%1uZօ+Zn5M(б/]3 G ™s"96ÇP3ƨ.&'eD߿}SJ4{w`s3(yeńcegt *rv! mV:l.LReJgB &? IvhZ &w̤sx"W.z}dž3Vzo8 #@ &2M(zS 7rEEJCilI1>99m[*SYHID!mǿx`B/Y گtqQ6 A{I$8Yly3r ~2\[,k_hƂ Q'x!\H[)&:m85", DࢬM<"XxJxJ-L)usscht]ȑ `EEvsVČVep랗n,N cy/Ļ7"a;ZI:d{c5Svg|A5LhDtyy)ƕy>aeeoѱXse+N`1noo V^%|VLYnǛ0TZ_~vFjc@ ٸar9#` Ab&ܧ)7?OcCkkٷ$ޜ/|~#/1Z~HN-,,r(8Ӽ=+cTOBJ>vwϮZL>R"$*C nqZE,&+֐Rb#MMQ0khy٠`0bbggT*$L(ayyR}doWy0S(U:& gI UA<$c~eUېEp%<38R,DtppMH 2F;bIK\T~a̰D@ @P|57DИ _hڇ z-fEne7)UT4F0u;yh{Ɛ˺fqXa>K$^_p>FJ{Lk.s Y@gaW˒1ܟ+ #J$\sws&~42-5n'G/V֞#W*\ CxT*Bَ8w#&ӄ`1TZY&*kcd#\EcS3zaBlq+8_g1|҂U f3uo Ç4ZsQbgvœdjWQF@Dn(-V 4j12 OjHZϡVs~V*xv* Lz~ n W41^(Y,Y9F)%Rɟ0M-<ٔ$kq3c͊ZlN$$@g3mPƜ!Q?y<55` |pzz*X@xp.}lUYCRo؞Jp1$\73rRyyϕ<Ғy✢+6o;S, Ŵf5M?0siᅅYCEzg?>DjkWU8#5ߤU$U49]s-XT= "epۓP( C~? DZ565h.w177Z(pSEidMZwVi,1+e&z0CDjsf9ɫYFK),@ dbggggggooj} DZ\\4,9l!6ii,R Usg^'dU?;;Cא Y$x0ﱍE8"ID$>䖊HějڻieOp;C)i]~t @G" 2:w1Iz=i,;;;xx(1h4(5riYJ'_% ,2$I_Ç/_1Jp8,˰i*))ҵ.[MݥK8@755eD.ՉvvvX9 Sz a Ip:vء o66Vr;<=====-F3IIQqc%_L 97p^8Ku\y($%nvvv`gʒ;! 8+fAh\ B믔"0JRHU,zhUHgEIDAThlF2q@ֻZ(z /L-i?g 7ogmnll3McIq'"}F'a7/g%w׿~93KD9 r>S$/c }ҕFǨ|O6NƔv,oFg'U 4 $bx_ (;!|,:테cPRݖJ%ghp%0vBV)&J\a q>"<; ?*JN6 dXYYRf"؅ d1RiAdo$_#kF}I6g0onn 1PO:vvmx)^EdgIic!QAv +Av#]֓s\ٕEV{ĴBGmG7Jȋ^]]iJRPm5:??9W.>F<44'OGIt:Bllc&wD ,=c"z`qTcI|>?? /s%R`=pdqoGb@6 st:ECm H|$Ԩ+Ѡv?DٜL.ʊG`pF ̮o߶3NNN$ WۜFl24Iih4PM~ L Xjx~-h486J%IbX]]EKz nNXwu|v9,4 IRqI?ւi6I$n922 Bif'L!1‰$qBg?&e<$+ўHc&5uљ $qfE3.K($OŵZ19YFPOO憑H.#Gr ]x|x.^EDV`zjjn%[s)T2"yTAœ#-R 靝p-5aفs['PE^p0.e3=my3qZNS*vvv8J*RKNv:di|4IyP?y+)}m_g}}R C|0HΫvmxҩUBε`ɋKv{^`KKO871 HMn- V|O| ӰkAs x|!χ2 Z Y >a/A"NFRK5mrCoΜN$lu BXB.[q#3'i=F ).//%iIdoo/k/OKLrO"EN` "eyuuU :I|knn.j6*:!dj-R#FJ|&'''v, 4 ĸ#|lnWXF#,4,}D>h볔d1wqqBed|GmrV$CzW{LȅQbU='ȳrhT蠵5=h+NOF;/"cwHW2O]8йd:j 6x Џ:qY;; YblGcĉ x}}]G/uyyᆪ:*7u{uWxT>Wd#N s+skJn+;BF{yP1A|!P7ă\7L3}KEξ6R qvv!么S*(v-탆4,#˄V l.FOOOBYꁆ ooo6eAN c+;$=`3== c0$I" @HCMB97Y#^'5Q[ഄ'M 'dZ)wIET|̌t<ĽHvY|Щ{yle;QIG9ᚥ"DY*$w.G K:ni:[֕b(I HRd$IC E E j=.t`0PJS@px|gRZ-IFzJcX,Rd9\{#՘9]'''y$\Gh^rBЮ\r͓&O+J,bbggGۗ- (&tUfG4>$ ׯ 8ꓡvgp[D.bIzO#(\Xp+{1LǫxB.`bTK}L b}•Jf!Br-`/YjZh~z=Kك ǵZ nu~{G6NjNt>}lܠlPrFY'ش+ Ej8SkqY[ȩ ~ccSt#UD0ڈ7?<Ћ%!kD!pZ !聧-#rkwwwD(?|->Wo߾=88rjMwgvkuu˗A>Y>lѷ|'-¿$!ܹl6gP8hb #.<T_<܇jbexx;/tBHffV)Ƣ6,{xx(qH+3&"W|WLW1_9?-נ]IENDB`pyformex-0.8.6/pyformex/doc/html/_images/smooth.png0000644000211500021150000000071411252262667022252 0ustar benebene00000000000000PNG  IHDR #ꦷbKGD X pHYsHHFk> vpAg WIDATh1 E% <@*YVZy+R11(+,LC]B̲f]F)5;P G+K%mإ%<wϺqޘzP!~=0Tȃ{B慾>m_uھ!` ΂aR^zE؂8 q sA"+m#xj9 6+VAZqpU2x`%+5O&d-+gY*@eY'4>X@"/ HMEG[Vpt" A\@@E ΎoQUU>N7`& IENDB`pyformex-0.8.6/pyformex/doc/html/_images/tetraeder.png0000644000211500021150000000506611655052026022716 0ustar benebene00000000000000PNG  IHDRX,(=3PLTE@@ YYY===Y44Y==YYFFkkOOY88}}=--tM=bKGDH pHYsMMHh :IDATx6Eaʉ&v;y0ZF7rߪj+@DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD;};g|:{J}3O3dev5 + Y,du~#DVF?^`!+,det2zBjXZ+!za!kXZ{^a!kG5 ,d-4F5Bl5,d4oBdM\`!k?#kiXȚlncBT;f5j-lA[&a!kN,d Y-nCKKQd= Y/mEWWWdd=Z7#*,d}nBֽ(",d{:Y,XȺ\X,yBdeBeVa!k ,dB֦ Y.k 6>XZUಶYFXem> $B~L\Ya爙\eÊ+kEAeV־Cb+VLY[ (&X!e?/ݰ":p`"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"qmMDk7. a a a a a a a5IENDB`pyformex-0.8.6/pyformex/doc/html/_images/helix-000.png0000644000211500021150000000326011300456345022336 0ustar benebene00000000000000PNG  IHDR,," pHYsMMHhbIDATxq: Q-%״>$?YǶd s jD?DDDDDDDDDDDDDDDDDDDD^mMyhjJ6'M!3'Ÿ!2m%!3[:d^Nt53=;Cx:Vܢ!ܱ*;nZzW7u_!)fƌᗰAO:t#|!~i.>¡Cz"¡C%¡C(¡CZ+¡C.¡C1¡C:4¡C7¡Cz:¡C=¡CK!p萺D8tHQ":dRN%eC5¡CH!%pG8tHr":$"VɩTC$T-¡C)!ԌpC&tȥDr>K\D+2!s 2O!<2_C^'Wp ΡCN4:ΤCNd:(ΧC%tDx$^HC":;"\G$¥tȿDBtȟDC"0:#!p:Dt؜D %'E p;:F;a+"ܔt؄D;'tXsaa"LCU0$dtXa1"LI0+!tXsa"LOى&"taR",E#‚tka"",KY2 t?֧͉L]p["lD{a/:ܐnDؑ"¦tM5@0a02CO:"B~a'Bb"$Bn2"[:\CܣD:yL!OuDȳtxr Bt"0%BD"$"B"%:|y_$B&+D:$B.D :C,utxYJ! @!1t "$ Cy"d ;!hۡHE^v(BӭCVMPI"dk:!+ߡIv"$4v(B2)١I^"$b*u(B*ӡIF"$w(B*Hݡ)"o"R2v(BIס)(W"Dt(B*Kѡ)nEH}w(BZعCŶFPa":!mաijEH_t(BZۡC]x"Ecv(Bա᷐EYߡnX١eC= :!eܠO:мUUAǝ?qP]lOy'<h/XbM?2I/B,0 LS,0 LS,0 LS,0 LS,0 LS,0 LS,0 LS,0 LS,0 LS,6IENDB`pyformex-0.8.6/pyformex/doc/html/_images/helix-002.png0000644000211500021150000001352411300456345022344 0ustar benebene00000000000000PNG  IHDR ,O@m pHYsMMHhIDATxQr嶖DS ?&N:^td_\ )&Ȣ_׫0;nX1&c`L0 `2d ,X1&c`L0 `2d ,X1&c`L0 `2d ,X1&c`L0 `2d ,X1&c`L0 `2d ,X1&c`L0 `2d ,X1&c`L0ȁu1buxuwxq5E,q-binCX_:Ar\#~q!T-=@VUqXIAb}ן$n'w1|b vc@OdQ:*Z)9bJNb!E"x`UԵO\8RbJ[ VE] oQe#.%EL!~`UԵO\8RbJ[vXu-iD=q}6Xu-iD=qqXu-iD=qeXu-iD=qYvXu-iD=qM6Xu-iD=qAXu-iD=q5Xu-iD=q)vXu-iD=q6Xu-iD=q~^3q9ΡD??ywu=[<1%'CLBkM#jx!D`UQ›F>1%'CL> 4)9bJOg`UԵO@LISr~"V7}bJNbqYvXu-iD/Sr;ĔkkM#jx!D\=VE] oQ Ĕ1%'jl;*Zxӈ'^ $v)9?bUQ›F>1%'CLuzӞ+jɢ,jY~^3qy>w/p?TTT#?ϻMc9X9.`ց 8`EYڟz寻 rTǠɉuՉzĔCQ+?Dhg*^'?'dKSr;Ĕ1%'CLəHk"$v)9bJNb3UQ"ELISr;Ĕ1%gE!$v)9bJ VE]1%'CLISr;Ĕ)0*Z)9bJNb!`UQ"ELISr;Ĕ1%3*Z)9bJNb!\VE]1%'CLISr;Ĕ+k"$v)9bJNbsY6Xu-RĔ1%'CLISrɞE!$v)9bJv`UԵHSr;Ĕ1%'CLɹ;)bJNb!$v)9bUQC΋ĸ'QOdQzݝ=ΐ}7?Lğ*Q *Q *1;D~:QbG׋)]hCLBbJ~uwAG7X x2OdQO~&_>a^տy2OdQOo?́vۋ)9bJNb!^3ԁU9 bJNb!$v6ʹOSr;Ĕ1%'C~ {`U-|!$v)9'V')9bJNb!XrnĔ1%'CLIm&rnĔ1%'CLIm>rnĔ1%'CLImfU9 bJNb!$v6[1%'CLISr;D V')9bJNb!X[1%'CLISr;DkہU9 bJNb!$vX;ʹOSr;Ĕ1%'C||`U-\su1'__ qu>q{DLGOFOF?qw'x/Xs>Q΋Ocģ;0t8H/B?ڟ~A i?/X_[%i'CG~/1ri'dD\8RbJNb!'u`Ue"sq)1%'CLIV\&:Sr;Ĕ +{`Ue"sq)1%'CLIXr.qK)9bJNba`Ue"sq)1%'CLI7X ʹLu#.%$v)9kU9qĥĔ1%'Crcm5*29!$vmV\&:Sr;ĔqD\8RbJNb!XsG\JLISr;m6ֶr.qK)9bJNby`Ue"sq)1%'CLI7XʹLun+ub\ڟ(ubzQg^xeNğIj<'G' 1sT3/kLz׉h~gB% Ho &tqVⰘbXbB׋a%D~#}o9]hӅ9]hC#s`E t1%'CLISr,RVE t1%'CLISr VE t1%'CLISrVE t1%'CLISrVE t1%'CLISra`Uԝ NSr;Ĕ1%'I\MVE t1%'CLISrĕg`Uԝ NSr;Ĕ1%'I\VE t1%'CLISr5m`Uԝ NSr;Ĕ1%'I\ VE t1%'CLISrs`Uԝ NSr;Ĕ1%'I\mVE t1%'CLISruy`Uԝ NSr;Ĕ1%'I\VE݉:ob\ڟ(ƕЍzݝ=<ޯ)Ϊ:߼Rcַ{01w)hCLA?qCڝ~:G-b>t&iD>>iD>ViD>niD>ȆiD>ǞiD>ŶiD>iD>iD>~Toڻ? -?SE/(n_ qU~M7pYU7cL9΁w#bӡڟ*j)ӡ%(~:G9.p=c0taQ?gqaQ?~(_~u@|ߺ^1%'CL1Vyӈ'^"$v)9"&iD/Sr;ĔgU4)9bJ·+*oQKĔ1%Cem`7}%bJNb!l8ʛF>1%'CLqAXM#jx!|ODLISr>D\Vyӈ'^"$v)9"iD/Sr;ĔaUKswŻne? -?SE;{Gy_SUq[Gc'uV/>F^t?[~b}Pbv'_םOWvqk&Qso]# V}﯎+[bؓy'b(_bd jɢ,jA#u`UεHISr;Ĕ1%gEk!$v)9bJ,1U9"%'CLISr;ĔYb rEJNb!$v)9vXs-Rr;Ĕ1%'CLə%&rEJNb!$v)9g`UεHISr;Ĕ1%g2[ ʹ)9bJNb!eU9"%'CLISr;ĔYl8*Z$v)9bJNb3K\=V\1%'CLISrfk!$v)9bJ,q)vXs-Rr;Ĕ1%'CLə%rEJNb!$v)9E`l2[HGKdQ(n_ qU~MuYU=up"VqA,q-binsĺ/Xأ>A^O y_gDԏN*8.j帠GH_Sr;Ĕ1%'CLyJQOdQ:*ZK'.{q)1%-b7}Sr"=*Zxӈ'.{q)1%-b 7}Sr"FkM#jqĥĔkM#jqĥĔkM#jqĥĔ+kM#jqĥĔ˲kM#jqĥĔkkM#jqĥĔ kM#jqĥĔkM#jqĥĔKkM#jqĥĔkM#jqĥĔzݝ=Ρ}u$O8ϻM[qc' Gףu:b"y_G_T7>BOFOFkb`L&Wc`L0 `2d ,X1&c`L0 `2d ,X1&c`L0 `2d ,X1&c`L0 `2d ,X1&c`L0 `2d ,X1&c`L0 `2d ,X1&c`L0 `2 GIENDB`pyformex-0.8.6/pyformex/doc/html/_images/smoothwire.png0000644000211500021150000000074211277771572023151 0ustar benebene00000000000000PNG  IHDR #ꦷbKGD X pHYsHHFk> vpAg mIDAThQ )0x5 WJK!ŒQ!|B5ac>{sc1z^fІϵ,,`5mX *C9hp.0޺a.c@ =7@`q5mE00 뺮%r`Rp\؃}LgbHAx4)0%b S$..! Y[ Iq|L,PD4K^\ P>@K $jqe51 q5%AhW` ¸M(ad>@*ch) Ņ PFIENDB`pyformex-0.8.6/pyformex/doc/html/_images/wirestent-steps.png0000644000211500021150000002416711244741263024124 0ustar benebene00000000000000PNG  IHDR[ pHYsMMHh vpAgc=(IDATxݶ32p`hs  }9鍱{y}8ܥg}1jn&+Fu}WQ+FuMWTM5΀]1:Spq V^z@-ό3FWT}\KEqctǼN U7pg שr\Wq\ |ju%+jW}f0<>V^І3cw %V^Вf3 Hctwjil>3ȀxFWp7Vg^Ъ&3 ct%yz@ 2;+W#W ^]AZq g`tj]د舽VWWW}f%cZ^#"e[Rmﯗ=_;WB+zKV]}zz!:d01nt-*GNVz u gε\=?QW}#V]}z(>__w}_nGs=xLӹ!TVhy ϭB}h]dn5W+׫k-cܳivׂ{k\Y!g=~1Y3cWox?nS¹Os-֏ 1p;`4FWг^\#'e[uuQ;אָr]]WvƨG4M_O?_>ژzǮR>fv XjzuuiciZsYa=SWW>oǔG+̣3>wJ}Yγ]]=fǪG|20D5ݗۘ=~۔mGǿ>L]]L};U~}8PpQ usDUc먫C]T~̎ݾd•8^\ʛ[A]]=G{~~ Bܶ+T%?o;N=W%b u𥉉U->d\^O=TzoY*Umqzvz?R&pd9]yMq4wuiϮz+u[믯WYuv{r]]jzUy14MUWWx'۬n_?*YK]AnGFRo Xqwqq|_z_Q]]﫧׿p\?ZA]=W9#ԃ論9aWw>׿vǮV??]Oe~ cXW^Ky= V}B]}gk=v3ghůkY^,čl^Gjcsuuu_[W8O8^yj2y<1=۪O4ǾN\\l=fuuz۳hZ+Y?f~^yjM[<ﭯK^~z+ 눢D=~VOߩ__9F= 2gʫU=ž<~^k}BT}U0uuuܶ3"ʵǐg+Uow,YqVȽ bOoFP&W^Z~] Ϝ{SWWW_ur}o>4M_O?f>Z=n=vvK0p/ nF@+^y*g?M?4!?m=uuuzLy^\w6 w+R_(Oz:?R"5lzx=oWX1-CۑT1(3ʵ?_aζ~]jL=tuuuzSʏݾ]zG@+_Z5=w򭠮K{[?zMY!nVvwggh߷URSʫU){>?XztS&ϚzJ}T}{q۪X?~LZQ׳iwuiϝm_c_A]]zQWWO 60ZʫU {zaS WTWWY<tϽR^O{ mW#?g$y?(Jߟ6ً@+Z^z3uAnz﫫Կp\?ZAuuuuE@Vp17 D]]]]FhhVPWWWWﻞ)ӤA=XhE?+V,,g^R]]]]+l\hVPWWWW?)z. 3 ]+V)}6 )ۮv_=_o^?BOQWWWﭾlswvTwקl?R}iR+@+F^8sZvwWWWWWomuTމF:@;ƺ^y~l*euuuu񯰿VHvV_eowyޡ7=ЪѯW?4G{x~Eʶ#Chuuuۥ\{^]}RWo7;'?}0ZzGzK9BU߫PW\{$XOG_ G\0^1)۪]aB]]]]v\~W=;/gq/^SWŮݓ_BRWWz#ex=obүS;VPWW㞫o{:BܶWvѨyV}[ Qbw1(MӤD]}z O}O-}>}ÝFW]n+z?cmGۜMǬ~~[|Ý/L>rp;ia:+uusg_aC{;Q hc%zz1n}u5A]]]]z{7 ncnFc9ϴ[;F~3O]]]z\_;Z!~)#+\Ǯbzw]52uӟ?^mcircH}S꟯-VȽկu~ ?72$u<g9ozvm1G9K?8zgk`3L9NVGD]]zSWʏMն |]gNq}}?|=z//~ϳp۪_]Oe]LcdI׫z|^gR7VP{=Ly~cXJ}T=|cW/Wc=lUo>z?RGͣzq f§iό+u۪^__;v\W=g=~.~oU7 |j^A ו]m?pEucPS~Y]]=vjs~H | )HY|}XW=W9f>; 9V+篿z_:wԏn}SWWWW|/ S7޽p{Ê۾Z۩S?^z{Bʶs#ej{Z y>""ߛ˒$UWWW>oYOx\?{>MӤ^G==UOv{>0cVY_qKK8uV󹌾Ǩ-3 eOѺ̊9RUW/[_o~~~zSUW7eto 3a+8o|5V쟕Ѷ+uu_g U?7v^O~U7ܛctuSYǐsjo8}~ηSqVȽ{ ]wGn}ɮWzNmo;FQU?w=TWWW?[-n-n4~o1~=TR&]W)>uɮW4'1J yuuu-ڭ/g-uuuur׷w(H]^}9s6e[uuuuFX]]]]_i{fӇG~Çwf#11:[u{N]z*?RBFWel9{$^Aw|+q}}7pѻ)Ʃ wV]z*]L;}Hv>ksCV}-~1~MV7.ǬQ]?z!ۆ#e*atU# }}$;364gե-m{//=R=u%Vhuuuu{qFW'fВ|Gؙv)+Y<Ͻ2RvڌY<|*ǯz}5Rsz?&UK<[ :zV~A;c}E>￯^k׽[^L. ܋ g>Ɏz(i |Կz;uuu;++l~|ÏU۶ɮWPsb_y-mWqcWHٶzdz{^zq;r=]؝>|?]gRV]]]v1 /e; 4{)r$Ci-a8n 9az̹M_z=?>VzDďX 5P|>8z9~k>mǮ~ycyS{> J=OӼzկoga$Ż?aRWd1X>߳m4Mzkz({z۳b[?2|Vo1j瘣:dyslV]]\hzm1+V]z(p?P#LiyWd޹khymcJ[տG+o{cW~.W5zl=zup?=#Α x=ֿrϯb}u5[]]]]z{7ܣ1j{}R\{r$=3zQBe^ Ǭ8[ծ~W /xGzz\sG3pdNN5#7M?32ϵuRAzwַ#~UZ}{̜9JӏؔK>׷+R2ڹ#|JMdm9ozvqg}ǼWWW>:VGFZZ?#^g 螨YwH]FFm{m?8z#׋ᅮowsuuw_ۇW~y0=Ju~gmկ2pw1݉Q1 ΘPRp=VsϚzSGWwmmK43Z٪~n{}~zc&~\Ɀϭ~}p Q^Z^9zθO1_Q1~5B]T}:~jnb=*n|^3BJsz芑9Ri(hC@{gܯ[~U۪oW1GU'g_qCqۦS]{<Ǯz=|K/gyewc^ru}P>芑9{139#_ό׭RVPWWWcûq1#0uuu:0G|<{p;?z_[[W]]]]] m5wЮTF=q VFus:Z Y*chKw+8fdY׫@+]A#r\p knntY*?0PFW\ 3}@m*],q*1 ԣځ2Uya܍ #xWuq eU5p7,ø^1 R bdU܍^F\ `z.G/p"w+hϨK׫zw+ w+hHQ{oMI[FWO;P'c؞yv+2u2 w20p  w20p  w20p  w20p FN\IENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentD32L40d22n10b25.png0000644000211500021150000010153211247035000024535 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?i IDATx֣ҨaX{9rCo 1WwG-$T Qcovmsz!B 腜 9'rN@/^9sz!B 腜 9'rN0ֳ#ov16y9@vi̼8sn&&<9'l"1`!-Ps:d0rNPi;L'=$M@2rNಓJrlhDzJ7; 1K#CgnhZ)QJw3)u9U4yn6s_hc -j'B#쓳0c 苹D30zcn-m7fYs@MtҔΥ2!&ss Q|)QIR aݴs` 07}}ASsNjJ9G. Vieu Fs0'HN۲OH#ys0'e[/[<-v.6G Xvt11!`3tB (N3|BMpE @LZS-*`toFiKCi.LZz CWDZ1fNx(jWT*Ns":'M]t)7fH!ˆ+ P;&9y[ꌧy}jK+M't߫I7@C9Ifb復vr:(T my]6qúHjvzm~9%9hQDV߿[gԔ+j@e ;r̓Twv ޷h`,(i3O٘*amL>>ƝH gm؟zK٦VF+#'>c̶-1NOT\h6/Raxi2Q|jyx0o9Pj'ϫe1 8T_D:'`Y Ο4تDpv?<<t?/w HPpcsۜ1JJFTgC6v4Spc+ۚ#+m\rSˮx=#} ;mtJ XnySNݡV%_7cK\S&Zy.ԥd~NTei6; =ξ? gy!%`o9"6}aԌۮ3iUvrɿѬҟb !G9E(B 3L f;3Ϊ#/>|_.Z)e^_ɋ~^}w`=b y^Z9g%r "< Kt,i'2 7sG缄O2%X7r{) 9'`{Y%q; Y7y[+rO&ZWX|f~Դ"g+q~{^OaQ1}Qj'-Oˈg; ~rMs$)ھq:;!==U9bieD[-z @rN@g`VuPnXcVgDKus,=Ѝs]!1uƦ֪ySW f)&Q _ dx>'}wy]jg.&r%cj0 ]jiGܥ ~*)9MiCe3RNj`TE;*\K8x6rN@;Qf8慉fW[NVƳX"ٸZ(6/Ҍ<s8GZ{F#h4aJ)_їZZ_gE3ߥy2x8 j͗ jY^:\%;%h *k/26}QxֆMs$û2Y(Wÿ܅'HVO"ޥN-0 whhHl37{anh}C8v^sǹCpqD}?2=dP uZ/972Ч).%k'ՑsR(m~{_ZO2vD=pW"\lm7TUZm+$<G2N</9gto5zo:]Ymk_f܁L~' Rl:''l{fB^Y/x7*]úE=4ZUDc)~dI_ג~0׿ jWSq=籅"F UM2 @rNEQ?L.#E‹OF)LWp6*a2*ܤW%mn s1e]I9JR.r{ݢ*3ӐU6m.x* 4 +q+q3~`WcCႻIOiNvV~o}˥vRe Sr֔2L\;˻Z-ATAю?$ﶫF5-/ʖf KG=OMDw;Sa~wmcp RJbd&E,@6!,DŽˎutz+w\Sv@rN'>9Z1lQ5uݠ ~$UF0̖3ϧϧrN?u+l:Qic@73y }=~jO(ov#: k6/X&w ԺN{ƘYӓ,r*E(Aޮ9I}۔cJCZg姽kD,,9'̦9Vf"'y*r6vt>Y:+ SY#y>Cz`YyrxܤzӯlB.4Yw7_lw:'aW^:\0YU )l[9$9aN6:@&rN(iN1߷ͨ&OV0"O_J?<&6!gxHZ:J | SIwrfD|`R_<}fuzSM{Zk=s/Ys BDZ *k? {:ZVYs-h<!ᜢSZ}8#*yLCz: aS 딜 xϸ\ܵg)*DnU({f|QpEbDŽk+ Cvn(eId'BfF|N D ]G){D=;:Ur<7Y;:ȣɸSH)ps@7LZX2F=pi~im7H9Ύ2s8z,@+~T}޷yץؔvfi:wH?Mʣ^ ZД̈́BbR) v9[=Kow|1x#J s=%t^}c{>~ :PFBkп56zYNݛ\3^MN!>"E‹śv)b_ ^ 9vN3ߠWߜ~dsO! PP @z9J)_ӑg]\ J X-ySN˵ӛ @R7)%4)u4,g٩ut4in+[j% n>YD׫H(D `,/e wʗtS$Gs̕ВHhUs!YDȾڠXC2'Z,Ɯ[ +?K~<F}@K#%8ʟP@G9g *'9ĝ%jn`R^zRN׫ͦwubU!Is@Jsщhܟa;kB7v&60mAW߈~Ri uNU޴ӹfho RnKW"\iKݻY]LQo!ުD9 l>]psyK`R/J~O* }sfׂvԺzUf\[߻إwTY)[/lZ{:rVTZTOcNl󗍺7jVFX^ka";-/hv9'MG֊:J ÌlvSvHonDΙ:2Wxys)`n-\{B36[O )n6c%eyj+u%bm=ն0ɑ8ߕ[4~YY|Sʪ}4gŽˎ9;SyTrNˀ@-aԉeI>ٍ2S^ě9O筄&o+\j|NUgUsjA ˊ's 熑p. '?鮖ؗ:'|ՃWzNU]8or {dQZ:sY;g5l<噭@ ੎"K#Jo35|3ZCM~/-DgCB߼k}`n-GY&-rوL9M}sքw 3EL.0wg;?!UQ`O@\Neްn`ȵӻUY[(ORYx0rN:p2ؚ`DDӐ}&߭sgΞ8 JJv-q  f(`nVpZz0N䖠.WT܆JЩ\W޶mO9:*M-7عUl?K %3,6tlD)]¦?}' {jװ6TKg,#l '\(ǤW N9"4&ÚZK0D{|sn*Fy?! X9'xoڼglJ lVTG Ry< <S IFEnr>cj{=} vқ0l1}5=VJ`! qP o-<9'E_ω)t5 ѾY(/aꫣGm?ݿi]gx8u]K,!)͸b^$+8P(²vyeGm/K^i3Gj|{yNeOWxke10%՟^Z)ekp^21}L6dFU Ґ601OiNF+LR(*@}Nw~q%V*D?xNQsXM2q1Ñ |Dr6S6" uZeĩ*㝛Jںfyi,xV 'i ݄!̥̕(k~OeǗg}|lco)MԳD}nBq'i) 9'Eek|6`TrވPߊzףizGӎZgsZ F `wd]+]:"fhNrfaC|`WASJ M(oP!:mJi{@c9ȖUtr94C%VvB̅rȖ]5èSsxIgm/̯F&dpKYgE[zSxC^s;-f3c^cg*7 W|dic&j_'U6-G kEc '#9L]«ީNˋjO7ߏOB @ܻ7?G >*;`y[֝jHΙﵯoQHː^]z,;iI@&eW0ME,:o.47.PQBH֝]-{jd{-}69Hr\wY֩|?r^U>,83x|>glotvf_3u<$yk3ϟR@rNbS2gY CCQ#qTHy٭ݨN$U_}猤dLm<ϼcK~yTz<<9^KOЁA9̦u)Rfɀ9˒;?WlTEZșO{3HD `*T mZ(&uFb;?Fij鵕ZN5Oɖ9ٗg\o}"gx5.?5sNǪϙ?]xNﭛ=Yk|sJkn &b1HuNwU;= VK*X5 )9YmY5o!5R\؂aUxrNΤMZ+e`]HaDzH&gY9uI>j]$k[i#*yr9p9_,l*}b_f@-v&HoZc0~),#:K$m+iYֵ| m~N܀.9fPܡ-~7gw o鄶[{FU|]nRNOFcBX }rԡ@MGگ绖:OC7|v2Bإƪ}ʢlOĞr.A9 ,l˖*'29㞻Ee"?FG1.|&!ПsC5NN_n%`iØx"Xy;yɕOh A 됰(]_n6IO;WIŲ$gk,N6~He6)Qw]mQ6s)l:#w*yO1LbH+&7%(g nnc +w'㬹'茜@kiM&vN,/p P370tj>qD굌ykG^6|#Ўͻ.T@K8-޽2cNtۨuE Rb ٪u^BS%%'=RT]jhF\ ]!1' dKsH`\]= #1QCw0*'Hߊz{@eik7O1j,0rNwr*:"*3e͙16+&9yԀscbQ SqVG]Fc LD ,y?0bdRX$(ۼhBhIWs3Z =YqfF әFsW*ean!"js.2;K?4>exN/UiɋQwj͚6hƆFWpa#@9'ޕڦՙo_v"r-;qmŻIڿ=yÛU\R 2_ϡa͘ijUI>/!Jc[֟^' -{l3EęIM{, }CL]:{3x4{;v\4?Ǫ’yq"V.FI}D rNьx(u\wZ g(@R3rI>SVg߾:|_;#JkV杏i?xPӏ;E@911HRoDxh O|{G?9GRIRh\u6j2ðk $.( 9֭rO\rqf0ݻ։0FimRߺy{Oqj|ާ^j.랜L0Yuq8(6Cx/he"?({fM*rI&s!5.F5luIsS\BWL v>Ðs3.68Ø<1`cL晼|֧^V } rͬP mxJPyhj !5ɒFiFYsEF7XkhlsMiÝXE? ^EN=z@Y=}S=&y#,ѐ mEX\6K}wWU: 9՗cYT9m`kpR֘]%][5 !H[\?I@G9-x nw.KܻU(Z{:UVIMX;vﮏ?)+#֊E|t:o /rN`MH=$KyRbS >:wWFfTeBmIgkZ-3}4nDVhgYcHF=fN^7ؔ6ix*^_P߁ooLwڨz64mm%Kď@)%패m~nI?bIxckmIpÐG3z9#qm/ +=1O|{?nw; :Y5 XK"}"5߲`v/ؾ7uN@;L,V(テQy6:] nPLiBir"~K.ק1y:dUOK;V"V@ H-C~`ny,vHD]=g.O\ʠys&ɨ g1P5 ӶZO++3OkH۩~68P6L3y@zMf96̝oSڑ;u}f^$K!d8Slx6/lB͹L>7D-LE$OANryWm^B3kHjۓ864l;oSYCJ<ӷ['SPJ?tsV}%NڐU~sVu`)TrVW!uOQZޫ oƉ@@D2?b6+dx秗 ZK\EH;QxV 0MU.< qFcɔw8Uf;v{'M-ۣ3n{; Dc[C/o:7?sӈ5ӏ;iTW!/]j򯖡7]J(x8m3zU9{M fmWߘY>.WGwą`m?8,ݮ$a_:!rN`8RUYF\YmY{iIM>[ulqj75HR jOXj?z¹~N`,++Lg䜅yOצr7w$J]^wkyPr|G(ӻ֣oCy!K T4)T~nuIWaȸ2UQ5,EEAcV"ґsl8dc0yyٕt. 6u*fܖW){!'L;$BxR5z-s#omٛТoiCYߟ@rN2!Z-58t+sҰӘ[eߕ28/ɑ;N$VMVkg;!:kzQ#qre̛FUXQ(Nnt閎iAZ m :h$mڽԩrO2a=/HٗѸgb-Swj 9'0ӄIf;5'Ԗ|cLjt+4z.晘ةDٿrs=h; BO׹M;hp(x #{q2潩&d~eSRƛ9'T~w~"-ZdHϬ旜qXk tRo~cZw=nt`Y@;j_Jc%&ޝAlǐwy}#UX"Hdzy[ 9߃cd7;VxG""`e@vEZ+| Zz]眶biHSU 9Uڇ"iYT|sN c|9{uf IDAT|@_DF9ϣ .9r΄}̌KGx T#f9&,T3f}=\`_B"`] HRJ+ʌjv;R=>> uꎻu%iY1R`MV ڵN7N9'P皽c,xϻ%SϡoNZ⡤eYd"p7_c$T ;= o1QJ1!!09'Fb_ߑiVkT<(5Tr&&A74s L dZ2|>^8;նAj9IZ;y_;Z}xu=cIպu+@*rN@6_y2~];^iNx5N;WKd0Z eԒ{EȴcKW(oq_c dʸw8ON-g{t{BziȎ{;h-{I=\$!B C, n${ yrP|[4*i+TWڗ.5OW˴$wE˿\nmx'j@ %+[o̻|JU]rbs+ vU8KHF HSS[Kgg%Z Q0 tT^?rɲG+lg0 L;mɯ3oiGBοƟ͆M~In٧Rn鼖%/C=ՑL>iu%۫Tt3aė' 9'3i1BU#k&5eF݌.xB&ٯh9*Ҥ;6[I]NP|]dHҕaVl^A|>,ʼH3.؜[o<m)73RKs7;_;bꜿ1\#J(5'+zugN]WQ vږ7QwDΞ{DY9H%٦HP%/m8PͶ7~ GKa+4QC'V@FxS{nT"۝v E ,IƯȤ{6 IPzӻUw3|w*H@6UN^[z#j&[f(J&m;d8]vdd7;VOъ:'oVӺ?Hr+ Mg+sA[Ne ,~z'߮ejDoܣУ<] 1,lڥ#v qx#Éټv URu4J˾(r t6[n iq46"%k<,UP峻32$~kxnE idsƪǹSm+Ι v =#nMX liTA#_nV3ITطCpK>x[q>~T|duR H@ l!0rF%].Uͳ:%$4qQf!Hms2CiX͉h ,U; t$_-3Ih|*Lu$U)SQ 9)¦Y+ 9'%=m ѲIN"F'r i>쨎+apVvb~5HI89xV ȯcue귺?GZ GQT~F;uFmᦴw#6TXՠKS2ta!5"[P{ķӄ;|z3~Z9 ' 9' 6n}$j04iV*2Nb{^ .>E ' 9' @65o њ˺^O RceT+RO7mXk]:$a#t3C3bsgӺh?%+e~n2rbi67, 9k]߇L63u*]joٽYlzMg{oy'J(3B E $KGۻ Gߑjl75IJ]Bs u 潦N ibiENMyX 9'PB~-rȨ?=微9#кF $^ >>S'LkM`E@]kڨ#ҭSp\v|4Vpʑy*u<ãzHXwbvyv^PA sG 4n>*f|a&B9ߺSsUD@دEW,|EҶ먆 %gmQo1aG$֐7)K;%VA Ԓ=Ȳ] Q_kV7uȮRg_-=޵FWi`0gI/A&mď5h]~_MqU[14؇>5nYRWJ+O 2'_⢾P<ڼN2GvX5GMvJi_$=H,d9 ) W9y_nuq~nG<ԥU418.kmwPʚwp6+̘E J!9cx k\Ж{oS-sf|n2sm<skF>_Cq=)\MJ-hhcܣ猍UGT vl܈^Ϲ c zI>RODzx) Q(r!7&8R_t[FfnE\G3ύZNP;uw|Og(ΟYij+u:٦s3%D 0rFq#!!ަUusNŧBr `H٥:)JWcޥNKsuJ6mDGà N2ѹy}Ajbr"5n)4sJ^9g)TG shl}l/wee1J)Q7Ir0x'zF+9ΤB ر_ڻ śVI8(rN`ê&n]7ݒv=(3I˃jsR20Oxwmcq`b[OP4{vfۅv]bTi] 9CyƼ!D-M9L b$׭ݺUyvpRzխvʺMaLIy@Yϟԛk^ gkv_$ϖr4w{@+9l@s䜀t{= SX|Ֆ̙ς3d𢵞 'z-rN¨o N%x䖬 ݓ39y.tMkϡ9gQU m/g'95SjcT?Xs4=rM>;+?;eqM;Yq'l>VR&02͑f5Cfѱv>|ȏէMЂC[/9FnK +rN`1߱WCKMU%ETT &<(e^%B$z#r#d>0;{nm}6 :Kq9ǘcf~%I/C}ى 6ܼ4]?9n-%C:'*Å 巹O%M9޸5VƢi4!T*r) 1;GC3c\iLovjiу=!Mr퇉=~koa ;*?BmFgγq~)yQn3q̧ uN`K[r-M/ 2ׂxjR .w+ufaV&:p&7I8̹R^挱QU.bI-mowޑ1.ֱ]Y!_p΁wYmmPɘ[ `4ISL;g|7 L4djnjpcʷGIK)6LGXe2'f1*fxevU>э!S4ܜӮ^f&~KuRx9',Xc0W:LO&6}3ݱ9-90٫,=3G `Í#] #k1v*M?ts=ള$W?TK8XyӜ,Ym{>-lsYRU{ &ŁmG$wNK 6$~ՋQZzdmvݷ+yZr|:oW9#4iq4n»~jbn-[}F1B#BEISmb?w16{Lt(6U M.Y;JgZ\J)ͱmjki;N]T+7dpB@!-}OKN2s;zc?LPgtuEwvټczey*F kפo"gϡZsJ{m];b϶}4;pϲ;gP({>C_**޶= ׻)ox'5vk ygƼc;og/u:_(&մ7˵IyPb IDATZ9]i~$"}߿U{g^N?i1'<*KK;}zȣo(oX9' kh Ak M g~Z0%.K |~Yg++ⵜ%p iڜ4uKR05ӗf^QCV%p.~ٹL)ƴzuB35J,5䟭e^mxYoa`&5>5Fim_'<skz<ۀA3KGUB\?9/3$֍yO̝\x!WOm ۶EԽw}¦B9'eǾu/,$5xO M>sLrKw[jeq?'F?vbfjKn> ȤZ*ܰl{M#z=rN?\u/xٿ2ul|o,2ƬrFfpΗssy9'}owVx*<'LJM2@{ns:@ #ܷcwNesY3ݮBgQ褄~> =rN~_VQyjjwl_5zG&Uq&SICN_<e? [:bvSmiNx޲[qG;ʙU$3ʞΘ!fh5f@:cI uvГAJ: Yáv6F~Х3C,sqL6NnÝnR^4}S7i,tm_]](r `K9$>yd7W]! űu+SmN+PGH%n|ZOF #2;G'yTS%^9.ڣ\IDZ]Wj4lBH8<skYfm9~Z_uVS;m̡4,w")Zϝ3s8>-n߆cʌS RhG "?,}|!l&nWM 4z0 ƐRkq~N54PZ%j}d濛0Wd˿J~fB*<u9n6 eIZO CmdEb\&g۴3d%p"V&ad}FDc`/e9)79S^PvJ %۴9eUCp@"rN`eFJ),bCrN{C[\Mi?igVz8 tR:{ҰYX uIJ0;*"2hB,? sh(^Zmq'C/  էM"W\D"/+5,4"$YHM]y/kQ3'gΰݦ59cA,x$3.{KP&1ŷ+/ӜH~_콡u94;8 kO'\?i<7X t"]d@/R=pP.{iVF<~Mξz])u\<'9q Bٸp@'䜀]յ7ςR5mX~'ׇ@o䜀,Ƭ]T @<, yV+q ̊gd|9g 9' GB)Ϊ;+<;eq~3L׮٤ycm 'F H1~4?{<ۄS]'YEgQ褄~> 9dfb,1-\>y.?)dx*̘+O;oIe97~a!}[=>P~3~m'ÌT5#PK>tٮu+Fu ]^w;L勶OBVqrySSw gEC4n2.èQ٥:U7"އJxΩ:6!)096$l%=d))qgń+vfW1D!v8\s!|dc$iќ~9g#nN'쒒Gߜ=B ,OklN[9#c?dSjv~S'!os[1FPUcysyn˘⯍RKNvOy?) MXڿũvN cuQCK}Flsջ-p]׼]jЗ3[to,9/x)/zj&ZϬ."7Oן'ӒpB9]G,,ds2O9s(z?FhpO`Cow6'MXJ6rWyz)R{xiX R;,c\cXu6~mU}7) :' +0YZVv3l,P֛J]dǐusbϳe`x~'\xέ?[ϴa"+̹Qژm2sOD>Ab:$#թ'qܾ\p~9d& ~S'Gyur]6f{=$+rNg;yN2*%L(һ=$1rNCZp..ݴn .N9Dҹ0z+wk9T ")xzI<Lڋ,u;.4)r:9\' X0DJ湄vеy³Sge:asg;6߶pspg+=,ci'S(fƝ^yZ{C%MTgNx S쁺}{,<21*{?Z?K3Tռݜw <6|i&6&JNaKcsF'F9kk8k!3M8`?m>߳B{%D0@sbws>rٞu=9Og7%(9:MT ۰M Il/1IV꽲~%1FimT$;̭6ճ"Y;\-[ ~WgrbsƎ5dr{ֲI^3#ORu :'lvS$S.rU-(F}l&%qiRsD~[>(@9M l ³!+6_ [ǂekI=DbBtQd h11=OVtNt%YZ윹hʣ`DJ=M>&ӰQUn 9Wb3v2gE_W9z'[ \uxm!\_g(5Umo}7k{Ib/Xt@];Z[ #kXG6\aѯ|ZNȜmF)'hYB/s{N_Xgν9;Zv5j{/|v:'vE}Q8$sj[T>wnQ{pO?<<~vNVȜP?nx>}zk᳘"K;9mө;852'n4>Ww,VZ{bmIe~/eOgkko U+Sa=k amj뼰sZl fPw͞>"9OnR\#+>$g<}o*:}g)pp |NUm <6&#|.l;x"jv[wqaiF68i4J[(ƨq\S]ZG|Imi^oR pͯ.ӗ# 3'Ȝ&;y>+x> tG@'Ȝ@F;yV/ R'2s& ^)#)kh&,~,reuF#J65MۭZ,4S :}Ntz=vR.mZ5 sH,eFm݅ճutrK䱞gukuYc?K kuY|f8( 3M!`5V"ͥ{q"km~n9KϮlȤiJp]b}ő[ݹƼϛ+pyZ2'Pk?>}r= 7<>Xۀح5x777Pi[;8xtvmڛD״~ {\Sb!.y2xN@O(XKKݖ%Ήʟ;;6*Ȝ@Jk afڐ1l&ޣ%m_ W Eouٳ8C~*S`|NlQf Q]>Ѝdϡo8 t:O_O|zN*T[iifvX9n{t+g>: @9>r:9Sc":CZa`4]'F>wwedӻ n֕xGԵ;ɜ@ ͦ;iW >ϙ<j%s*M#M3`Զmפo3bxZY/g']Z!\7Mڴ0y\ }]:"'窽d'Q>rCC?lLg9hK(m˼ty2'pUJޗӡ UU4?)V=9NZsnU+ƪv{aG윹9EQ8PO l}12g˳z _qN8Jq~?p Z),~DRUZ<]M~4Æ3qZ8sPs9R|NвG5֭Jd?y+uv&} ;sZ\mH:.;i念^i9͏ ܋[P iUl~=cdN K"nx[ݑiRJMjC#_xuk0,u ְo4^ p /3H$hmiy D6PÅߗMĤ~gٟPݦ%ȃ QRJmj};sB;Ҧy'PB;Žo~""P}N #-Qrsu&>']FzZ[a3sí^@FHVrKR@U9Vܧ]O@n ۧ;7 7WٹU x3wƌZNuotO˨>'P}CعrKm e9zϜ_?m9\j@Ŭ!PmIDAT@:S8 .Z-.R8d2'@HGbJP,9JUMЉ <'@پ4)&6 (}NU89'5ifdӤvSp9WMӶoḾWy@8sQٷfd[s𐾃44q}N8ku=wu!3ƽ$Oj Y>'@4aGN'm< f8[ PVtٚU9_=QsT^ssmy%ɜa=;ӢNNO+Qs%O dNmM𝌭- s~79;{ $wtWO iRJmJ)șs ɜaj]>M's<%}kפ|'0$s|ԕڟRjRlYCࣆ3_S%Imo p P.(?O@ \dNziuO9Nx PbZ`[ "s @.2'Ȝ"s @.2'Ȝ"s @.2'Ȝ"s @.2'^IENDB`pyformex-0.8.6/pyformex/doc/html/_images/wirestent.png0000644000211500021150000006576111244741263022775 0ustar benebene00000000000000PNG  IHDR[]1 IDATx*^_Y8II%[O'6Zh> d'>(=@O=a4wvUu,%'"y@u‚eJXL!,F$ ׳@̻)}xe0S2♸}%k{&aU;Pz߁Λ(B^Stۯn*nhe7|YTHʮ,y$=.{ *K.B$I]ʱ?MK>:Pzpsω+wEɕQ~jnwYEJP%1 IZp]y;Cn&]_8M#5K_oxc=r}ּv< wzQ)xGJvr9q\mr}bd}4KbPڬx3O VM|jYR 7E6s_ ?6p^zfb{$] P&ļW"<`ƹѤkŷY#"`x8>ʝu]YelR ǾL/j#Ji=lzfd7afbgRmkg[@6y ܬ8ɪ{O#2w_ECΈo煽تto~m''1N"N5#ٴۮdj2(K΁T%SyC}'cQ?盯f[Gzϥ鮷M+㛇`~Yz>} _K6rHJ9su]{ڐ^v !{2tכp(W[/J &3G9اҰS`S4v ƒʽO/6uk?m_}_\,WU7+t>v[Ne:ýN48:nZȍ\I7H,ݔ|SljES9%|8u_0qٲ)Tfoۮ>.W<Ꜷm9zVnKJfzq;1\Ȓ6M[/?`CdG}OoR>{W+\T^hlaXzuM_Wۄ>no1xؤzuԫ[ ޮߘH*R_5.՘^)*czgs?K'7 8^ pW?ymPDT/f >Q!ۼz߅[ڗwW9Epq :;ټ=:y3W?Q,Kp<|rԙ^}{XU|:c;ONSscw.7q۷΄4:ZUnW Qo^8;Ԯ|>۠71+Eu *g~Z>/ ˩{_!﫧.-IRcoF7$ߦSqe2C"LMNS鶻~d0=WX=!uW[]]Jk)DD"D…‹-$tabͶĦk>:̴]Fn80\^:z_14b>'"n7/MQ.ouz{0~t8E޻~t',4ԋ.*Z? > +23O9UDvrmtT?a@nc3#Urj5OЍHX*~e종? ly(=}x]x)N/,1xjX|Sqַϩ~xu6/c>d&Msk۶ٗf睶(PzRnm;L喦5zE1L_iЅ27JcmYKW|pېAi+~JQ맳 z8hޘ3Od*ibɘ(@ׁU|ѹ7cI; :Q~*By߂5}8 .dt}8uXM|;䐷)*.9~,vy$]U쮯Cv?w&we2IH'^e]8`eNsG&Pf_}r!Z_4d2Ա[_tTn%uT!yuY g`o JOĤZ مd?{_NG&7,Vm"(=΁.bvS/~%]H| ^7Ɏr2NDa㨟3^lqui&ϔN9aii.Wwe{pwFе.ΖV~ݯ?UFW16gS{хmi#6p~*+w}myPzxqNO2<5;Ŀ~5{ [~بȬ^D'SrKfy(IQ}pjPݍ+#]l?*er6Ńa>-]uH~ J0cQqL oyP}c/L?Eg1dE8pnylRb,Jux\o[W[2ӆBrC4by])c$RiIUU H JFWp5t2qƑy]g] ƞIg_j? O~h~ӏ`ݷcLCd ˳ۆߥ K8n?I @W0-Bw9o#p捸SV V 5fVAa] 0-Eފu]-9UN}z{L |'(=,6jlUV786*ln|B6i_9Ӽe49!i5E:iۆ`1hWV[= Kٞ-P'(=nu+ٮmF ~o΍{8h"&K/M󥏽eW Ml;% p]:T4Cԑ7qz/zWj4-nx-" Leai-Qx/}QL $M 09FF[jH Z1OUD]5hr7J/"ym0>mGͲ:i[^Hh7c~l_bevKbpoӒQ!}?ﳽ?/E}u3 _󞕻=5'X5'u0Z$n%[fGE/ /al5<@y(n-IP2X16la,#m|sw}Jb`z{;ŵzp3=s, 9>xaŤD"ʳF*L@y`'}>/{w=<@F4}i4[uP~1I5jDl }޹e6[̶.{)):4}1qL77}cͷ'^5<xhLs!]fɕ-U-˰fhiE%S{bz)1Z*1~m9 2!iބ5Vԍcn-UIh5k0kKc]v។[zwr }IFdC&)Ao︫sz. 1^lI|6=\KocKEx|3L)]VM ?:ޣe[7ft+>MlLS3H qԶY'wikT5_: bʎL1C;w%i$b4cSBKH&''B쾴݅ 6Q}w.LCՋf1ճV_Zw^؟g!:ЛMe0AGkć䧒kWݫs<튊c)] ޚaQ]GUQ*7yvWCVz?vm6(=INovWIjMg׬q7c`Ħfjxu|^b_6ZW_eW3 :b&0qz;ðzm:r):;;JnB|(+xO.oFiv[~̰qOk q{O>MA]w՘}/~{&2bfG=PzWqbf[ Fl(y}g%lo0mj1 PiN9sSF|xbϞ/MEghw:jQw }]y2O|#FWӂ R0$eH"Tե%iDҳ֊GBDBv\68 hr"/yyvFΩXger/#ܑVt-!C=# 6=`n/#[Ի`W(9, cˌqN"3n|ORMїz#&_= ,YQnv>ZY0WQ\@_>갋x"؟zpp { G UɳP{m1kۑlĞRmTMQsPt׏^\exIVz>9 O˼[|4V`&ͧ6L.uwQkuE]^nօ};_q?!hF24Xg\kb5sgMޘ@xzIʔvUyU\C̖1 kڈ¼0܆K^7|o!)2wk,f<"TAU_hV=Wa- ެJ%E9Bڮ6ca9wl3Q_󺓛ODg> ?AzN&Qq3/iΪ3uҰl_b᳢ j ot&]պ=kʛ H#^]ۧ:}1n<>Q#&2OPzۑ!^LJpvYP̓ӣ+{/m%nJlaa,d,qgSn/ќk8Nus5+N`J|mƓkC(=2f3ٖ~urHSר<aVx!lAU'?;<<4ySm^} =.ċD ÜN0> f <4s&8sJygaϥ\iDZ[ډ 5>|45-nTCBFڏ@oOV r6%[fy9|f6-oӂ[kj+>?'5 cUxY&=(U_y鵖W`FS>Ӽ(=a&M=QNѥS_Y#x}MY4%a٢+Ch?qT2iOŰ-4z up/Qhp_}u2}JxKw$˼e&?kHjB0T(]ˆTw=S(uq8[;03x9K j툠/l:KG1{c}WVpM}m|ZF ڱ? ?_3~o 'b4WSPz04ֲ<~9HNSLqbqHWL0Tϗlce_ h6e,lei7,9ꝯy  (=_Uw [ Cg ʺ)BE@qb/׬<^ce/w3};㴾Y`qgИH{9Pz>G]xƅuA<^Bdz% 3z 6s鄟l_c((6XNQvϧ\uutm&t ȜhEMWG=au@9i6ʻAK-|k˄n[f޳4'ChKBܯ4 1c&cj=KEMCAZj5/1 [geޮ b*<2ևkC*"h{zP}kNWλ;*t{c^zb1|{:bvadK9X̃'~ntY/w9}V!My< C{__ ґ~Ȇi~+gfnʹ썙r.<<۶qoͫx,Z~("-OS9`TZ螺dk汙̲ ^E9nf5vs+m{+f{tdkA3'4\lJ)e&q  IDAT3eTHHHXfۗX]Zici)8y)CAH^~O{1x;a~ ˙x2ͩ lw>!:{6=&lv!2qH՛Z"kF仉sgNG54FG/n W0(.퇞;=ެk1W|6!l#,k1mƹ}34 'M³r/c4{];^\ǽ}9i&{Nb(_][gΉ#{ڸM x:;p7 (=V+7V!sYǑk홆3k[g̒ː/OI}KBRޚ7t$*TjyXen?MH_Ž@ͱ\JQbYOӳؿ͛Z8U9q"Fz6lڏ7m\ti~YT\NHj5wo+۴!y ?6= nذW)b]—M12_V Yq0 E@/ru#Jם꧙s.@üoWkxlP/4?ϧ|Vr= ߏ1`]}N] lFOX\H,;fr\oQsm2Wб (=``x*:GW.EBv6[N\9.h>x ^Rݑ gb!d/2U0b<7;ωa]:$?Z(=yg"#ΖԜj69n-) &LQzNxoĴPؾBOՊN?Te^5ə1/3fz4%9~</۷)7W63Vtm{I+9#m46ܴoBZn-|E+x caن)idj6q;|oZs /MuxEdpm Uxa?'>,0EV3F,+ךsnݝ(~2+7Ae_ y733:2!y&i6; (=_FnnB[aYoĽ')/vH(S3Mt9 ;;k(ャGĄy.Q(=_>йwձ`.5G8%?42Zk㾶Gn䮰s5 [̺>{Ҽ:X$"uE:7i NSoh]O+_X>*\[{NEМ$SFk-i2QYBiϳ|oOW xʥ.mME|/ {mDb^e4/@7ob}jp1pH.3 Ԕͻ;}_Teu!ʫwluJ1,Y}M[vXxTqv/r5t76Kȱj21yS S^tXxx;lۦͅ9%KuםJ?-nK{gф=٬)!oOVs:oB5 t.c<*Vmxw]w ̒-.QΠ4b_!|᳝C09(AH) 0WέӼݳ{]2_#-_6rNr+ۥGRe +ͻx LH>2}ruS|@f;EWuĩ:'V̱͐lm|RxN76OG/r%Vb#6Cx*P*-ori!2i de^-bao .0&__:ǥ' cuVUvp7Z|TY_pGqUaB|{N.ƞ3q4P?L)ra=q{Y E?p7dc#3>34!$OA?(= wNEjK\Lbɨ;~7; {$W3vyf|-;/},H !yi(=oF5,I8%Hugfe]=Y} /+Údz4mZ٭ƗfVP.b[]@3SD="6zqnvɧm]܂?Z= jq9yk߶V8H W1Ozx~oEL%uޠa8rwU_Ta/L 3X,x7KVnvT r~3fZuqq3hXW3gߋ'o][* ĬQ`_Qh<Ð4/NJ8-[[Q8w- ªpQfUkH~}{[l7)d4O%@x*rOt士~QiZSp}3se1 b9 , qX)t4ujfN,xۦ?B;B7?{]8TomzX40]kvaxd"6q;V J[v}%Z0y:L;d{ws29Mk`׃suJ5^nf"aѥlJ6z՗⎯lf5Ҳ}4_ (+L`]q7C}j $휠z0M/VP)Vu/v-;'w)0aL^ U_5eLaXr1nH|Ҽ-2taN)^byá`_aSu*cs(~"x@]ڞ2)m)ֶ{ߥx3#0w]ҩo0@X0BM1 Bya%| NJ6KOc>i C34!̸N$]y )P}EZݨ W/q8f qwdnSQfL<#cڻ \]/boCFI;`f%xp lzfM1Z$BZ6Wz ~Z`԰WO\[#fQLʛPH,s0[Գ& h#(aj6.|kjN&,^Ƃ~ ~]Os+e]b;x$ݜ #vVYDoT-Ix/՘ͯϪf^SV3kƜ\J ~^ƻt*"WWeGrߊo"nO2>ys$CocΏpOs>l$~d䁯i8xR˫/U=4To }XàUVsµ$7 LB9%mj~۔K2tt3hlEFG#1KN8M-x(uQ>0 LQ&6;c!zypZ٪ Ų|F)nL7^nɫ19.Df`?t}҃oe8޻;vwa"pCgk.a"YfC8b0|x"L?nU!!{vձ0Bj2JU[̼:qhM v/rߞKd Meݍ~}_sC?G󗣭 o!*خ1 M3UZ~(=>Ph 2L>@9- g9 3k#{3svwoX\3ΊD1_iZd0.2Ca <^(F7³o6[gX_Y,5*CzwnCۤܿw58"=խwjڝ  _i5s$jU{gر0Mo-z֗{1醃Obu/ͲjYtT6.MrR&5BTLmG_ $%jsB 5Pzqgb`@NwmY3Z8l)ݐ]Üc_Lbcqќk]vWaJz׃N+dO?(h$k6mUuotvXxnINQN;˒ΈIٗ_f$88zoY/̄;&@o{~Pld;Iۄ;BXRf*ޕ҉;zzC!`t9azf܂"anid' cJ>lPBbwyZ^Kɬc=W_̃3``h\9cϼgV|e?̵3]@8=lSJAǾ!UH|y/'z;V8sKC9/)[-l4 **p̼"Wتt_rxѼ=dށw6=x~예E/VN8<y4pJKڔjs_>w+&[`7lټ gv'\L\f]qcʹ/w==H.F!Bp#b"ܢƃw6= e|'=zC#Yvn0sfך. _z: f/FlFz$; wTw{,NHf…āzWqO7^ f1J/,\M઄ uݡ}R627ݸm(w7DWWf:y3:9,8(& ˻9Ka'_ K}:`=i1+.Mీ렶 #!h|mZo iw G;,3kէÕ 6BjP[V P)=-ȑaLe\-Kt.W]^> w9=5Ϭ##Ws1(=x-߶ڿVgҵOwroJn:yσɃ+ G #,鏹ܫ^y91[r# ˻- k^7{𯱶UP/:QLWug lGH_WhZ_^;4xQ![ݍMtv&#/!uaNUR} /h Rd"Us]tte-~Oz/{06%' zy&L_HzF7yѲ׎_ڸƶ| Q_:Rd^&t{,6b|=G:ve_`Shu>O3-c1ұKsHpg޹j}"$:1;@cxJ֓`}oʫEAn;2~4~=OrQL&p-##:V|f+ksbЩ$3+ՉԼp{f'<ÔO_3yu 1U3E}]xe}_'ޛWUxsڔ I1t+bH$!1s4+&"K~fRĎŋRu>(=1ƳxfSLH͔vܴҷqBV7/OaÞ,*^Iu|pE)l3<$jܒXh* O֕Y*p1--0|Qle1ɻxW6f/cTcn0iK҃GS7ڈ(q{Rb%\=[v HyLmWtXE{ﱥ+f IDAT>mYTxM!uIzϬ8oqzp;+8L` Ēwva{R th1EnCߛueܟwHo1j?aXw{|Pzpy2اBfdDSV?[mmn٧ztð.zjnoGVYXXT{Ⱥw\E7qk\C]1lm;}{!Pn̹m&;xE- 8,/_o~yеt̝K8 {uSӌd\amv }ք b|>`caރ*neSVAEZqt-ڸIwWfM̞s&4#mcJ.e "춁Kx-IiX6}/PW}ݾFQm7kx_ι[ѼNk_<[i$Sg,N*"҃V歓>si˻s{ٲEkN/ C`rtÎVLZ]ﺜauv33L j .# Kjnr)aô4ѻ*IU+!:ɷ{rcs .>/_P_W_{g:K|إfFt''5{دkTqIifYIb9k}GܵŖR'muqQ? <'bX N<}¹G#NwƯ}-P,ߪ0KX߁MgMDI}= ?O@6K]8G̫txݤkG~unpoK4ۆ;m:,jng% N)]7Qg5dϻC0P8llYX{%<V쓲@jHoM U͵BhV0Qbx[C`6_RE*[>Szd35Yi[듚1xr!3lFO8=>޽|@ʧ} k{*f|w"8zwCYVAΓ.r'^vw=^zk[_N= Wofiȶ27m '2Oz_pݭ?4w2y ^܈9Ø{r{e&ǿԷz?Dx g@s#>^Gi>˓+.nkf iƍ2~qs+[GJ3Z5Ї։[w.u>gMwQSkRTbfw7u.6g^kU`1AlphWZܼ7 HmcFPڸ>Bi`ӿ=⒕m1iw<Sl&U®p^Þ|лhU+{p~ѝy1;mz\@Rs7ٮhv~({cM8*hJμdn+sG2Ҽ9<,ˊ >nq9YۻT&Uп%_D]4 p.y~bx(R"gzԃi#`h,3UٍyE 7?9aUG@Ц v?~-~d6I~yjV\\xi-B]eU(;M]& vuɮH#eFDٵUY>r2OLg^-112ܑiFhUCwJ/Pק;荶^A\ y3e7EX)S7uCt~_u 7e}1H w?C3؎O\.Mq Z<߇Mz佩}Yvī8$)0uV{]]8L7eĺρ8/ºpݼP.Ǧ-y2>H'kUjΚ_y#rj5z{(~1Jp 麠u/!5H9Cx[`)c2ҟעqѮmF\G=o:D];}:\;EήJA_*<76"êX9xVڬ} _WOۓ(oR0\7rco4橪DHthoL{a8^Y?ـiy>0se߱ƕ؟}y0B#Sh[JsɽE] \@@_3ɳ׳)vnn"=rbRuť`a'侮9k7jiN糶z>@H _|T>nzyT'sڮ^T[V.};!y0gaKl:R {9ʧ>/aEDeLw#mku_'.D4GoŞo6oFvIoZϯ<4DVS}U~s`oO8{y}?y@dZdaa=V֘XJA|(hiGE Yʕnt5q o|wݥ9A(h'pѣ+C>JS _65v_yt{#'灴2*\>}ej2L⢅i 0ޭWi&aqV'g< ~`"Lʭm^۾1uqnêOs/?Vfr@jWkGf^; Ln}w;Wt2SJ@V*xx~dN4>uh?YLI^Lf$Ҩjwy AD3ӷTerTvsDKFA&>#/?@[RqŤ)F}yuN]Edp^}9APW5R-fe^6ƻً7 a~.Pj ?A\0>O|_oӛǞ5Jc1"$c_evݍ>2,Nid\Lш}~'fu0m/IʍGaOK '8sod($]dg4y]~EN+ rυmSt~cY;GW!1 |-ߢh|z~n(QSWc>8}\FŞԓO_ l&2ʢg]P~l^B^ƒc27Je?ޭsy?xR.rm{)][[\?,nl[qmxѸ(Ѐd?'S-;e듴%<sցLDL,,!/؎s,Re=y9[fzbOf=鐼`?!ۃ`كP|UUۣ8q  [U|қg{Ɗ73ml;uwc=mBI;xwk齸MU{y\4W[ِ)3:G2sWmsתAg)xW{ǘS'jӷЦ<Guz,zE8'c?92ק^+OQzguԓwBCvYxr9v/?0y/hMqO୚ڤO.s2\P|Euf}( r\R<߮;+g#+U?x|&@2l/0ơ,B K>Ŧo>NLf%C5׷ZKSkRMTü͢#n|2G_ZDDfcRMBϬ1F"[̏u⃔x߿Z\}пj+1 :{3Gt(سM=d0cի7C3 w '`ßWzc6\Y)gGSۻQݲ֙Ό`;Iz#9 6L~foC29Mo$~wgC畾?7(=C:5;}͑e;5H_utV{*=uqʜ;kp7>@էto₲t GUCs#~≾K:noҕ؇s4pBY]V]W]F+uhN@ͯ{ i~; ^i?S5r nW_7xrd.?̄ bM+o;n͙W6<7YmpV0_okL{6~ `ٷmO{<t1oNʡ}v P 6iF80.ҋ91xr˝tNcCz!QC8A[Mcc# hv)|B;ΰ_x7 6x9s^w|/K=7!Oi\Yxl{gq1=g`=r(= -{:8+wUɾ3aW}jZ( xi^S}W<_!SvCѪ/*||xrcBgf318FweSu.w xOEބQ})˘A]Ά:UI lqGrsӑDEk,e og~xQ"S"Bv^ mpb/S:͗EːHK>1@(|Jz_~i\&x[z'uu7" 5ߝOsVl<'w=+wnv.K^+-Knq=+JS4g|u+CvQJߐ?x މq 6Jld4y/Mo9s\ld3بǭS!zr5zqVsxzg8[g/+ H<7-صvOQm>Ц<#6h{iF;A7Ɂx?\(wэ8C|3(k4G2|pè!~ط bxҟo69R=l~AQω`q5/s.O<˱Gͪ9 /#w-y|^)lڒJv;ihx9jjAS1~ e3az2?t<_峔@fk//u1rf ;3bj&7 s4kj`+Oċ[,?{*O:o9D2-.&6mE>/M|0%X ^d}S0³-mx &!Es"$c:.춭o^YE2`>˦p=73wY97̴WT}:N0'56ZoNwq7o W1 <^^IfM:nt{+AL~ٌAG}^%yTAyG'2􂆼ؿ0` Vu];]yugG1Էo?Lfut=@'-0oӯ2{iz_"'< fxJP\Y@Jzw[~KL. IDAT V4z/{!/my4幽hxT.c3U@L1|z؏m (0f ?DU׍$u3gGf ` LzQwDJ/DzG ȱ5Q69cC|MDz~~}|Xu x0 g.<'H 6|Dh T?ox7=*,vkG= 9] wkQ>!A_ηz;"sAMڻv]>/oߊw<zMwE2KҋrLOC?+:d&+5xGr:|ktYd/yx9︝BoڿZ"o|o_-?jqT1݊yx9y&n.[7N)FGW>/UvOd|$"?1;@xz0S߳nq) mSv-f cs 9YmM|0sY%n;S\3۷s@oSe<۠F σ]%_L5ds.2,C7AF+7^쁳Rvs7*X"lgq [-O9u- RX{Db=:5 B Y =\"mwE=ۋuϊUj[FG^Yv8ݓ(!\1@ܗG2k",_[.k i®eNzP&ԕӃ1%rej0 6J"k^M{k>s@=پ6K>l!t;x'wGxY֌isf8H?~vˢOK]NmbCBrz7Jy|ۯ-7Xx._{cA(}Mi'?n"ݢ7&3g4̉~(;<~ـw?1~Cw̃>f'q&v bu>,,{o5S ntrTFiM6y@o֪ڏ}S-PB e HG2PjO轟/\p7T㩷ǸT <|P5W:Ek^tmh8{Za+@eHL^Z5cI>'j Ҟf8yHk^_X>`/F@pC#ҿ( ]ݓ{؞o56YyaJ#4܍><v͸ɾ.(/_*s7=;/Jsblq{QjFOC;3/Fnwe>.7mb'fT;~ݗ߳@Dx=+)xPlD Mg@D߈:9"Yj9  (ʽ// d6J/nwefi;MPGypò*$L1#?nx'}}]@$GH񻨫s}afězy؀/M= Xw^пy63WM펩)Y|x>(V97,a'V914_. P_e,fSWoxPq~ <߿Z)6@<lAn fU2Ow:xbm_,cSh<=~y  <Y;do~(ϒ\NzwayUv T>(=@OJ|2Pz d'>(=@OJ|2Pz d'>(=@OJ|2Pz dgøIENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentD32L40d22n10b50.png0000644000211500021150000004101111247035000024526 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?i IDATxr*P8u߻\Gd 1U}$/ aOfɜ"s @.2'Ȝ"s @.2'Ȝ"s @.2'Ȝ"sb|T.PT@dN[\ x͍Iɜ7r充VBߵ/=2')-4M^2', RM*!mJuq(2'WqϷtn2' IAȜS.ɜ\8Ca㊙ ST]=]jDBBD1 @:\IeI2'gehDx aWl 7NָcWbQȜ0_ q1gyQ=Psw ibs]Ȝc?u #sI!A`{MˌY% #sd6'mJ.l:Vɜ7m.V296cfၳkmuRߛG!P$(WuW ^2&o.v!dNm E:J\4޲W"- 𿳟*s~pM|tkSPv{4sxZ,iьa%5#)9#m?77.V4kfmعulW2'@>^9WlwaSChb};DZhFɜ=96Giph¬GԻ%C]퍞:Һh Ї}iIK^7|?1. Bɴ1;VZ{ei%qG#]+;Pi'ua8=>?ϙrx Ш+ic,;q%Z^Wf%qN\t3P8gAh$>4㕅mm9;v^}%a.fEktȳx,l. Єv.Xɜ6ޯ.d%_.FM90-ȜO{:p.dxa Q%D>"P P>!7 o?h)QM; 2' f QOIy2'n, lCC+{A>-V_:IׇN9 !C!|E$:m>ޖ>" q@VL4u(=6FK#%] ٝ`,ZD!\dN Ti7|(tF+64ul|NȜkshUZ( Z?WȜlo)jWgȜ$IH!m[\(?QL}֚},:gsksm.~Qm1\g&יJ4Q4ZqN6ZK6ÙWoѣja|@dN}֓Ç6bH`&1ϥ]|^tI!B c/ a60/*= IhFt7 36 I%__~Wj4\n+?sl 1ⲫ!"R<ŤQ/4`]߷s1fօ_ ?Me,9|!s 1Hhmۡ iɜSi7lm>Ֆ ׁs[BJ7VG~[늿#9>TG̴-75 o=}+9]^5nun>w-/c.n Cڟh.^e8y^+d%̪=!^B59mU?z^ՏCÐm4 L"?ΛsFpsk^JM`DSis6y?dNYii4lOn( O wl9TdkX813 ˌsZ ^Ll"FAc !0|3qpbhK9_}D;>~WMrFIFqNe p6h.U(x{Ȝ@kğrZKn͵{W$÷P5hoϬK7ZBTWq/P_E`O` 㿿Rh.ͳЈ!m_aPʽiepjj棠/ʹn;Kn' e2 2{e Ԏ!@dN~YݴKeRV!|!AdNr18 !'g";;Œ9j]Vl؂qŽu{"( Th r~U`+`W:ms 9ڼg;=ЌP>)L1INF=vNu50go<:mEM-ǛEܟ-l xrdϱ"(lxs0J.i7T*yP/}Yw{6;CqMk%m)s>w1P mW}O7Ÿ<>LvPP8@>M8Kp诟FYv^(K7O.?tٲrMkE)s}]e{cU 2O`Z6Z:{h42'P#isKme}vQwnt:$sO;4UTDԹkRH'dNa'Ӗz,˳ܞ8:Xa6 <ܭb7Y/N4vX4o+v߼4TAnwz#B C0_Nuq9]YCÜx9_BF64Sw( d{pKɈ b Ul<{\݋5 d.p۽YzeM/P3u!wTjv412'M\z%gL]áyȜ@WE|Wc,"Trx.o";DPH-De jGiSjj6áyȜ@: /*jxhPԗ@2'Z}cmw/L5o& \>:zwt/N|:)` \u֣yOo 2S6a'#!GS] h tC~-gNH'\wIm;^uP8.-WW3w`ʖqC\z@dNLi3$nՇI\x6)?<"2'OveskE4Av79~7ߊxI(qgj$srp5\綇N?5HSp]q ɜr9sR;pHdȜn[1.NNǬ@SAdN`͝x{UOʮnr0Ԣ TD-p.i3ᆂ|&5'Ο p &4 CC+{A'4MZ˟G!X! )^B7W|{QU7ex}_A} 8w@[J|*$(lUz4!6WwB.}̭ݼ\lzEoݜEyV!.9c7/48۴s"K^1zͽwC!hش Q j'sBo]Y]Ɯp]܋937q+#hp-(tG愞d sLJ]gpC7ۑo }4'm/P`|>EtrC|G>| n!xA&C_/AsB뒟ϤPc֋L2/ ,[hX,qͳp9}/{͵N,`nevն^HZAsBReb_Pg7ޟՃ܋ŷGszqx{f).pKEB 99G;??ۿBBP zNbg/@MDP(9-;:Μpf{/sƙ_d~jC)42 t^LwtܙxaӋcz~/@S>fR9+=卜N",mzWu6dt[Ex$@>N|%B2'4oxXI|&Mf*ȏ,4Fߤ {Q+ _Jy#DW7 .ՍNQaXS??b`Pf*(T,Xv[x t"tx\g2KuB3Y~1bX}Fmoõ$Ln' 5M9iNd K˞;A$);CG{-`G5SF#2'<8TlC|Nh )|[lNh.o=s1 O([lxZ?b/3nWlף؂Xu6oȖfYN8 r 49nTrq΍t##E_vuxC:$s]J>͖\sګQ=6mj!9I[;+ll*ӽ.BD͐9nPd#l/DHac<5O2'W9cl~zK;9!%*u6(dnI>P> ng?nɜS'ؒVVbaJJyvɻ/K |_1 ;e*~bjuo+{|)lk̭=gFm' )g9!Ϯ4u5TR .=<@8׫&mbc #E|Z=;ɜAgגF?=wPn"zb'""sBb ~i.7{SS=A˽h@8S;zׄ!0 uq2'tßb{ siwaR( 2'¬ݟgr 1uD;Al'  sB/t`a"y}'|9[l}áǾY蒯%E9C£߉t)@L kUFA##2'4s~I;ke|oO am1[֎ 2'KJs\NW]:p(qdNh~lV6NC! CqZϒ9jٽhv͇CWSh0J)|'2' BCп$W8Gt1줚 89HJ7dNNz4p~/-%2'0Sә?Uv>?CF>ǹHc0Ö"~ $4?6ShYPwu7"8V>iſ#Ҩ IDAT?|te%mVmr)K C a&0{ƽ?T-ZO触+~`}g;o?0Yyjmn`n-4xkg"r2FzF5*[98i w_ 5\5 Tz{/P"4oOx>95|jjNzCtۿv,&pBr2'ԩSbo";+! KMMN9舎rڽ 9ȜPNwɟٴjq2')(x ,h\nlmj֭ J sBU:<%vXus/B]!3<^ymGZw᩾*o1MqcBxN#zA9{>N|,]nTóbUc C$9~H[}DkQkuh^PskNP?nR-gW HldL2'ϳb:k0vsF,h q47PbɜpU޻yV@W pɜpt`DxJsV$ ,=tB7 ^|2'\`#nIe6"h5ɜ쿘9wu#w[q(CD|{;koghFPZ<}E'D8LwٛO`Ebk ~P.ɜq ;lx9d|OcՁȜ=q;NحHioֿ sBq#`eE*vژK؞Xu:=]௥0LiN cz1>lB璯6+!8@sBevΟ?(%!h{9уO@ɜPݝW`.}\W}HFX9;;~B՚Zfxwul2'#ùqAwb¹`ݽp:q^rȜН+kݽ׎%P:WNi&sBj!oqDHGp͎2G)fN 9%BϡcyI4ٝBRǐ UMJ,j6-g]:<]7B[1{5 oB3dN w w6=n1I ,Ӽ#\8s2'=B[Ms׾w"MUwIg90L{|i\=m}:^LV7p`Mh dAR ß@|A+珓8W)wK =Mܟ+&{^(BXj9{}M/mBdN6ÎsL\Ο{R2IY",ظ1V H~̵E1fMD@Zzyi+F!?Dֹ=0cEnç?ovne&#_G{c }R)jgQU9 kɟ0_ڄ>ɜÞ A G~ ׼1!|$z[T uOiŏ(/;\ݺWaI.PЎQNXZhU"oy2wNZJs`^[j{$+|\Dd*$pɶn!s줡p1gw ءǚ<&s@?K[M*5dC79r-ꉠ x$}N 8@.ohޞ^o~x[je 2'[ϣ ܽ9CCkG~KHYvj2y-518Kd?muͧKGGAk<oi 5+<򇏍za3 [#Wm9[yG͇W' Srךk5'dbm=Q{qR*H 6 ~&^G竝C)Ƕ|ټs5T_) pB#ܻh+b !0MdUN'뒡i+1J[ :H0 ]dYvMF2'p\/D4e96V .׿v.{10o=528N:Ds]̹U~>;oeM|XCY{i])2D2^hSy1?2~Y+xA|lO6x|"kuX8ik55B [Q`[[ SsȢKVE.N} @tS6%Qp9(LM1vl` RN>76_,M9t7X|W z!An _WJDBM?;4hxka_ !]e8f㹎f$h4`z3TҌ7s-j.ȸdvw䶭R%7 J愺ɟAh7Rd33Q8ɜЎ9Ϡ[!S5{6G$yO&sB&}텈_N-z߈)p$$su3)7Q9 fVT끳jF*f8e>&͖F;]TÛkdN`ᖝwB)Jl | JǞ-Or+ \ ]|k=9⑽fr>'@>2'@. ]Wͱ3Wwk#EW 2'GGٔ69{f 6MCdN/ߴs8to53mӻY; p?Y`?Hudy?'{)G*ӁJIHd77y \j[더+n!oGÛɜy k#W޼îvfܯ/O P$薠5=5aM+ ݆O"($g ;ڔᝥ EUv2'ЦX翧 )p$0Ҫ<82 (-=gg6n s?V!'pZ+62'e(*pDQcaMɜTv&dZɜyd鏁SxukB1FhTGX!zc8Wb'dNC)72*$5imc ! k69 Fr^j'setRB>^2'(!İO j^(2'RBk2m&sBt׮zIh[iy02'$&D'4F; P6p9m.Ȝ4d>59@psoK[ ő9h+LL =5Lu2'M Wf+ޤ7|'@kdNHA D>8_xuaՄ( @GP皍8o-Q P9st=373m?Kh qT.J8S^,c 9`!!BO$דgI͑9af1&qh?Sh )cgHc/4-ҋ~$gtpƓc D.^A:rx1|9]quAawPvBtqp)^YJ/cny9NѕNB>jY-J,Իi6o uJ}sC16ƹ8cm9q NdNRa@ȋQ%y̧` Zg3ef 2'R g 5?fq2'|Vf:tf*,E8H/Bۻd|Zv9Bpz dN(B Bd)m [Xkj1n-̘ Q=][?cB73''$*i{yBeJXxpp,wzGMBȜpCw\}$&ɜ Ǖ @QdN#9"٬̧ .m!WQ$8HH?Ŷ;gn!I6HEv Ś @dN3t=pfɮgEiD\Hd2'|[ o̞]j6l9512'J%9v$,q8d]O~CA_ب*p^M sЗ=9$6˗IgYu]2'2+`'+,m9hPfF1=Q*k*'sЈja.R2'>D%m냙o՟ K۷h5PY1ɜ-ߕc.ܹB2's$sѕ9nƮ ɜ$p=ڍ^[K[k8Jæm'p@A9[00"sSl5_V'0#sScrk9څ[L-p lܸ\s' s'/q@{ ɜh]O}92'/SSdN(lr3#m2'-1 Á 08'x1p @>,57_ $ sAЮ=cBi2'5_ p-N] 9#H5ok =dNrx7862' v:'IafbQANi 2'@׮F͇P G̞3"st$WԼkQȜtP#MD*%s4 53Aij2'@ ]h 9j5"sTyb 9j2P>2&P.8D S r;Ȝ"spAN`\dN2 &s=]jc8'Ȝ"s @.2'Ȝ"s @.2'Ȝ"s @.2'Ȝ"sT3yIENDB`pyformex-0.8.6/pyformex/doc/html/_images/Formex.png0000644000211500021150000001360411300350603022161 0ustar benebene00000000000000PNG  IHDR X' pHYsMMHh6IDATxMzQ),,AwDQ)3JHݼxmXa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0Њ~ſZOЎYwN7 zl<Ga;/,V<yqiߐM?6;"NC8Kyǯe컝 6mG;@vEgkSf i칯yb /oɌFTY,%gl N_^j9#*ђ!@1NROlژ,U<3#; 431fpl}#;f?skf2gMfmw,L"~`kc+3_s_KvTo&^}^Ef/˷go&D&m0kսr5;m/%BK~w;?TU?%5qEͩ1±lvGS-g ?խxwvu>o65v"gi>s7ՖY ,կ_nJ2m%m:Y<(o֎Z`nou<ƺW{*XGq;Ry׌d7ժ2nz^6:oa5wތ}q칯~̂Gn{޷HZ>lr,9Ii_ ,າk/~c݆ytd/NaćqM]`lMis/6/,rA p!+ re3~ʏu~ d iJ:IZ5h`gMؘ,̤ 5A`G 8iN`bMhNBZXYZ#V@pTf ,x85Ap&XH+X4k ,Q 8.Ț ph hN@`&'Xά #H+k ,`S lǚ p ؂.E`&\Vd &BZW&0k VO Ȱ&"@&0H`sH+LfML`H+ bM>VS ,Ě  &fX@5A&Xߤ@  ,4ieM`% HZJ`X؀VXp $5A,8'W;Xp6 `w Ú @#hó&&$ L`H+ ,8k ,h5AX.ipP eM4GZX80qp 'NF` }H+Xk&`S lĚ u,X&pA ".K`A5AX$ ,&i;XO`|&$`i@i Z J XS ,eMy V,!˚ , B`fM(I+fM5,.JZX`mk1qW!، 1řI+v!8-kE`qB } ,NŚ -X3&@Sc1|???k)>6mh⨞ퟟ/Wb"8$w\2`q0ۭf/?za XX+8E%h5AM`i ,bMsX4AZp&Y|Vb81֤'؎5A.B`W\bu ?iY` i , pq$i7E5AxX,%C`5AX$`b2kP&@Z@&N]74  &XXL\< X5AXN`7i)͚ D V'˚ D`]U ˱&kX"` I+؞:3k uN v$ƚ x<1&fNBZ@;YV&uTYx4N`5A8u D`5A84iG`x inIO,i|???___7 i^.Wg`I+FSmfel5-?0 5vB^O;?Gz_>5g }eɃ;^LbMW;؏Ϋ\8 B>eM6~yw#uq;gU}hwqqe& d]5i#jv>wNSL:v,W};9d=| AŦ(?`jZhZҼ]k:Uy*qk/]Uޏ+?K]]٦%`{ N3ӿW>.K |] kFFS/ufWHn0.57mXp ":3*/Tc7yбkOƖoaƺ+`K+&S ޯ]W]sy2l[9v3XJ"nWVYW/wV)5ԉK෽uI(ϻScJ}Tq;ҩg{p_SjVclߗE3/5kkᤛ~m0:_P3?(0?vVV_c5⣚ĒpMh],8;3@||B-`>5uh\3bO^83[~6B`5ʤTTŵ3qNjag^&v/bK3鹃ؠDOf8Βi,ObVv 3X6Թak[*3X] @Xa L` ,0բ_~=`>&@Xa L` ,0բg'@Xa L` ,0&@Xa L` ,0&V~@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@ؙ~G1.cDGuZ3rm^_ޠy!w^#==yO4{̬}x>T%jF5گI,`k@yioS; np5$/*cO]gTDajj06O? cMSOtvwMt@npְ0qp,˜<'jlTvvwQ8.V޷Q%֦cc E/+q;Sl3X>9)r.?8{ڑzZTΥ2Ȗu8囚[?ƚpBOȤ\ԟ Uc8 ֭xx_\is"8wqB`>omyݶِ:{?C;#漑tCTvvweSd7{{!-V.r{%#9y?u֒Ü5;*j`%F>>r^sv4{Z`gt~IoԱ*⾋-f8{@`wΝ`G=CDߖP,03Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@Xa L` ,0&@?s~IENDB`pyformex-0.8.6/pyformex/doc/html/_images/stent-cell.png0000644000211500021150000000171511253711161023002 0ustar benebene00000000000000PNG  IHDR [;{ pHYs+IDATxr0 a2= CI9j;c $&gt #l2 #l2 ;K)]O-%G<- G^3Y2"%)ed̽ʾQEO Ql sj @6z W ldS fKdl2dciNU#}-ͶA6c[٘@, @F6\`vltg!lѪe`٘CB6{h]_GFegl|bƁlT9=lUPdV-? #5|n]|*.!N.6QydIuG6ؔ bf dD1ِ}ܜ %Y.^G0{O'ګ푍81|ǀ`hcdl2 #l2 #l.ÑxIENDB`pyformex-0.8.6/pyformex/doc/html/_images/patterns.png0000644000211500021150000001121511253664076022600 0ustar benebene00000000000000PNG  IHDR,Dl6 pHYsMMHh vpAg,*IDATxDQ̓-((r쮪̊sV^ 2}?l(G@a#qQ 0 F8(G@a#qQ 0 F8(G@a#\(SId_wV,K`^FUQD*Gaa$Q8QO`I]($p1IάbyF$ WbyFF$ #V< "U,(D`*GZ(oD`*G*(oD*Gy#a(zG{Fr?/y a( '%`~ʄQ`1b؛g!?}*Fa5 kH&=y4?q0gr}/!0#ѽy ILv KݏʄQd'@&;"% a.day/!p ɣy H 8@IKH_(day/! ! ȣky ك0 os2فP<: ɣsx ُ0 8@BKȮQky ٛ0 O9@N=KHadNx !_Lv J=KHap2ف@}KH&aPv Gy I&dB2yK(Lv pGp#d q2فk yKQ"8@]Fٜ>Sk(r2فyFِ1z 8a8@`.yKFل樜GaLv 0_<%3Qs2فZFiduʣ^B0J3@&;ifµQp2ف@}FFidȣ^BG4 uUh(E9@`gF)dy7z a&aBLv #yK dV 2=Q/!eS T )  Q22ف^ByVFdy QOe8S K(L(@3/< s a dz QMb0@&;`K(8(12ف_ByFF) ʣp-aSLaLv P|dTx Q8Omف^ByFyCv @PR0%G](?r%G8a:γ(!vz QxM/;v} QxF]ف /< Q"@P{hʣp#J>@/< h %|$L dɣdF#8@/q(i9@/gQrr2ف^Qr2ف^kɣM݊#+at@&;K8<~Lv p>y9@/Z({F[r2ف^:QFq2ف^šQFp2ف^Q:Fp2ف^^QzFKs2ف^¾QFr2ف^=ȣ'dGL-dx &R0ZGF]dx 3ɣ!.dȣT .dܓGYKdx yFeat %yLv wɣ$d!20:ryфы9@/!#ȣ#^dx Mea@&;KL(FOq2ف^BVG0! %yѷ9@/!ȣ!dx L3!@&;KH(Fdt$r0ҝ<?8@/!;GyM+yg?Lv (Q %$<ʽ0d$G @&;K7(qadx <,(:@/!&f@&;Kɣi6@&;KGslFLv ΓGlFLv %m0d0 vpAg IDATh CrQ4 ҧOA9Rk߰~GYQi vӃQ8> r ۀˉ)s]dr>YĐR lJᘄQ7|3:]н -;=IX'nA^"0? Dd %@}fc|ID8{Ny(/foa1 `D-ƻ8 @q8H4  tLnZ@xdY?\ntVRIENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentD32L40d22n6b25.png0000644000211500021150000005047111247035000024467 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?i IDATx풣:.P}ra 6_)i]eCm=N!qdwo͒9E \dNr9E \dNr9E \dNr9E \dNNÐ 8NfɜNɜNVɜ@b'@dNr9(:#s9Ebh #v4C"; s  K &vTM;%s@[A8 H`[:*%s@+ b'@dNhBA #pP#*Y 58^ݿ!9Z| x17vgZdz-Ǹbe4ƴg {9BH9e8@w;m3`_7~ Uls@Uί̶\0xWw X XkI@bJk%}{EH1͜r毢ɜPiO;oSdf̭,r@֝i }NJ#h Ys@0fK8{w%p>ܲ畕>'m ӴLzNZLA>'aq-4Gڒ6uNP'|sE|i]~ gj2'|t՛!$ )&-Xt|ڔ!iks!>SI9`Iґ4讣ɜ»?^9`"O{sC:b%0~eno><|x!s@J)2W8,[C L|]X /BhU=3r|.mҐƔҸ[RcqnLpt8*rK^YzL_,ݵ8Al~[x [=oG;u-'W95ڈ)ۋ6w9hb39sm{E=UӠPT @[fǙ:GЎ@̭~77ocp` @40=(-O>sPž-g$[:˜3wonТFz/N#lSν[򓵈 sZ7p@̭ kʜ;Uw̿b1ȜijeRyHQ V@fqY:XȜİj=BT apIҘ )nr޾տ2Lm\@qHz1? Y7?)@^LّgmB+gG@6'  qZ+ljc."sP[P"#luڛ=~kpuk(j]X(Bwcc{WiؕY[psE7K;Ŭ&q='y|\vS>^^csMΧvYfMqeztPJ͹uO9IRvSﴕ'[z\c^8A+/BBmL5g!?>'] $iqL)4 X 9 fw(ѳr'G֭JW Vx !QRncmi_OTmwmX ~ϡ奣c3emu6=ɜq|ڥg8`o'O_9 )!Q nlYI LVg!A[b=<8ɜlSC3޽2nJ  ?x%B{&O2'pSüjfdN>8<sSYOy9Xw_* ^ Lɜgpk"Oi2'K Oq<ܝ鑩@dNބ Hug^i ]dNLw!vjr᪡ dN^ uЭ BW(h-e<$O~2')Q7]3yx98b|EQg9WIgO!r F[yϰ$~'#y9:V>p?cƴWDf am.@!)1-mu_Y.r{ÅHk,?\ڤ1 C4.`Ld ПCϑr9ga|!p7Ǩ8% ЙlP +?bA:P/+>skG]i3Wp{~~~XdauK{|w-P Хb93sp/߬EeQ9{Npn"saz{̙Ǡ?sq O63;5DPm*MWu䀲dNݸMJhq}aG$ӆ (Bj|j]#q9ly=?gЂs%B|0yNjg:QD_:ةr],Α9Ǫ)!ÿ428#lCeoʶW%tB-̈́\]#F؋)۲Cv"gNu'Bu8J IaH/]s߂(U6Y|n+.{x9yMCZ0 Rai-)\@dN06~pz4v>ZຆS$s61ixCsowQaA %smOoscU[I 's˃+'@~ڛ]nSm}nF㘆imOga!`0: +ڛ1sg'DƮ v/Kj@/_gцw.Lst՛ҿaOJkyvw$}N<.z~y[ U7,aҿ1mۃBuZ ՁC:qWwDջ[̗*<:V *`n- B?Sk}ٶxk&s\'G{s㘎F,notzɳoWxzAodNio=vV#<; }9wу˫0fbSehI[ pZyfzxꉵ+ԧ̐UOɳ_ @dNsj^"gs3ɳbO bUmI{ Hk$yȜ[*H=h4y:ҳI- 2'~exuK[\SF'?2'NL+=>kv`8ɳa^i:`qkC(:?N3CTgܽ&s#pNE$,',yVnA@w] <0S!2'6.bD[ \̹aygm{ޠ92'TV6:/ m9)8 F_-Hݢ"O^8ΧBɧj#aSn.l;2'әa[{- ڲʗP?`EcϘ1}? I|5Lm ?gg8GUj#ҘRe |p\#C66-HU@N{pX3잌9YK4aHƗ9߯3 N}>vC)yBmdNWG3"e[_>.|;K4 7P!`bxtcœ>T=ãlԕ|Bq%8}Uq9Pa`qxa{@vWm.NcaphiyP 2z2vnW6E"J[ojUg̳JȜoJ̜C(woƜp$zg 59˴ș1#&s T2ө7 UD!{3 Vj[/=@v1XkO4g;rNڃɜ@&È!ÿ4{T#/YZ(Q[ɜ@fi3)irrTΧ8B5ڃ PAK8 3f9+?l?vڵl%jXqa}|/:`;m>@U^Ҹ^F+|Z(Ne5 }*bys026颩·rCdNϑܷ923*,!fC` fcc!Z2UT6ntdN C͙GL-XfsSbI۝<uRkPCx;03R{OZ+9)LȜ@<{ JkgWsn)mxq,=ɴ0>ȫ&>yп9@O=T-?S 0MW;m py*Ģ{HpMѿ&sw9}oV( n3'pӦ4 g]Н1V97~NTwo=Ob+ԾRr+@qyr,pʩO9mɿ$%ֳ!s]>v2{׍XhZO_~. a>ϬjYiʎh)y:Qvͮ 4F3v'jye%bޒgë |gw94dN zf^6g7dsߕ\̳eZ ?;"~-{}clLS 1vyz4ɕqs_1(NLWN=TS DȺbY^|(j!V> RSU1 cbTIK{[s3gmTɜuj.2BθzAj :@mdN"ŮcDG \yZ%ی)I) 1 \!p2ö :,S5yV鴪y4LN+8=Yb"V/62iHzinmC,9sKl )檱pJ˫<[2'pB=)K*ԊUœڶ<[=2'pTyw5N"ǔҘaxK!9R) <8~;3i_OÐ ?ۢAapp%s65Lb8˱ie;xSFX[#FM: $Q*j_-kh6 #-Q3x䱥kuGŚB}<!nV׃z83$ƦGx>9(ڔykї7b_a*d`}N`]}˂cOA?Wft;Y: OU#s1^ (T@6X\']ؙJ st"ϾZV3j*I݇-*}5l#p>'qj#\66NV]P}*UmkgR;p{؞1fO?|BH)mܖ^|r}nՏ>oY}9QUFEw*w{pТIaH/_gw}wXd,,T92'5mia*P0XثJz]=oamrJ@Vl\%(/ 0wsȊ_Z&sBVa81 vhmױї1j)kjaS  5\Xz! >տ:Þ4< u"m>?9v@j4mϵCD*u/pw;ȜPKf<+%ơ@jrQg:x87pqUL}۾|;5D7zl>6W>i2'T⪴=/]bmϏ?K{rZ=Z: dN3->o6:gӶǗO5 IDAT*ZΙ* 8J؞㰫`Ѷg24KÿB\bv#7\h->gJ cUC=t>ǐ/&mkk[ɧڕfc+9'[EACr{)Ӷ=i_:gag'Trls>԰O]n ph>)+o܉5`OzslrP#or&qw{\Z$S$LFSg;j%mó a\w#TG7hyMɁhT{ dNH30gKmXG5r7Gɜ@s3Ip}׷|̮u}v_ȜpӨ ί?g捥vTja@p^J nkUO|L{nRYYo@f2'9/'?l:i-*SUzKw͕*8Jq"si [Y94/+6">Yo0Kr5|{5%$ em1H#,8IDiN&4 R9 hOL8U{P@율Tz0<*#32U;5<4s6#inJ`;zg0P@eElsz US&sBC"bqev`y 7?_獫GBunNc1ߡp'jn7A>'@eY#M4y.8϶nMf72'H czOǩvF&hJC&變1^%ǔҘ͓g<$mT܀Ȝ7/ڑ`89=NPPN :#mnWVX?Sm?#c{w\u2'c5z銍\qC γmf;:!sB2 F5g6o&tkTk^7=*&sBU|EPjڭVY]dE/'CdN`7eW:Wf"zi8KzDz~RvmYzmʀdN8CE[G7Հyp@ ,$jHaRtBT][(;I28e>'O^hlß^wn`Չ

V8yz@gdNX7AWׇ"]A;`V'0%s1 D(3uzLh=v9juuJ*d?Vm{,m 5q#8H '/Ĉ+<̳cri blWcJiLCzSqmR :wo!4Cih՚+ӤKPU=y._,Cy;WN(IkfGX Rja}Րdf@dN xpl¶G|ǴYOk?AdNǸggRﲿ,5iF>*)!a{;Rÿ4P8 yơځ,ߥ=4GfǼ򜵧/40,(U=׆-~0F6h>'b69#4m P=W!,hX(` ᳯb9!c)~ *L=W{3U?lzX#sBFύ^ Sm?'7>ɟu|B  yu<_ůzSm5<2X˟)K#=ݾW2'd0 YJչ+⋔FoxFum1Fk~^[S %7 D{v4y79rA"l{k,ݲ@{p[أz`nW l iZq! \<[n0;aB?ך@?C-tG06=:L6sBq}V?G6ڝL ,6<9o5/ޝY ]dN(S՚wF-C6nں#+[NK^56cJiI0KPhy%pn=Tjg9?O]fzN+ynG0~oU/pB9OFz$uo}JPo2q" sR @Zs6=g${^||[x V%LNn@(pBdNŠ+Mlj=B ^P;s8U2'x<wl)1 =>1)_m"sB1ޓu2vӮqWZZ^V sBPa'F?饻BcEؙ9mopBdNˈ+cfMᾓWu Q72'Vpe l6g3-|&&tH茸1vs#mHiad_&E:>\tK 3k[Ɠw/K FU1q'ˋo~:O =9}D9i{9%-i wo~qc1%|)Mo?4>ngmk&d&9.)5zj~ܩŶ'm5t]mť9yڛ ihՐqL)ip\Z{wG29z]aMcvq:90 CziUx֕k>r[1] l'sB *tuDhСv׶u>,8vV;`鐶T{M` Q۠9,lЈ U>V(2'AWjۃɠ:ml2-p Mizֈ[HX/ :a[ԹL $sBkl/':7}z`8)pȜР]}ۘWٔ mx]&ܦw?7O}+·6 ɜЦ.~X[FY^#=ym]:ӐM"2'4XM|XJI_"sB:0Wóe QVLV 29qHk Ϟ]y]|؎o4pn(@RЫ..|/JsbH#|4l \J.Dz5P{/^~'t7eha=8ju+{ | R } 0bZrLeH#MMdzqp*  uU7 F}P =>k3-&Tlb|1[ ^*5mx:v 0;|;5?Bt@dNYFq_!>5wMp*~彯Q ݽN#L_ScxAuQG@ߎb<=ΨYJ5{Vwj @2'R0ĝj}Jk$7X?)uZ|kw:8V0񜰖 S*4jĺ/x靓oW |` s,kR{l'Lwku;& D lUFUxB U)ӟyvo< ,0]55 u2yL[|~V̧ s&JNU^gJweBh \ygQ^Q)Y>w_ \]<(VF1Os(@cdN2 -,kNj3l3&4@dUvyvϮV"i&ɜŦ U3#3R =RHże-%*;|Za2'aSWyֵڍ _"i 7 aHcΚӁQ[M| w#U'm=2u=k[gYN2'84y헜6;igoMh xޓ1O*,;8G1:!s,:9N&3 Xp v^4m @?dNNo੧;~i{&B?|m3 rZGizIi4(ؙסV M:^)@K݁a{7 ˗òms~Qh>0< ]`>'pió}ҿ!)|!Nr]'pnPZAO@[ l:e7)Kg}dHm{v{6, w<}N~.,ěu=wgZ/|v-o n'yL-xWYu> (}7gKU@6m|=۪h{.k7v3vWq~XJb1϶Gڞ~~&F2'y,Zl{vVͫ <[N0fKfSا?.ml!sq58֠>ع lRY`&v2'Pʡtq2L4=5d t[o T@'N!cg 8iMwo&a^ǟx|CɬU%MfnSÝ&ZEzh3 caC1JP2eIDAT^ yTj{xΝ,::d~MQ +]iYg **j];49(EhYm\|5{ju_#j~}R2th2t;xȵ{|wCΰ錃|_rTIz'yF7 N!Cy"^:i{sE7sk;Dv̶Z@[c*VQwkn Tt&s7 <,<"cgU?ҐRnawO*Sz1@c̭Ͷ5fI[,N}^̴'@42'=֒ODÑBMf&K4I-T.Sӕpbgn'@X2'.͇أR&P ɜId!M:l?'pj y}-Q{q7zj[.%L;.j#pTG{pT_-XUm(597Hbҵ&dN6K3攛L 2'<ǔҘTyɱ[gӴ)p4Cr#NkOÛg2-@dN(^g4vJRG~VdZ 7xxfc~omkT>%:~O>'oXe] v8{!s+4ipgur My2'@*+-F3[mtB|XI k(2zUG|[q6v_L -2! ˪ʐX;u. nT8k2&.{.9>;QndN-N;?[o`֥ ЂلyxaN0rlq6\ @|>ӅmNZDGRi9 ޽lz~M]y`bwxdN@kShX̭Bk>8XRQzT2O3- U!P׶{czwsXfF(Lf,>9S~ %dN ^n*R}>6%Hu+l@'W9#Ɲ:N2yEY5*~B~44|/O퉖kSe @IG*-Ps;}Ǥ18(L+<hɎܽm4$sM+}'3]^'9$ObNs; 79kMx[K\ dNJzTVgp#^䩉*Vͧ =y2igb CM3ήNa\5t'Ȝ@ yvmI.9Ks49)p eϰ8 䙝y^U5tN’9IUrY:|}k 8Ln癋|p #yRyU|Z sy^L!0ZȜ@8`RX=yfdN 7tw J.qQO@2'ڬ_ӎ024m Td}p4zohi^wmj@M^&ܦQۓ-q'j5{3Vg<Ҹ~@9ZcJg`>_g2 iX+s5t=kv~6l/W,8wo57VI%vޖq{p}Nr+~mea[Vϗp:2@+NLEzή8h T۠6< *mb۳SZSRiȜ@M%9c @?dNGOaRY`&]9~pŖn@+h黀tw{M+;ŬG=߿p>skV\tlmjx!Wu^4i{;u<`@CZ2Uvz/U[ t:Vcsm)ҵkչ%l ۇg=ڵ].&| 4玮Z-Vs綶gx!-e jZ& 13 kIkź [sd hP'l (v {g] N9FU Jv;tP!wPWQS9v՜>1\{ȭ9v>ϔi-]Ֆn&sMk1({=,^,̲ASx gs,1\"~OP%h]T牸S*XzOݽ=B@Ӗq0,ona\#8*@ V'!4Ջs|/p!q[̓3eZvk2Y1rp='Ї< >'3A.v'p@V2'۴;NGznjbMANJ`VB{{pN?['\@@aTg96 P Ӵ)p@I2'ՓۧkIP tW 54n'spTة AȜ@祚,&7>IsMFs´:NH՛ hdN.rG4U6 \H96 >'ʙaV*ȜdaUM0 b  .ivds)m@dNr;Mh @fc 9[6U2'ENi&sP'ӨMh @A0B6aH7piOiacQdn-|hs?TLXhͧɜ\j&4M,7>ȜbL+46dN5dN08L`{L& Q ;9GbL&p5r&P > w9z!gɜ6@dNr9E \dNr9E \dNr9E \dNr9;ٞIENDB`pyformex-0.8.6/pyformex/doc/html/_images/transparent.png0000644000211500021150000000151511277771572023311 0ustar benebene00000000000000PNG  IHDR #ꦷbKGD X pHYsHHFk> vpAg IDAThY1n06QC ${^%HHe`H,PZU@_ 6/Ɖ o!  0 KùQ,˲T{y88cguZ#L)Y&9B{BaL߶ g40$~1 vq(.y~iۗ aKmG[O@mA kdgk{2YտaS^8gyK!u=n#l6M`|~}- 7VJ ty_)jŵmo}kw5}tR۶~zڵ4hÏ;G.zߴ)'۴iΜjժz `y5k wp*zV%{s,QrZGy?wffÿ}j,+/O@qXLJ_O/ݻ^&>lf)]:RS=GL (S^-X,=zWN㎙3ׯ/UN;OzW*W>A=z 8ӶV23.?[;磌y7lXq1ee%''&gd#‚Qtz{4xpv~63sɒ1c}tŋ`w79~z[ol׮|nƍ? 9+k^ ʕk;'Mz.k"%%.ϯŶn]r޼>ZjǧD}rZ}#xcɒ^om!+Wg]Wg^}GT؝5kLO_(9îF2S6-S&~'WbKu]w[݂V1㡇V.Uq .߿LW^>y7/? /WT}] UISQۛn5-ۤɅ6kjOcxic̶i=6v婩rEժ}ȑ~kUt;{vÆ3g|D b~睇=:thժq:uj֬Y^fڶKgğ~ڲ%+yܲe҉,ؼ9+[Y`SOm` .[gae{l׮E 9j޼]=oW~qk.XxO>C4Sfڵۯeˎ>F˧O_-;{ٳ~Ȑ}O8߿nݚ5VVf:u7n#O<쳯gX8SR49~/99#~y1!m۶lل #GΝaCbb:]pAo~{YrΝ6 :Lr lݺ`uTxp͚mXl͚;ƍTW_~}l[_<3۵k޼^5WSIC=K/[,3sw[SVo}tS>wMԭ[Fu6ir!߷!??{ [MhO>y5gѮ]׬YjZm}?ddi,̋IcM>|k=矃^s^yuW]`us%<رO?ݦMrS>m]vY:rHӦu֬YS.p7ݵҥs?7гgРA͚ի׮Gy晷k?]:_?|eӦGѣM7?`5{m+YY:yu))\tѠA\-|/h*)}tz5kV^fx]^zСo/[1.Ǭ3hUVm}9ꞶlsonRxn]zlڴZj߿O_mvlWxo֬zj՚49Q,ZJ||*GuUS3bħ[9֭#yg9檫>J<2l WNΖ- {uԲeÆj(ӂEuoYح / 2P$FeFrr֯裡C{?4#z;n.Qr,HK7na?ʔ3nuҤ_)JUPT||lڴfg\qUž/gŃe{p<؞n۶l٘1\У;蠟vX׮={^xaNV͞o1eȑvfMժ3f<ȱVS˭[,yŋ/>k4iꄄu>k .8ڵcefϞ0wӮ]SSSb7ϝ;tiu_U!tRϞ]4mZjBBfU Λ'3g;z1+N)PRR={^vΛO<7^{}* O=w7'%5lxU]ԬY2EWtA]֭'?+?iiU}Jv5~˖m\w>hnuؼyW^ JSS?|W{۶eF>n&LXiΝuO K?x„ ^{;9'-m޼{':v[¶m|Wʒ%APf۶z9ݺխNg{k{ﭷf9򭷆?㌚5w5yEzOMZ~Of፽֯0aٲmRS=t; 6KL,S!ӿy;vcUKJڶ_'LK.6m5kL/a]%}_pAϞoi!~z^xk~ر#G^qEח/30|gsϪUֵk]ԩڵ>vkMW^y7V>}:y?Sǚl Kfůc_}iSjj˖]֥Kk'% ЦMJ3mڋ/>p]Zxb\ӥe`Avv Zg^rI*bW'|Q=`ݻתw(/Ǭ3hUVs}9޶ٽ+!龥TBk_#u};^|q7xeO6rEnA[.^W\qu'][i=Ĉ{2.qq+z'\[oM8|1t\jrrVgXZSO0C*Tcu۶_~y/eҤk9k3θv;40LWW Tdd,^{ו&LNκu3g|% 駱x iS第뮛8+.z{QN?.A<*Sq?ᄚ5Ǎ{|nݺu[좋ܹCm9U];5u/ww~%K}|{};QSCҥco7߼)SnC3ϬYs^bҤAɓW*Uykyɓo#~_299C̚tR=APŽ}t̘KOOJj'M4骫4q{޲eժu㋢TZN9eC?/'M1/n'۵PᏧ[|3όpaffٲp%g݀*UF:լsx-GݲeJJb6|+֭ժux ;ŋ}75{}w„ .hdtm?O>?Wʕ<[yg .ht篜8{>crר/cM^Yz##㧟~Ԩ 23+Th+;F#8}*UҤG˛n9#Ö8hu׍`A,V?'s'רmq۶ - ;YgϱѪd6o.Qgsϛ7]wuծ~o#>zAoɒ[o:uڵ};?z]ֽ{:/?g{ef.Yxqxm[AU'יw/d?rrV1~_~[3+3XƉ'^6ni7?o|ya_RWQGu/sO>n٣G6hP߾;}t˖ ֫עEvݻpݯ?Qtݺzv[^rm^Ν=q㔔X,<_<%٫VM6bkAj;tmyǗ/W]5tVCX,eKvv,qq )):KHHIVJ\KH^cǫn׮jXlժiӆ5k͚?)ظq޼G?~ٲ>xNSgϒޒ+V^2-mʔ/OO_׿zӦ |իHm?߂Jߏ9ө\C:c*W5kf1Vե5jtloϛ6֭V͚j֭W\Ѱa^XWl#YC|ԩS̘1k֧~ŗ_~9gմiG6mk?6qO~%'9[5k{l;SS4([611LW}X|ʔe˶lRR6=fv55.|O;^7\jW-ۼg٤;V;N]c{>V,#cٲw%v޺*XgϱѪd*sԂmwž~Bn7Yq:G*Gr?|g3N:;ze[ᆧ2dgNRR={^rIAwѳgIIAeKQ }X\K&+-s3Zq(^"p-[~e˖ \ׇ瞼73sڭ[<p%'׮}'ܯ_oX,#cɒ>9rР {7?N9WwQ.ZgbX,{\:33;;kHЄՓwrڴ?'O0Y{啉y[^}𸸄2e4hsn[orLwjjV_~#|ol_tQ+WΘ~zu\\={^};ߊeWЪU''?nxMΚ;˗ׯ饭[W\bbb *uSNb֭u._+?iSRRݺz!{Ym5k򙙫V~*Rۧ;Tʾ=mͻҥ˗K2eŊfݺ[;Rٿ{]wI??U~A{)}Z+*d YYn;{*8oFa-ri=zu||g5`!T%^Lf]tC_|#'.[ֵEկ_l_ݾ vϐmѪd9jA;b aOf_eOwkdRRۯK.]ֽ?曌Ԗ-?-ʔ)ͻWT:]\qEוWo|ԩ%%n]lzk@A-ςm_BN k^<KU\C}>؈o䓃qƊ+Ϝ3B-9qׯQ?'_~yѢ} JIyGuֽ:hVTjoj*5b_ڂrꩯhQVVZ׾}J%&ŵm{)k+TxE,?~֬իoڵK'3s矿喗^Zz3fƌG9?v\9a?tXa8g~\2޶m2y#|u99۶];f̉'֯?fz>pa߾7дiRRàv-[~5##a>=U:Z+y=G->mvWw=auÛ>%wee֪k:=YgժUnP^uU6u5/: zիmR*~_gmE斜\n.-t ?qcNw'!!)rp'deb%&oj>:uRRlYtՒ6l .?5ZZ<>}~wذ֭˖ZFZ p /SS33.=g^{ǿjӦ碋׵e%'שӥaU^ǯ6}Q}^vRnxmRR'pUffӋ/N>n+WfgWغh˖%Kƍw>:"'g?~ɒu<9v߾--^3~X!..9n m =tР])UjӦsfղعX,++==++[ZGWo)) q衕*8vg 'MZlOZhg屦$,ŋ_}ǿz*U:3[gu k'%{p_\Ή'֫xo̝nݞw*vT< yhUVq}QfB~ bRxI\?'gY㢋z諯vܸI|3W_wn˞ y#>J#T GWҡUWyd*y]gS%}v{MQAd@Fnԉ :wVضmڶm͚ロ5kU ָ[|>8f/]C996|>7lڤI׮?! vNsܶmo]~w6PZǎ_ަMJAj|ϞauNΆ |7VB[W6m֬ 6oڴiU II5j캠PILY䓯*Vܶmҗ^<[F駌2eZ䒳kqVn޼\m.wڴ5kʔi޼gF~)ҠA-[+~G{SOSffjj&={6iyvӦt|៻rr֭9 :߂r#8CUm+8r Onyڴӹ⊿zu,m-..5C >}6 E{_?'MZ #c'XFƲeiiO=uݺ]t7n5T33/ܴ)tb͛'n7h+cM_z[sύ`AFFRRF_>|ȑ?<|F>;|eƏ1b bjՎ=۷Z5֬>}ݝRn۶/ɅӎÞѪ*s٢3=ݷZ?)S g6mn :䐆 O9}_zl9/xĈo)^!*V/. #0a U+[n۶jջ>Wvw\}ztQmtPfuV7._~;SΟo99*s~z͚9)) yQU+~{we w\۫p&%իwY^zYvHHQK;OwyOo~nN=k֌Ŗ-3gIa=5*SfԨ rW ed}}V!tоa5kVvrAvwM>n}m[ Gq%W\eP1\uUUΘ1aœ9SA||ŊxꩵknYqqp@ Lee-{!=zx`qq5kֳgFo'6beʴly[l)ӨQ5jgkg;wڶm:Al'?~YSz.'億O9eaN?|㍑#<]>ug zzO=u LgOZ፧KH\ ?J:A}v}:jԨf͎lϞ]kϞפIRҪU_|1iҸqUܥKeʼ?;l*۸qG}%Kn-[eK/+/] իwtUmVt9>k֌} ժt{oeG?\~Gg'صk͚Uk,X0{|q#F̚5n۲1kvhUVs}9jBBlž[>nݒB-=a+=LV֯{5/+ױ<%4m}lmŊsmO>ٿ̙]>^x=tza(Z03JӢk%|UJHAtUc|avcO>Nᇯ&_WLʵk_Ewީٲea<5g~x„ɓ?̙;7-~mӦko8稣j󎽠4.bvwԨJM:}{Ǝ /P}:Pq^_@RRso]ڃ=N:vOϙRFӳ#Lto?jԢEQҤɀ_e{~gSۖ-APLժlyi^{E]w5ݻ~^*Uj+O:fɓGZ,'JN &/ؖXGtRڥK?թsmV1" *rH׮u&'_Ԭ٩1ԨNRj멧^y{[嗟~zܸ+m+[NO[wǓRj:{:uڵGz{ٳ7m rjjo뮻#<Y{6 eխۭ=1ǜuָq>;jԫ~o| 7fg'&-[RZxv^ګ9}qMn54_x!1qȐ;|w}g1Tiût׿^yk5;cM]zڵg]QG إK^t/(P7kփ~ʕۙg֬YF׮<km{}='~K/=o)SzF[kׯ߅6n{O(/uvhUVs}9jb޷٢3[9穧JJ eXE#{[-5oaҤkT99wzŕ+wa?N~w_twǎyWXASr%M{/I3ũ PRŭ]vڵ75uI'=ܚ5f׭[m0sin}iiAоĉsNP e@D޼矗/ߴiW5esr6m'7X<++5y;5tQۓؼ/}=f#l#ZlذjդmVY&O~>׬۵G9uW{8WPRJ*g D_J%իOO~>3^}u֬ ӷmKNTNs Nh 9$P`"El3[-( Pȹ P"= A D"H$J={ٳ-L $1=====݂(qD-ߜ9s̙S~ᇷiӦM6#<?O?>nܸqƍ-8 'p 'nݺuY&P4rwm{ڕ'n޼y-׎ϟ??|ܲe˖-[9ZhѢE 4hРL񔯛+ ̙3gΜ%-/svڵ+iA ʒ%6,Y2z_=f7ߌ]}O?um{oݺuk9999%q{/{ +y/_|7x7ޘ2%Lł ;)S?[nݺuSN:uJ܍cƄw-z.hɒ%KF=zpuԧӻw޽{KkZ,boom۶m۶|'|rrrrrrgygETe(ga>`NӧO֭[n:<\aÆ 6,|MZ5jԨQZjժ ;TRJs׮]vڽ{+曧O洴K]j 8aÒ'M*UTvݲe˖?_ b k~kii#GtPrt7xkM >}ȐyV7/.SC;lժZ;o~!-mڵ -P /ÆMt&k׸qvC:thiӦM6mӧOoҥK^lѢE23Wڰ!'ׯR~ooСC:vرcǿ޳ '&&&&&5Cf{W#۷oy͛7nv<ڵk׮]srrrrrF>H_|_,eŸdd^c_Q.}9־Gm>NKO>)p%7.YvtӴi7Բej-[kWNvӦ3mZ׮99'nڴiӐ!))))ii+WXbn]^NNNNレ͛`~ ޷ܹܹW^W^٨Q,֨Q__ٲAP쾚|e}oq}+pCʉ͚%&&'%'~Wrr۶ֵinݔ)?e++\{mn^۬Yݺ͚e_݆ 6̟zᶗ?+gXtƌGرe˪U[6Sa'%/yn顇n)N>9bN?o ;aùs[b޼;:mݺY֭RsjժUV^zwqeKRJ-[va\r`ʔ 33%%3j%Kbɒٳj#{lĈnj=an8+;FrZ ظqƍëJ<==ONFtr0hРA=>`~^S܄9ٷs^RvwܹsÊ/ / zBx{ߤ`7e8ou{N\p=|=3f,Y2cFZڲeii/_G9xpXn%ˮv/Y?ztҥK?XRRRҢEgeee͛+Wʕnާ`A,Xp 997pqq_FԨѺu\\֭ZAQ=9rȑ#y ML];1FgQ#[R͚*ǎ}YrQx|̙KΜ^_O//yWϛץ˫vҩ_\۷[}v-rs=3c3fsa^!b5c]74}M7qAQo_4=ayꫯ~93111q:蠵k<W^­+#` #v22f%K~Re*Un[j߾E-Z(evK Sr K.]t_ y m1@q GUË*TPB>|atWaÆ 6<裏>h)Ǐ?~eΞ{0(|\VY(ʖUi{~=6x|q~]Y>+,_9dȐ!CާywygQ,[]޵kwQm ޽8w ...n̘ҥK]= cn1'W[bŊXlŊOs}A{a0lX\kرcǎ7nܸq/℄ڵ*VS RSuKM /XlقUs\p9~駟90k^u /^hÆz#GEAoi Əѣp~%-q㴴XjX,6-O8֮MKKK嗴3v9w.5G*St[~T^zEbbk׮_v/~ _|1a§ΟLJGW5^ aUo+sTP ܣwUh_m% ,7 a'~jj׮]A,m4a„ ϟ&;e˖-s3f̘~bxO^쾋a>G;N6/->s]ZFB%,>Ǧ{7Ga݅N³Ͱ>Yh\~muGyᇳf͙wA^U1ԯvm,o߿fM^ЪUvvvv8?bDzzzz}eZڗ_>|zqF898zt,6z /b/oʻŠAbEαXw9ٳgϞ}]|r]e_lR'%U#u$&.Zh]w]aQm۶^*?kM 5k{dcy ῕+?Pʃqu-юfΜ9s'0 ;X,ԅw]O7T)f̈1#-'¢@۷ovv5gΜ9/ܱcǎ99:u{챹{P!..*Tټy͛a~i~)>>>>>> (rp\iӦM65o޼yz~??1@#1;`a;ٺw޽ 6,1q̙3 '''gӦ'Nէ. Ks9s‹1ȡ_ξ 4ގߧd.noS{W8޵|\+Gⳗ(Y(?{__hflS }~a.]tb+Xy_cxP4X>}7bN. 6mƌٺO>} skw 4!*VlذgoR'[3_^vT sr>sοMƍ۴)JJc CYv-o^pcҥNn=]ebSfͣ #,)E,_|:Ǝ-$?uԩキ~99a5j(FYX Xԯ99S*{7mZ*UTR4@]+W\reع0|nݺuDvӦMu,O޽-.%(sUgP8ѢE- Ëv,K,YO](F\g @~aqIْ.2m. Sxˁ⹴:uZy(حnw:ˇL޻w6\.®-[:4%ot1cƌ)v2}!CAp>[Ξq#G[ww߽fMaoYY>zU٣F]tQ,6wnAwYL2RZjksϚ56iSaKf7(OiѢ S&O.=Sv[,\ةSN gNyr ?5!,S|*%'''''/#uEhg1vu6ה8lW)w@)nN+訣8Jj׮]oưiӦM+[o߾}˔0`S{jUq̘-Wnݰ{NMsLx  8{ٽz'nfn_|qIIǜ;]Nέ[n}[pҦM6m̞}\/oUL]tmW/ZojJZK7o^}֭}u//ACZ+U+?_}3fT=wA0}zAYC m׮].uHKo aÆ m Xh . Uk?_ {,p F \fA]%Vlٲe֮]v:ʕ+dIٲen믿>nۃZjvvyw6AT;,/QT+>ߧ5O>,;2bVZJO9rȰ6,v~ub̙\/Y7k{ ;qb߾_߶׏y#G{vٗp]iȑ\믝;s)۶]w݈99*9 q[,]t\rk-_|UzʌٳS;|ʕW]reb\A'2 {]p_i=矟_|qyAsS_\pAQlmۅ埞{.A,\7 N!no֤`Q 4h{}Æ n?\rk%;wZ XS ذۃ x6oÆ'+qrw TZjժ{:]+ܬXbŊ ,X`AP 7S*/n]m,9Ƽ슫Nvnݺu]vڵk\vްPR&MOKxsΝ;w }8-˹}aW{]A v}]aLپ2KUJ8lիWr5@8[nY:;{ /=_͛7o~_ziᬣ;r:v1(P,?wS'Oc?YGa}M4iRNsΝ Xq j ] ߵnAqkժUݻw+֭kACx'^u_Ap=ܳ|wAjUffAغ;(sjՂ`ʰs 5 xڵk.}BT ga ~Jx@x5]\i8w]R8>"''''''X]ɩ_~r=JرF_#,u*PG}G޹j̜9s̙zꩧv謁w}w S5~6);   sΝ[br:v5aÌn3&#k\v +:v7w XocǡC/y?ƚ7/!!!|X,_?'''0+;{7-WnXoeԍsr>MyyNO?5i3w!dg?ԤI&م1/uԩS;>{EʥvYgM&&a@bb:ۯZ,+kAVܶoÚwA^;WjjjE:y`Ybff<رAPbFFLڰa|e͚;үtP,\رcL:jժUYGyL{A*[\A~}8/g {N- 3?*_| cgyg~7|_?{+x ƌy[ =z!G襗 2>>6l )yfl3B&C=Y=Ƕ_tҥKltx{֭[n->E$sڷ^_CE#F1>18p`8&(5%k~uSKqa鮾[IlGW^mk%}*sޢ&?>x?߻wqD6}=^M99_~y!۶߷+VzI'tRY>O?W^⬳ʖ-[6>Z$#cܸ[nYj;^$m۷vApQAǮ^~9Q>}./:zɓ` wir ;ǎ} XnӧQ?iiii0e~ ꫯ /6|533T~KM ߰a/h ~㰐сRRJ۶}-CwB8pʿ/=, mڴiӦM~j-}eP|E?Js#]Q^zaÆ V@nG=z\@)?7GWr/+WZiGj߷m|mSR/ӽK^U|q%&M4i\+-ՉoR @X'11---TI?>)Gn{-[+N:)%妛nl~˂ejZ:CC⒓+V馜aÒׯP!-ӧqvVٷrͫWxժ}]RЉ'xbK׿;ȭWe˖=餔X׮;W b~_ݻwSvְۯ 8㌳ /;蠼tzݻn 0 ۃE1]ݟ>|>}zο~[necǎ]6;;;;rr;N23O>S̓NZ0.|_~yKO/AVV :O HHTRs=s =vOIIIIIٟ] s߲e˖oR@Cy vUǨs[ح/<́B˗/^n?6lذaC) \ᄈd]QڞKb PRk *vټIlWvou:_#ۡՅڵk׮]䎯岫V\rʅSK`P[]ޗ﫵ֶ2'̙7.=q~-[k|͚eglno[cّ#FHMڵk׬ɹ>}~b#Fv횓Sܒ%7&$jUnaYHMMMɩ]v˗/+kZ<+<_?Vٳ w?5jIMMM-j5mڴ%W^]ޟoP!>>>>Qҥ ZGa@dI͛ߋb/_|X*>~cwf֭ >{p̈́w_~sr*TPQz-[lpyg X__w.lժUoۢE-[?dfVԶmNΑG^pA\pA[x`,v]deure]vٛoW_AAv0Ě=vڵWhٲe-ƿƮsȞ^cQ);ط(ߕ=@s+y_+{ևPÿ9|Aa8?Km{%}J=hF׹v"gk9]W\qEo߾}1bĈ¹n曇}/̼[w︽ժ̈́7q^XTR۷o˖wy3F86nݺu- oVL^EYĻ뿸?{F5jԨ@=?,7 / g۶w-(?5G`w%vqy\jʞgs +wG|˞1GXG%KNNz99RR oRRn9%%!a܄m{JO/JVV6ml<|5ܼ:(+(vڵk>[o{VZ7o{;O=nuԮ]v75jTؙi /lܮ]vE^F1ZnݺVZ#Fի''- nR!?}aÆ LIMMM ] k+naP *BCEo=WErs,tZjժUhOxڱOSOԚ~Jne=bc4,QT'e+n{ZE򢿾!<M%ܹsΝmu{$^AP8%Mrrٸqڴ˕˔8駷l:tа`6ŅAN-/-Zظ_lف}Qînj3>쳯~wEv',sッ꫷ݶoo?ݪUV7N>}zٲs9/ p_wMiӦM;:t׿vWhw‚?:mڴi=љgygo n|[^xaٲqqel\TR<ӰaÆ'w޽'ѣGA1‰ܵVZjJMMMMM- ?1ܷ.) 뿸_L0a„ u7tM7ݔ;!߱(И1cƌ! rQvqSr;ͣg%7`W5EEn%ݞq3/Ev}YSX'<_Օn?C[ڼV-S+- oV^ziȐ /\(>~^ؼyv綧EBO^ע@ϥۮ?=+ W"E: o'Ati uazM|'|1wyM|ZtҥK~?1^h/p_؛`]"\vx7x㎗˅@x۴7p 7ȾܷG lSY H~›^wv!O?yrO9 jS^zaٲ뗒2dȐ!;o9%eܹs>6o'(n[f@a(u]w݃Yf \zi:(Pr;vbW/wEΝ;w{ \w]ٽ0Oxɻ( L9F%99ׯqiӦ]e 8p`\_Q_ueʄ]ӦMqcNNq+/ T| ŮuũO?y@~]({as>]h[7o_RJBÆ ¢Trʕ+﬽dggggeee}/r=999ݿa+/-?@Z9rȑkΝ;w/aU<8K۷o_һ$?]l=W<;6o~^(S+"!aܹs -P!>>-cQ5Uɹl)];]hɒ+W Xx:sQcϖ-ſG-\wbѢV'/v, e˒%[eeWZj|'wVRK\,SP4./aŋ8`;K }_̟_\v, ԪU,3bĈ쒸fsR_]wꫛ7b;SO-~/ APj]@&loG؈#FU"rzW^y3333O:餓N=i=WELw?A@AA-, ԤInŽW_}W_Ҷj* (w>/Uys@%{6Edqĕy1{ٳgϙ3gΜ9w ;?eʻNt~mڴiӦu y'oC J233333-t $"@ A D"H$@ A D"H$J,[tҥK-t֭[nݢ,W@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ A D"H$@ŭ]vڵD+ A D"H$@ A )zֳg=[ҟ F5jԨXbŊ=YzֳlI nڵk׮ ֭[ng=Yzֳ- 洴(4iҤI&۷o߾Ma/]p`e@A믿{?OرB-4f͊o#zᆇヒ/n*55.x{i7T~'|Ç?7vXrBCۏR :{̞={s̙3gNy~mڴiӦMxǏ_*tkbh5kmZNmwۏR :rwxU{#Çw_qƍ7$S:Oѣ'pKvvvv&X"}PFj+XjպuE%{®5*o%|ٱ+v5T)Ov챕*UtӦ_o]]Vd •[^:իkr+`U!;(>>!T2e*V]E{5߲l$Ѷig]q 'yGpW|٦M h/%ێۢEyb͛~[_SOmjNMMLk\qW_Ov[gѡCV TTtJJ 5k6kvgq>:}۶err6o_㎋.:nذZriVTk׵#G.Xed,^׿·֨QJJJJMZQܹ{ŋ32b_O997Λ7f_:hQV ))IIeV\^'t˖efz*{™ZA}\8p׼T۽ſ`…K¾nܰty $%A,y/̛:v}]zo&͚5|xN*L(WMoIO2e/WNm0j7n9s>㏃ %eƂ>ي^()~㦛?OO/UBz6m 5uժ[ƍ}6p`>]6m͕+7kv{ߤIjKoӧ>8`ĉ/8a}MkW^٬YJʎSܺuW_8;!.QO=g.kԨr-[VX`YygܸK33Q_gzl۶7\:1n8 {o2e,7x'w}㍩S{l^K.f͔)]שSnÇϟY򡇞zj>W^٢E[ ܥ3f<̙/t饍oJ¶ZS+5[bM>{OJ*]:!aEůe;*v<>..5{lѢ[cIN5ךZ0/.'gӦ|~Xr˖=_7/ǿk֬oѢLJ:v|e˲+W.!!5eA&N\,3sWW_ݨQAPT:UU&N<:uJ ҥ54h͛ cl8{Wt.]n߾lٟqde^6nPb?%q`溠Vk(996|-ZVԱw>QG~۲vСCaæM~ y)O?Sw=t}RRÆݺ]ye橩Xzot-"5?,gΝ[SbŔknݺs>==N Ax…&_}oߤIjeV\vڠA=7wҥ_pfg];{spCϞ''.RbzZo}z//[uߛz̙?>h駷mۨQx)zRRrժ5n|ݻ_s͓O~Ѯ J /󧞺Ν:n݊SRJ.[F<唿}̘~ز% /kO=֭P!9teW?N4nOܬU{G邏;q˕KN.U*9|57?昳Ͼ֗_T {k׮qU˖-]:9b?{!C^{srjwmp_m?K̝W]u)[׮]|RRҩժ5ir1}[Kdfn |k2?G?֭ԩP!991L-n227l ϥ +^{CJ*UWu?}w˖e{=M "]PEQPQPPإ*ڮ^.Ć(vEPt鐄yOܝ9mgq8FX _wqQW'l[cDz~$›o-%x[,ڭ+ELŀ1&&NNNN={jhPYK-X0n\^d25J~˖YY1RiigΤxƂ۶zUUeb2wnx[x<\bqyyjjyX tq{?HMؘNGҚ܈DijH$ֈ'Ndg׵X{挗L"ih rPzzE))G^-:̝K"\,YښN/-߶-,,5/ۚړb[֚m)dgV۹nS(_,D"HZ[_05[8\]_EPUyر_5zq3g:;?}z7nl:ysaWZZ"Qz#>>}̘qR~>h`пQ3fdYYϝ;hP\ܨQ,qK_## KNt2g++55,??1CKN 3AUU99OOܷxxz:yzN奯 ODF?7nDE=ztÇz{< )ð1c rgѣg4(.و[n4DIΝgذ5k{5{,g碢gO}{)S\\ʴ_|X.4N8EKJrr^u+222rqn~ .ʕY`a{5m@oԨ3mm$3c;lj]%%ǐִg糲,,F5 ϟXrIDAT޽W ӫ)=ExHˮ]#F ]^nc9fÇee<1S(\ĉii6!H ⪡R k"/xjnFPO|SyȑZZv"<{vbffu*2;GWoˊ'?|(-PUWqݲKo6ڪcz崦`n]c3'pj?*@Jbcw숍-,$ M5522>=wƍ"LIs}]];wVSQZL::7n;˩S EE..jj#TZT Q:ﯥUsS2`„#G쉎YLLPҎ5…Bu#Ï8˭Ū䲲*!UUtQUoB@`;tק̨Ǐ-37'ۢdڣfe'OBC'Lس'?[]:ؘBսXyEGb@kn$|kW~x<ܿZܲ1WּADuGPcGl6))ۇ׵Vq)Rjj`XY-[QII%IIÇKt`T5 T,ywwMMRg̈ͭZ$#ǫ3:TC@ho.,M盙h8vLnnjː@PSsv޽'k(-m>{{%%<# UUmmݻTV/""'Wr˓~TV&%䤪J >>|-ٳ8ޱ…׶=0&&8ܜFa(>Z!!w5$7ACcذ 򾗨8o=׮/+]+oP(dќ9^}_/޺榮NjkhFEoff xQQњKo6ZBM'VPn]c3[](LN￞=Y,H9^9АLFKuV1J9LWTsJy1W/ 6wK/_t|__.F# cWQH$ jKֽ}I|6飤N2OzFATT|9cK"!neiSRR}[YYǎbpB"OИZZk=¾䦖2ŮIqs昘}nߞ\Yٴ,ݨݴbP L/YY|ӇQV&q82YGgϯl'k&&SUq`L&o\\yy˕Ǐڲ8]XXwMxxhj_5[CN'A6oW"774f}hVVtEm͝e2<|!*yСYK=H/ٱ}B嵽tw~nۓH ]swh$/=?~9=k>:]nywplɓՓ &"{ 9+Ǐnj!t+' 'OקPGzM__B~Uw]0x;EE#9vބm(^M'VPn]c3[ w޼ٸNI@@LO1UZR 8O$NNoxBaEEN'OL pQ@ϓ2PuƆ >>:X?)[[_ߕ+}$/=٥ҧO-x:cٳw7sg,,?S{KJ>GWv'^tX\XxffSxÇ~ZâUrSKkbR(&f ##MYoojXzp֭[nShOʴ V!"QM ̄Ϝ;‚Fk4Azzu=HdL"A$>?7W hx)Av͛|LFٱRˉq8%%Kˑ#\:V ʺv-#ϗhƌUEq 7n|wh㕕ml ik^̼q#;rM֖!3xp}2d2\TB 꺺waӬ%+'$ztulm=jK][}t33 Ei-ӹ\Tn{wz~zv@㣣 &:`Sӟ%/۶|s`! Lٸqls/^\WPbju4ݺuJud;\z@ЧON/_9&Xwf˷C&,#mۓsc+ (Z_濵[WuE̖h@(LO:uЕ+ccb 9sΜ9qRp8@@p8U!C:}z͚54GwqcdXe:|kC H$߾]`o?jѣ**˗GF_?}KNRPCtKΚ5mǏwLŋ t}iu޽CCO޵_b׮Y>~̮]WX] p8%%[mcƎyk 6[ HIyK^ml)g d2tJj SC40ؖJQ~լd(,^s%&~}nLoOdb1,z^R55TA\>_"A@k`yi萲ЯP99 V,F㾪 SS_ĉ$Ù8W}PHyyߺ{7:Qii5Vwm6V 8 FM/ݼIINHZZ$Җ-oFD$'gg{z:X;mD$vvÇI7;տÇ/^ZZ^^w`ˀ7aiۣ@KEEڭ-lcfD(LO?yr4')Sÿ~50<;<G+*RSڞD ʊ~ Ḻ8qǏeeM~p|F/ܸ\:Μy&6W6-ܹoKJW*-)yH[j = N72SU**RR.]̔GM PmGF{WRRI%%oDF_ׄOOT p-fچ7pD"_e{mѻL11}㩩i`ʊBpШȑsbb$]G64[&m+ `-yڭ+lcfC(LK;qbGG4?iұc11wao?l*x鱱߾}צ27޽_++LVUUM )55tE,22xT!蝪ZzhroM޼Akey <[[OI 呟TZTَyyRy?hkZڮfQo`,-=='N ~~C(CL2e„ i4NV(z5lnbux׻ͿXq~Q:L8!C  OJ:xҥC055"b)@M--g@[[UUϿu+8xOKKk'O6oϗg9Ldϟ1ŋ99 j۷۷6n| u낂n.(hR>?##))1111))#WdH}k zAHZZ׬mjPUUR}IIUUKJ45dEԃ׮s7.tر66lvIÇkse%en>~9"9vބm79c%l%Z"[umlO|O"O7qbXXLݣG7U|MTICY21qШN(LM=ujÇ|h4##o㍌i.]TT>M6n}۷G,Ͽ}{ÆM>| ]jNI$7nٓBzSKal<|GN/]e˻wu DBa}L ĉy&O>{67J%0pfWWMM%MڟN[lئ>p`h]dǎ ͫ?07_p%Kvep8= ,Aئkm 5J"yym8v_9:hѡC jkbňqq :8>mgٳ4`?+mm{׭2O9s^ݰS//OO[[]],336…KMMCkl޴'jjo'~oȑ: :zjjRX{֓'d#߹ࠤD hi ~yyM|֭CX[Ǐ5j>df>yQv6p>>۶8驭gn tٓ'[Y;j˗{98jnN'$\tܫWjjFF-;޽6 nkp-qqG٧df>~ybb"3jqDK'N=iO5[[;<:>~Ĉ@"شi^w&:h~45 ~Ŋѣ%JrSN8Rco?KE٣uͥ8ݱff4ڕ+Od ]ֵ5"9ބm?9c%,%z"[uml ,ذ!1M]WUU 6̚-,,ټy 4~`˴i'Oʽz99ikpuuoh1($ȺuWϛwq66>:5f̀ Fiw7o>}۷ ǎ-X`aQ] 1hZ?{aeСNNQw?RƐ!Wo8'̙)SNf 3ܼY**NN6<9lk8;?}~}^hڴWSTVسg::tzUUAϱ׮]iVgf6yrXŋ ZZַmiت"JVm< ig b֭[ny͛7euPR] ՇD$JO߻NI 'utƎq@"d2KL<~<(hN 2Lf2,,>}Ϟ;w~ת1W|i>,rBhر}hd29jҥg~vႽ>>Ϟ˚MUUaalz$H*+YY99rٳBVs纻w媪$D1|y{<|[U|i+E_׭ݛUR" ֶ8pƌ}ZwP|ZXUF#>k\&H ܖHJJ=۹s HDO{WC 14:]Euzubbe [t(SӐoeEyVh9cъ,imi[u寈8jֶ1,ϦˑJ++32nشiʔAtuTBa:vtp]:2'~UVfeEGߜ9ÇmffSD"HXzϙm[ttVV 쬦F H::^^O֭s{z`x x۷ qDue}''C?e<@Q ( H$))YY+'$xLVXbYZNꪣt RQԩ66>hggccbID_rZ\\NNUO``3`J54ww?~<::>݈GΟp8MUU_biӂfZxl77##9GnizELeSCcP)Sl9{ve-.@b&-G_g@ig4E0MVoX!h;9p8 ?mRiii\ΝS:8kj2d2ǃ=mϏ m *+CC-,h4&Kۿ|A"yɊݛ={ȠÇ޽'MZhŊ+CC/8˥PRހ_w/^̚5hAfxQQ;? I_@%4Baf xѪUnnrTճѣqq?BXxaaLfn߿m۸q::X(xPۣ^ k,IyǏ Ϟ={ 4ZyD۲6А~kiQlwM+b-355555U@ #eg_rԗ/.^ rT OYXv"YY|D j4+Aw`a2~@EEEEEE*h w|ȑy e)205wݺ33B~}ȼynn2Fٺnn!!?u"UV]_kjdR Anaaode`/gUUn͛'NJG7)T''=tȑݻMk)8bD^,BX&&{{z8iaXXmfd|yܹnn66zz4`hiu4dHHHDDR@ !L}pXpa]))Qd2iixħOL&\iӴiw媫3T*fkk[X88fͅ II*_ã{wee*vUb;_<}s..J$;^Seew5-բ{{b 6g={ް<6vT2Lן8ƍkתkxnqL %%9:*+7^#Ct~Hq&D"iiqDvv]ssϜ!I$ !CJOgoIQQlAAffZZ,J$HT*n`Щԩ6ܽ[XXkl7TcZ{%%R;Ԥon޳gb??WML45Y,*DRQ{3g=Y"). [޾C--6J%i4ee}Ν ]̙uԨ8kl%v/!O|k'bms쪹c y&m Ylݺu֭7o޼y~ ׯ߸q-^xy맧G8xy;) VS>< `%K&LppեPp8MݺUX(\70-!mm i4.w|V;83}Lhb` (+چV5TUef:ԫE~5R3ɓuuR(;)__##*To_s-Zh\}KR'LzRQiذiBB, 4ij]r'_uСSV_9p^i`0iRttH}zHȲeKO٧;`@ݱiQRK~(+D?5^o] ̱;!!!!!!%%%%%7/!!wŅ114G hh vB^X\iD_V}G(LNޱwo6ᔔók~ꪦF p ղeԮDRR%:18T;bqvvxlh8s M~9th:޾]XU盙WWws;s&7!Cݻ?}jD}핔xHd0TU]\vNLRY_ϔHbb&M'x%^֬yGM'L#q8gSSS_ŲYE{ϥlvϞkǗ5l ?ERiqqL.H&͛We28')''lԧOpS޸QPP.M 8"At+M>}oH,}).nbĖԝ<`$_k{w*+qrRU%D]]KJ~bׯϻkjtQQuOʈD%%<:kjTo5LJ;~yۚ=֚G^m4Miv1@ hj_^PPwRH")(vMHvv'O,L66 |쪪22ޝD"+/bqv㶶l6G hiyxDG=J oDwѮ(/=?~9=ǫ_=ࠬL&O\fh}-6oٷoWѬڵ22;˺ky0Z+C_"52Mv"&%mheE#nm))?*yce]?F$r8~~*7v-**^7ؘL&9<1y#գdzٶfdkLOd2E,-t=Z[@@fǎXiwq-'[cDJ/^cQM̟_zl'jՇ oX/XDYcK.m*7F {vi+Z`:=sgee|'֣Fq4˯^_-çhh?D$XFFL&+-T ξy33ϗts#;vTRҤ68ȑ\.~b~~}k]Riaa\oxCLbns۰m|LFu0zJ24Ѯ]{*3ƍ 8 \_L.]ihX̨rc2ϜU\[[AJJyT Ba^ޣGyyBaPqp͑?;ϗHD(Q񺓯XQYF:x0%E*eɓ-,8:DGm KKlx`hvbVUD.0hiGFTTTTTT!^BHtb1T&_Ͳ% 7WUeׯMH(-UQ+ s~QTKX2o^l;;7w:8()+mT[[EDaKVQ=!orBf&yz.\سJsG{ o5- Ə_Ç*áxg-5+""; {#TTz:u+W޼8#ի7 ;̬;Owu4ʕoht'b SS_ĉ$tgϢvش׬Yjժ+ut~^hE߫b1#4eAk+`66_Ѥwuڻ0XSem9ڷ Aئk-0秧;mII_QJ%%oDF 8n`0x0SBSy8WW}}:32.\n{igd=ݻ=+.VR- K[+~}*M_ՕáR[qh433&22Ο>M)JM[.bY?[mExZ˖ +%&s'7nl+hl?##))1111))#ϯ]VyǏ>zTXb衪( a_|N]]-9T4hj//}}.''22 `ʫWss.ÇGSV]_ .îT j6Zñ]gh(}{s޽[TD[Y͝ob"_Gֶ|T7nٓBzSKal<|GN/]e˻w?2ko"ׯw>۶8驭)NY_-[Ο_6+kذw07~a0ݍUTŒė/cc?~<5AMK{WSS$j ##6:>ã.IFm޼waf mAm'|G?źE4Zg[ZeA驯O"鑡5-LsrΜ 2ĉl6y߾9sͫ'fTT6l8yr ؼ�׬qv~t^X,)&|vdz=z ػwǎzzl6}s>Dsw`'k)ZZw t#>Ϟ7<˗sQ(lv>ǎee }Ke2(/ݻuwޝUUI$NWS34cyy۷U㯔ɪ =;vl ??g.]\55&L&D*21uc߸ԩb ߹3g:;[Zjk3$Hۏv+镕 ~Rmhr~*_~F,:zM9ZZ7oJ%#OԽ{IGܺe#x_W,OϜyvaD4{rs7(hܸAw75b(@Pl ~'߾-/Hӻk~}ekN׻L&I$%%Ϟ9c F$V[^[J"ׯ*)djk[Z8cƾ} iiw4FMyn[UUX{`pg^FFjjt:D$RzzVVNNǯ\ylvPWk.,]WNyuۥR/1;ub0d2Ҳ8p={EEwIHHHHH@F3f㛗o %ӧs$0ݺۗ^_".o,05P=h݀ۊTB4(,uee\\ tO~ۦ~] 6HESظ~۷&*'&>~|ʵkqq99UUJJ>GFA h2>@4X!~C` "9u&I&x<t~i) HiNN**8\kҘk& lqQڂAڎ 5cWK-#߻7{ȑAA {Ohъ+W.^At}U(̼p!22#72Z͍!@ Ub55n?;i|ڧWDR^cB³gϞ!V^.綬 4dkshE[+4MiTyj  K$B ^w ,,gKE+WNAŋG'@ UEԸQj^ P~kiZEPQQQQQxdd|yܹnn66zz4`hiu4dHHHDDR@ !L}pXpa]))Qd2iixħO|LVson޳gb??WML45Y,*DRlmm ~Wݹs kkGIJ%W9qZTUwo^l6fgOzzNj`@&'޸qZӣ"Ct~Hq&D"iiqDvv]ssϜ!I$ !CJOgoIQQlAAffZZ,J$HT*n`Щԩ6ܽ[XX?l7TsNink-CJ޽ jRSBGf8>>,l"//{l*Lє;w0w3gUSz%oMDxmsג牵Vˋ_E@PQqp8>/O*}qp4ʕ?VQ׷uuuͶ=v,;)/(ٜۖ&3fu֭[޼y͛:~)"(+/ˆa0LLzRQiذiBB, 4i*d.wWccnutTWWQ:t+40+ &M.,HꮛӦ-Y|ҥ'YY))8kWJJee]m &p8Or8}xy͜pe͞ի&6{IF!|rKR T%9@|=Vꢪ*3С^X, $(k! 43}k-U??[[ yRYzzvd:9.]-X0{v`cBacBkH,ܹAxc5k2TZXx󦧧իDQEDœ_ϝ%UU']kL(e~55D:s&79&xl5PSgϑ#,Xl%KM0JSͶXÇޘzo~-;yGhjժAMjF{l*7ܹɓP?'TUv>|e˖. :ӳO.WWwc{Ӣ:Zf |k~m{{XY"=Ol0"Qjmt:5rKOeq8Amڱ,6ח!i4K˿_)/(Z\ܜ6&bAsf㛗o#vwI WRq8"PUvqٽ;1ϯJe~""rr+''Z9ߩLJڳIU@ uu}|/)}-՚1VV˖=zTRR5+<|dIN [IQQL̤I$ԫך5OX:0;~==2czܾ=5xZ3ҭPvvxlh8s [ >x7d wݛq8%%{v%9th:޾]XU盙h8vLnnZ/Q!!w~X\XlnMCcذ cg]?dz={]_VА[$x{dѼyu=Q&srNrrRQt+?i\O$cc muoxR)æM}() ;u?ko&p8D"VV6NA.t}Ģ}bKN{chXB_]/z&NGΘUHTRnZT1Bˌ۶7[k-E{+7oML(2hΜWjN޺榮NjkhFEff hUbE (4MifL62;˺窪<|{w&x޼}P{6oJ$߾]榮N plvGfe^_`˟@tw~>\&Rl;'+hR4"{-**^7ؘL&9<1y#գdzٶfdkLOd2E,-t=Z[@@fǎ}Ģ}bKNջ<)ŚRkڼ6|}힭!StQ-3F_lT;o !_SZ1::neqcRRiɓ)mQ^yחáPttݽkobŮ]f}3]\f 5ܹtON_Q񺓯XQ^"i5zۉ]A~;o~m{{D֑?ܥ˘14ڃ>yy?ztjv6oh8wn׮jj**D14g11_DYY>S=z5T#lmql(4?gμy!!ؖs[SN}bP'N$%ߥKo>{cǦMfͪUV\SWI" n$z5Kc.^ow:8()]tdׯMH(-UQ+ s~QD% '!{+w:j-+˭z6=Je[D$ti߾jj/_ٳBaII|-.df"…={47~ЖbLVQ ǯ_CUh<޳g͚]U**zMu+o|Z\ՍGkfVPpΝӧNtʷoiz_Krn{` UU5z_'o/mMiV'5Kig-l;^15$.TZmDDrrvקN}"꺺::jiHDzzRizs YYgϾxQTD hi9:ԲqOwFiiX{(?_(D:ǧgOU՟!HIt)3ϯquקq8>?#…됖~p|FFxx1x@qOh֕{gJJݺt] \miiν}[RRRiIWFwupEG͕?+yʍNrpmӃڣT KU~x5-:TWW$JLܳ'Onظ1:W"xĹs;ugy{ oeޛ6y ˖ݹAxvqիߟ"bkohѣ/_׮LXCII<^{󾖤5u'M;G$ ڛ^hS8ssrtN HK;sƯlMaɟ7Fh7NضN],HM-/kjJ ʊZrNWѻL11}㩩i`BpШȑsbb$]G64[V{M5@$2;UVfe]SyyMdϟ1ŋ99}D hi9;ڪ"H~[7?}ZZZ'>yyspptt~~_hi z>rի }WUUXãGDmW-ۺ|Ų CUUP,WSC;w֯ǡTZ\|w }p6UNG͗D[r[.(QWogڦ7ۇ oFp86kאq e/_v>}ܻwt+sMLKbDRPpFP=))Tj>W:dIÇo~ԩ\.ҥ>>[{s*&}zΣGuq⊊u *;ڦ-;y_5w*)ynkǪ6s]\-rt@˜+]˫UUeeYY߿.nc켱Ŷ(43d!II^^&FD,]!N*3222'NW ijcifD]{NAai9n\t#cml쒒׮ݵJ|xss,wk7%6{t9 ͇N77={d+cVzr^77]])KΝ{JMȈ8~<%mm{׭2O9s^ݰS//OO[[]],336…KMMCkUZ ܯߖ-ϯ]5l;vxx_0lK**DbeeaaFFb˗? w殮ݽVL\**>}:t޽ot+USk=" -?//;ɓO޺ukQFG'O"###=F.g۶<=[?働D˟029RIرaì=<꒛T;j{Vojjnm?bD]RH=<6mڻݽ%)-:t=rΠ/ p8'OO}}O ✜3gL9q";vv޷/<|s' N?6ooo5k>]W/ k >];|lv8:ݱ EE>Ĝ={"9;2Z֝<ȯw:h~45 ~ŊѣޫJr=^hS:^ijo'~oȑ: :zjjRX{֓'d#߹@ V K~14ΛضW1qw?~<.nBWϽ44xo:{66FssrP^?qb^!{ii mҖ=o_Isc۷?򥢂ںfQ8ݱff4ڕ+Od ]ֵF^ߴ(=+ǀfu֭[޼y͛uRܻ訬 x^IIsQĕ"ׯ*)djk[Z8cƾ} iiw4R)rBhر}hd29jҥg~vႽϳgTU<{v؊~~]pjjL&L T*ebҵcΟqSqqbq搰gKSlx/12P>}՝@(/ݻuwޝUUI$NWS34cyy۷UKH~߹3g:;[Zjk3$Hۏv+镕 )nRIN>{vɒ#vcd"FSU52Y4n6[3bqVѣhʑ@yWw7U2-~x"쬦F H::^^O-{z`x x۷ %ٛHAA ԽEfXX}rVhf>w_"QQѫWEE"ʝ:)+K^✜KVt)'@0181$Kރ@wߋc<lt)/?E[sc[+x7ϧNquAWo_GG;;MM*U$OL|ʕkrr֯?|xwjCv__4@ ՖG(LO?~|]/\8x01Q P ?ؘFCj 'OsdjjNgw~Ygw xMyL98@P.?0lTCC__wǣލxy TU-, 6-(h֬ŋgvs329b -RYvM[ӦmܸcwV1S/_Xzٲi4MNFXjFa?ʴm*+v/jhohcOҥgO>kkS D,-----]\\\\\@@KQZz!wEwNK{AI d @sY~̙7/$x<,(((K"#ǎ;vب(+vݻX <޽y9uM+`yoǶVM[ZZYuZVVVVVV_;Vȅ"ߗ@'?|xcr6 6BiNN**8t~i)Hh9~o+ ł;tܹfc[+ML4'H$:]YYWcǾ}.gݩ?ˣ2#-[fps7D0ݻ͘e͛,KoFDXӿǎ::JJ4`[[NysttvPضbG=+inW0ZT`ĉ\. 2X}}ŋ[L~jTԫW׮n訢V&X={=# tNIyǏ Ϟ={ 4ZyDV"޽vҥ%O"))뛙qLo>4%&o޿Ew1a¼yFFݻO>jׯ?|?FE߱͟#!x1gθqxvvNN#G:;jhII\mW_e˃s皛haE˗m3B&<^K%d).wŋCC~N#s3YYTjΓ'1u]+ee;7gΠA':;lW@*(奦y{֥KYYBYTj{y5cիD^߾#GNyr… ,\}½{{GFNb`@&_[Ų4Ν =ܹӧO71Dkth_6*h>9_LM;t cFqغu֭[o޼yͺf)JJstTVFQVvtw*+SRupPQq8&G{E" yж4I1111qqIID< ۶fDֈǎ IK;vl--"gmmwHM~ YnP(݅ ;vq8<^EĉlIeeqqwd88}5ۖH}zu0 @:ʕo$߿_{w@`2\ή-% ^?ؘLFz۷(v dq`7<^Glg62"L=w.;DZX\Pp9&&T*ܿ}MqbLsunݺu֕UT))_deݺu֭[5io߾ZrJJJJJ o^&UP =<ε`2e2>?#,߳1WxMLLfG̛fm(+hT*km3؅ؖRii/׸q}jh0T*j`Э{``X؛7YY11-H-AKͦRdMYY_s|}W_*mx<l-|ȑy e)205wݺ33†47ݹӧ;wΘbm()QD"ޱ՟>UVCJA珛.`\*ˋ_E@PQqp8>/G7nq8FX _wqQW'l[cDz ojmѿOlCrZ_xk3`8999ijA6o leeJʡCw~]VF͞q1\n/~d.w̘g63P^ڹȑjAA ׫6 ү_`eVVLTZZÙ3ii<^C1ٳիv[:|xT## VDijH$ֈ'Ndg׵X{挗L"ih rPz_z^ޝ;qa!hڏ:HoX`۶WLL Cuu'k#B׻lm}}t*-ML,-mKmV,OI9zڵlNaPww]]g)dʕtzii|maaPs= SƦ;ל*h֊o"=}7n<ٹPSիÇ RUkmi-S(=>}f̸t)?H40Ԩ3zpd4(.n( 8إn bׯ%'] 3gLitC%'᠃ӧ'O?<<=Atu<='MG'O"#ϟ￀7=:rÃԔaXؘ1C\RQѳ3gTTwlDĭ[[TRtt3lؚ5{=ksQQ|ٳNݾ})..ee/_<+kix>[z@NNN⢥ED99_ߺvqo?}ZPwʬY AG 7j̙zzcƎd6l{5d~tcHkڏYY#Fxy͚zGD,_qޫWƆՔ"I$e׮# ./3fÉIJ2ؘF6l KKCUCE^xUnF20R<)<ӧ#D")(xTXvի\ٱޖ'O~PZJnke#9$mU97>iMݺ6f}_ɩ$ >\_n@=jmkwξzoVQ_QW8@e'OBC'Lس'?[]:ؘBSd ٳ':[7g;pOg21A[(9?kօ #G92qQQ[<^RB$y{t리cˤҢ;bc I$i|}MMqOϝq#669H&SR20ܹo_WWGΝ~)&| gg7Νԩ"WךSRiQQ||Dė/|>GvпVko]V*/ON.+B2YUKUN :x|\\ffT˖7G"К[ ?կ2++"/l̕5A"Q]}/t#&'؁.ᔔZڬ_K)WW55c0-{d%K:ub0?]*z&Q3fDEVU|HTRUMA A p4Zoئh4@PWws;s&7UeH 9;@PsHo ݉|~W*++ki_~w*+qrRU%D]]KJ~K Nq{k۞X\Xln^3qBMֲN {!Æ]Dyfvm||YY]%`ylbBFFsz}ATZXx떛:=pC=|wQ($EEEkFr, +h 97գgXI@Q^ֲ"[uml=="=ٓBI_ҤxP={X85ruU").~`:]Yoۓ++F0:: ؘBAmiݺoy}ISo((h*+?ް]Dvlr5<˗3gbun݊#FP?Opii?RcfKC64tt?˗.?˥шDx+bb~ EXTktr .DB:jӦaBW/ C-X>-#Zfh5999=V _;[z%,>}pee"#utᅬvfb;UUvd"ŕ\iX}-ZZхu't&W󺼵>dHdϯ=qO^ aî^{?Vt+WtݻѣYY5ehd4w˗u03aTͫCf--_#_hgfϛ? kSSmO")(v]CaۓHxϟ?~tq%|[}^]n8mgwd⼉Hp=*-BΊ1ctt:jɓ)mQ^yחáPttsf\ V>hD"22b2DH\὇-Moe2:`Ȏ^))YZͯ@uZF/hctҸM7rG^<^YАF7[ 9!2Dꪯ_ߕM|X\\pزe9:v`pxǓ'ee"}\V^6t.wΝ^^]֣Fq GϞxKΛ`m~`e:.GbZBy6@ rrrrrrjN4wlk{rK$<^vgϮZ5qه\>wU\]1X_ڻz7bKK$ 4^ M[FG+Hs8tzc۬Y{KkY I_P1֢J$ׯϚeg7j))Rvn..ÇOlaᨨ$/=m[XXZX,XڞlOWҲ4Mn5}ADQބS( Pݸqao'3 T4ץ˘14ڃ>yy?ztjv6oh8wn׮jj**D14g11_DYYTg?ך\>o2Ҷ=97>%mEڭ-lcfѴP QW% /m#bii522""Owr6BCiã7ovq;e2tȩS/)m۷g05 N7100wfSoWd-[._ΖHTTBB խ Zf8ѱc@=gK8pd33SR._ƍ!Ҏ]̙ 󋈈 E%xzXqjjj΃;v'Oi5O Y84ɰ=NAQܙ"H5Сx;ڸС{ݶe<^UvĈ fd,ٳRĄŪy2bdGDX B{i`p8zUTu ?>s%&~}nLͯlOdb1,z[R55TA\>_"A_Z5\IDATVA kPmb =?05u NHJ9/_~!Tw[wC۞Vc)|fclϠZ*`͛-.n˕Drppu%ly6""99;ԩ/_b]iH$">\OO*ݼܹ>|񢨈@4HGiLmo6ҶG9c'e濵[[ѧ&_PP뫫[MٳA( ;;MͺBazɓӦ99MǏᡣSwQ,..~&.4==(̌J jS#ݧIIyzjj쯠;o0~>TUX={hѬYq8MInJntN~L1|ETo{Y7&rHv6b} xt(5U$nK,HM-/k{ ++::'l7quקq8>?#…Z)- >_'鹹qt:'9eq1:jLmmZڹsoߖ}TZR5Gعs = N72SU**RR.]̔GM PmGF{WRRI%%oDFۂjptK&H㫿lJ[H{x.}qDDL̡C?xjjzyzP87~44*+?>r$&ܹ\v׮ϟ++Y,sYɱ&m+ `+yڭ+lcfJ552e,kkK(LJڵkɒgDg.^kWRPfw2{&&5'ONӛ81,,&fѣu=>p`h]dǎ ͫ'.ܱ}ɒ]YIxƍ _={RR>}V>ujɒ=߾ѩS\>K}|lyq4TUef^/^Fvf7F&&&͟ߣreeb֭QQ99?Pzҥ"hFFFEE@?~ܴi۷}۷7lش>@PQȨ RHT\,ϟO7ogss.]7ovuDq;V c[6%6n;ZС؅ MM^b񸸅 \]?޿uX͍e0OIc޽ ҧϜ9WnlcL{¥Kަ ơC5w6o55 ۷o?~ ۷oN=zKK55),LJu2ȑqpPR"?p&O>}z!CG5N_A23<|(;A\m37B:|ɓ[˽{rrƏwu57P.]:w+55#AK}M xqWWk븸ѣGGOA23?x115Ȉ8q"5'N=}ajnm?bD]^ hk{xlڴw{TV{nd4~|~Gb~~Ƀ[ZVNr~~s)b'bNIsc۷?򥢂ںRzqx33ʕ'OJKe2f]ZMKo6ڟl=ݺ6f|~b lؐF f?rMKl޼dtld4má.xK'wޯ_TرFF˽{O__R4`?>mK✜3gL9q";vv޷/<|s'89mpy{{Yz՜HPKx]>xTĉ3gv(9nݕ+<9nͧON3`9QZ͛O_my9akbűc XXT4Vrq?ƍ={FzlPOO''KKuu)(^}@*2dmۜ>u=*֥?}չ)={::١^UUPslkW>}ÙMv…kOHOOOWͤYlݺu֭7o޼yf]%%ޅR}pII"Qz޽vvJJx<3vL&I<^bAAwꤥ`d2ea1p{ǧ߹'6VKd BCǎHUF#tUU.gQ.={紴 핔Hy3XUU{zedFHD"ge4~ʕgfg ?[I^ރwϝ޽;JH$fhأoUUIm[K~^ǧwo.WIL&H$&S[r3KHHKkiAmO %*j;;ccUUHYx_7L[$'=dɈ]d2HYsrZ@ 6s[Λ~D"))yl3\\ll TTh4"=E^=6D ֱՉ?^'&'oҹ3jPLMCB޾ۖaMXن匕G+;,yo֕"bYаgζmYY?GPC*V ));wΜ9thffl6B$HtˤIcEE}r2Ħ| M֭[nZВSRRRRRl|28M6 tqqqqqyߕ++; ;w޷gϦO70CYe(-OY 3h9?ݻw޽DYYYYYSSSSSSZCHH$))2w~N70`~7;n֭ۿ۶qttPGӽtX$={ AhrE.%ms[b25nH$Tj-H}ʩS_ xd2RU$x}޼5kNR9g~r<4EUy~Ec㘘h##!e2/#J]QAH o\((RKa5kz~EE-_nhzVn.HK/[6,ZN]Ⅿtw`Ehh}ڵgWV|NdBBաCSΛnIeeJݻO>bimdZI/^oiic陜LKJNjm`uG 46fd燇GG=G۷G&c0ܨ'i=f5kfyvV945}vUTT] JJ$8xxddwݻw3g_^|>UNeebcEEXܦM[訫qQMMnk'O -K )(Oo^ 'dիQQ';9or钞^Xغuh75\n݃s޺uҥ?]{p1ݻfqq--++gg;;5Qx:7occ]{l۶-[f[߿P}7ĝW^~-[\]sr<=;m"udhhKƍ5k֬ŋMM%$Ħo##^ɓ/CC׭SRj_ߎ`dD.) ps>/38i 11Ĩ˗/\ܶm*}ĸ_q{sF'/HKKKKK+$$(( W ;/////N'ZBjj۶EGWVXmc2+*=ۺUUD`pQ}:;FrkýV+(}pJJmmdz~ڿ3b=st EM:;"dݽ;cK&kh=+su#C_b1<"*MI=kwƏbyyW //o_޴%KT<Ad cǾ|i{jhNo[;CoqO[,w8ƽe/uu%%{a+:=#{ QQvrqg3%% JJk77꼼cQgq`.nEWQ{mI֑ `(Ųl|2pP;O X[[ )AA}۷j7AIi˖7o~Sss^)S|YWLfa '!agUQ*6"2ΎBA32jhӧO~mh,KJ YۗW͛zz虎eeQYYW%++ Gk ~n_*Eō:/U~+ho/w]~$ MvRR:/d2&8cʮߟvsU Zqox<WS󳡸8ׯ7mRR"x*ɛs紴XAA]]_߼):p8lvMMbX,>M3g:+KOk{-y2vݸs'f>|/(Fbowof.->.{bVt{˭'WC0iz?u`"ƏX ˓H!!o憅hNNRRǧH$iٳedH (*肘4ZBiˆFLVU;wX!ΧZ`Ε'>,-zx]Qŋ54[XXqL9iMM_Ss|7o %KJ57Th63jKؘQWfc0UU?~`hhѢѲla0pwWRj/ߟv -?wZpM&ڪ=5XgǧaêUcEyըQX,0~~gμ("bdSFbx'_ASwoϞh}Ν[',Ҥ9t}?퍟_]}6''UǏOLO߶M_(>~ɒLMMLf44А">[7:Į?a2RSo>~ĉ+WBB^HOFknf_v:hW Lx5Б[/FTnW\}#+ D&#^ZGw<CpXQY,RR?+$=TU~㓖VW'&fljŭ3d*3m٢wv]*ol,,lldϿ{CzVDbeeS_f*{vs+6g˫xm'zzX..T*3׮eg3;v7z׍ }pOLVV;r$5{M@@Ss+54Z1{{`uuVW?tСӧo m=~-ۻc :[7<⯏]ΔqP^nyYYlɦ66˗Q""d2`2dh܉+ ~>t.nEWQ{\ `0X,V6@8VT~T~ &Ap8"B!_}X4ZqJUR9o}|RSkjDDttƏ_u?90߷ ?|XZTU~_mˇnju`Z`/-mekX}}Zډyy FuuJʿ>x 22-"c ۶4OOgG?~lnRI?\ΝBt7NDDGgJ/7oï^=p`ѣccϞ]lٲв":t{@r'c0/^ܾ͌ &Juq z.998̙cǎ9t߲ZJeE2EE4Zou0W_ŭpꗻ#jykѤwKڻaL毾U6G0X\R {(\ !ͩξh_$jf&+K&c0yy|T[e{MǏyy;jm/&'WU M6qbw9Ss3Y}hHffT*8D=|*R{n[-7{ abbӧ=gtSޗ.%$wTTI tƍg>2 ~o{KOG v~[%%=ڼŬ, vJ43su=pڵS&Mg2KK_|9#a4ؖeIbB'e)-e0LVTtrYYAA?"q{sed,-d Fɹ?- Hqݫ_yӛv޳+9uIv&>;[S4ZAATTQ6-w0&EEzs`ʞ>uw?z쪪Xw'ODL`۶Y$$~s(&&[ꊊ"Hiid֭XSӱT55 ukTTiiUf76ed|sFF^^cc}}WQ! 榥%*?g}w_GniKojf&)ɡ/5,STG=\Qc\\lliiSS7V\|ڴMLZ.^ZA{C0AIm[HAի7n$546ntuUV_~ ш/^흕E$ݻ;wji))؜>}ʕ򍍱v99?2zޚJJ>̬\P`ew3vvaaFFVVS()tzEE^iiII^eg#AttNNt4ST;WHu+ RR:76[Zz޼jIsܞla( ZrH1c֯WW?{=-1c6{{YY}d̖O 0EEﻹXqfaɅ nmؠrcFDd O۷7o9qt@Ǯ?w2YQy4 ڵ[>|ط%3\]e"Q^eF/и~4KK#"gg33UU^Ԡ{FLNQƍJޛ㺸|P.\8~O\\.\;wܸ/qp9S]]L A**22"#W?|x/n"UU^rgDED_pR7n|FFff^9:CdRdi)/}?k ў0/,,-R)_5+"""""Y>cbA=+*߹v$AA'DE ɡ:Ebb;][ٍYY8p(D ɢkN΃BBNNuu9bCCZچ zzׯ0ώ3pL%yxxxd11--M_(+knR|K<9}\\LL&N'p8q|8 >>@痐PS5kjoܟW#PQ[[_`w\6w>|sVVVVV[&n,14ե^-/à '_U"3m߮ϯuR^^o P **"#OF FL2 3bkj-ᑓ["1K}1߫ߡ~݁O^ 윜 ahn|>+/Xuumr~A) jT翦sr )(Gvu51gWPP*+߼ljBAXx8aatzwFLfQQPо}AAEE8ҥ۶M( zT Ќ8wh{K[𲪾ի+55>~6446TVPĦϟCC?~YH+W,Dر4AC27nxy;4/ܜ9JJ$wEEo_[[+.nm}co_=@ R``~f|GƹChN^ o Z[߸}N\\``CCs3C"ʪ͜ju;v_oiH"BT 'tzNΕ+ǎoRd`j9s]-:8ҥڽ;6DPƍ=xKlmG2?'rr޽HTPpuݲUAn~`X,=JK)39{^th##!!K=z 6mڶ-7777'[{ Xp… ]]]]]]{p _AH(:/ GUUA-----gO_nM{`i%״}=WVVVVVgo}Ǐ?Cn>?:O2}\ aMm F @p0 `7n#F @T------mmmmmmQ CM)]vڵPÝwvvvvvvyyyyy9'.....D4mSiO0tdP*IƏ?~xD"/_+******P` 0dfffff:::;C }VV66慆B@/`(Q .?P@/'0 IHMxA0?|`x#$h Rb|w7F &'zݍ&XY!ahhzzHZHMBoBΰD~ **&FHK+(ΙbšCw}[[bAeMC۷۶Ϟ={mo64pc.MHpr?^JJQQ^^JJEEKkժO+*Xkyjk9K1QT{Ν訩KIQ( jjS,Yeˑ#׮%&VV2ˆR/W_w9;w{W_}ZX?/ܢEk:9"\__TÇAA'O_}``xCS bC'O~49AdYY~״T!{3{i`˟<9zϟuuo;Dٻ iZ{?|8ujkÇkjxx)Sf϶75UV5@`~}&99&֭Wqpo^ߺׯ߼A^[#g`iH[}tp8{ncc3{޽׮ڵKP!0K"(+ ATtx>n:p ,֭szmHuW7M1EE'N$$ 9ر$JK߽KLΦR C*0\Y} HTQ7o1c8ƼBuS )(X[榤\>wرrrRR::A?YQ|]͚5qʤI&&}ZJJt>U :KVB0HWWL?}'}6ϟ~p֎jj 2y'Noٷ5?٭`0RMM-\]_aeeor_Wب@"L\Rau")celg'# >>O%%?vܹ6f$&zx̟tWt:2eʕJJqqAAOxo>d~>g,&&V+"ᔗgd|}㆟_v RRڵyyݾ '\jA$ʢOn|U49í)ZÉ޽l٘1^^ܽjÇ+W*(>W%=ڱc; B2el++)))*z:<<,oϞ'ORR]OO 7,* ܰahmDsskk}ׯ=z(&?tr ?zPXtUlvmmjɓKy`O8s9K((h99ɡaaNYcǝ;/u'ܻHa<~<~ΕDINRrl7INظ1% NuW/>{;+-l?t }陙_64`0d44Zaal,{wHHDI?Y͛w`׬{7232iIX;vkjtႿndff`p玝DV?fWWٳjӯ^q8'mvu CC7n9sŊ{_ [۰ի 2oٲ};ǎKHCDEwx{&p_w!npWQqϞ9snvu |3:YSS11;K- `0 ˗_gpk7mtpXw9SWbԨ &F@7[һ&88oFE>u-Ҳ'6&9С[/\xpwqoÆZqcTΝ[<|xԎ[Z޽dloo@5RdْAA7off:egggWXbŢEӧL4~1޾hl Yjfvx3 Ο ٻwș3oݚ3g(l{sGPM AZ x?0;!==,,?Sn١#p8qS)ƍ?olBpjk~ in7002b_zѣϗbN/,  9XBM#joxjk ]G6CjMl~4#r@_W[rŋ=uPk߸ر \]""ڿٳϟ=XQQ= ŝ##_XAuu2hPVw\rre%x"FE04=Gݾ޷oӦ g01ٷ/5HTS[a[ۮ%$,-=]QQ"dbbN//GQiӎݽԴc#(HH=IљT><T9P N~I||ϟ{x\sCCwjd%$|YU ""zzw_#$>[- UU^Ɩ1J[[996xYNND"XP]Rv")و1ii+/`ڗ72Z~R"!#޽⦦;zc-篮NI}s˖ŋ--54deǎ50_'}op8qq#G;>Élal,&  O?^^ޟe~غp55CGJK/$f''xQR_ Lr%4ZFÇ99-cJssyyrrlL#[{GNTTWwϞu봴?+$f݆""RUrR\\EE8 &\inN"HqqX>tا827=LVS[FF; 00\ F^'O7?Klpĉ?&[H22]== B=yRP@q8DFt_ ,VHHCNNHzɓ"ѱghaaYYl6W]ӞOBV_f#HSSYYRRYYSӺu Laaddk'LT['':{A8NZzΜ={|}.tÇCB][PJw%%ϟh/19T*/otOű%%ty jwD&ʒx|}366 `OЋ]]DjޞYVV]Gqlldew󣢊y$$VnD29>>1D~Z,%aa+֬_*=DUTȷo++-SP*.~*"QQFCcX^ѣ􈈂:}ѣ>|x=ZYlsz>u϶{G31cg$?4/'>Ĕ66-ʋ;9)(O|˗.̚~Ǣ)/ƍ_i4AA5k)<ptPg_+&F$v\drBvge0ZV?`0ese@&njYji6o9y̙TsÇջ5d0** 6Ax"QBHlyvn)w<ڱ 3ްX4u>ܱT44,.~ԩ/ouuSS> (-=y lYٳGں5,%$~íۓ>78()hвEhPVPe t %$bEe[g١#0cͩT"1557zKKEgϾ`dd/;VXXPHHHJ*-mj*) nh ƍkPOFގl߲Gmq8^^ qݢe,/ێ <<22˖?w/^Nnc#!LɪUS|`(=@_ Tu W\C::Mз nd"' K@`[^R D"ZfNG'Zܚ+*jh+W&$;v͛+V\4iRw"DEyylLG@Xum`JaZ*~~Mͳg##gy{k$C$ΜqɓvnǏhh/jVѠe t4 Gh0ahNLL[АB\944'ѣ|BqtX,v3$%WӋLII77705 }.?)xAXibLq;&587Xލbb<22fIK,qٓc̙LJ!!uuOhT,yyWWw^`0x|본lvOnxxdewd2|rrw$傂/_jk>vϟ (KJN>jTVžmI HI͜)/캺sdZ_phBa'zÇ`t4 ddLL:.H>ؽl沲m۔TTřLCJJ}vpz !1kĉ""NeeZZhhBB@@VVc gKJP(ӦM"*`|P^b 8YpLKX,/8&!/"(:ŋR:{Gqpt$OP:'1!)S֭>BijΝw^IH(/ed,QV&{ 8ܨQFF7N** vyy%$tLI^^{VT ԩ͜IeB1c.;;*)) ޲#2\YKRR||Y07jԌ7#HevyxDEu qss]]Q$s дӞ6]UUTA*+SR s璒ZT s#G.usΞ5KMeFu^^Yم W^PVxzY鸇?Xş.\뛞^Un㻰XQ3ٹl}ï^]6=tdII).~&22""-A,-=xp,q[=ْ]Nܴdӧ_߸26V[P]]VVPc0}1= dʔ+WnEʢN\ÇCVVSSOOGGSSUUFFLDB"#cb޿`EDM۷ [[Iɖr 2YIi<  ^l4`)9sܜBi? ^lYyU}뿿}v0z?122DbR҇dcƠLVT'߼pH$UUss55tφJ3g1Я_Ϝٲe۶cFg$%.>k֎&ϟx[G|TU ,_>sדV b@ᑒrpyJ50ӧo {ڵzRS9sժ7m]]1YkjntNBA@ϥKw'$^RS``0d̙+V,]je5_jKHxyEEIKS׮&$ܻf҄ ..˗++607v-**:ԩ[24[tBYٟUۥ1҆oI$dΝӧ?xn]TTyy'; P,-==$$Ο |4.?-A)eeCC''7+LzR,L;vr@m퐐7o~066554޽:%)99CCggYllTUvSi'ϕ+g΄GG'$UT3h)%%խ7o_b͚E$${1a__{O堠f>>*US~׮[ظTF^]CGp""&IJ>dg#' Gl kh̞-%E$ySWb7u8`0ZZ{;cɓϟ>~ܲԩS88JK oHȫW]რ3g;榨-7:g1j}:~CÛ7 u+2$ƍ37߰meˌxx~&yx/VQڻI[{*CCqXZhk׮]vd`UUt9&͜͝QZZ@@>X,YDw^+#]mm| ?{z^\%%Att\qéOK;pnP #G&Lh{c;=0X ,X0MMUUUU55! )&$s#< ;0[W߹XgN.-deWZl̘Y?`߻zzF+#cj:O]28д $h^kخnSSqqX?GF˗""3f(/߲/<0\xŋG 7O:{6%UP:uM۷OSL}Ÿ`F zm[ZUC/ʬYkq萱hwKw!NPU}}40  99UU;ҺxŋZ=Z1|`(>?q8l2ԓ?: 5 ;#IS#r 4)Z>026?"ݩ=xpSB }_zjzCZ@[  Ї&@/`(Q Ic"#####SSSSSSB WRRRRRRG-GS똪*P`pq+h-IENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentD32L40d22n6b50.png0000644000211500021150000002724011247035000024463 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?i IDATxrHVjr Iԧת6:A?< 2S  @.\4'hNrќ9Es  @.\4'hNrќ9EsMSw 4'PWms@\4'PIN4'hNrќ@5NOs0 0 @.\4'Pk9Es  ͜МW@=WM`ygg:`(9 I@Ks@ hNBp@~3] 0й~7m9[iNέx'0 ]98M <'C @W4'tqV-ùge 6yAs@ۢgq%,[ZiNhX֧yi9U3yi\9I.* ':3ֺ(Es@cGZ48/- bМЌy̞|tEs@ Ώ9:Ds@ N 0 Ȝ[ 91lIDs@=s3i)C,)GhN$@pwf@4'opLМP\_͙\9.bqGs@A-f@s@)fOs@, U ΪHhPʇL9 b @4Is@6Sg< yd N @W4'dpopL9nwbjә.Z @Ҝp niNϕD9&Sj; N 4'\?8&ќp͞4ϿSj0 l>(/Z몵T`GsM5NyLrJMX9Gg,lҜpYM8Bso9 /4'RsZON0 %54eƏ͹v- @T!|RJߕ|Ov7K4'Z|=(;k~JmO6:hœ`  @wf2[7МD4ڽItc hNr;jW3jsjhN%fp' @g>I{4Z Ҝd+8iSp4'XMӧyNoRIp@.-]ͬNiN07&]6@N}`M iNΤEӔ}Мg`,P%I hNP6MrdBsPSmNiΛ+Ҝ6SPܟwQ=s'D9(n p[*g)l5sk(dڿ&Tb5x9(sJߔ\72?nYpFb}<'9f0 @o'ֹ w ֧7=chVj3A&N6NМ\V x%kvLr[oqhN:RI?}:%@{spЩtVmxspDӛI@sυڔ0,E 3 ˵7 '4,2 : \c%-L <'on NyN2)8c<'?n N$9 q\ks /Cpf}@Ws`\Χsk8hYyN䌷\?[p@4'HzLu`sk?8;9! =3 ]҆:g A; Ti"ɱlsn-@D0B0 NZ @JIsi'Z(b'sPu/W0,yNY\N t7pJE`yN>QyN+HÈP`7-n/oGst#9E@t9z 8>D{mads-.riN+947p^4 0 О- 9c 4yNDž_@7 Ќ=~7 ]XWwIs4`a7!PUKFfg4DQd gI79ja4'@q类9l㽾p,qXGiN B1^;M4'@9g}Iy.-y[Xd2МE xrd v+cUhNEimR\-;>n#Q ' Ykۉ iz?ËQA/{ c<̰P^ "%(U ycͳ > HKO/wooqWo9]^0x*Z?O^D4',LF,! 8GsgX yi>D>Ҝ7[<6nDWfDm` Mc†!G l>4{ ETWGC}e? С 7pډrn!`Uo>Ssv#vݓ*3sSeycT螷#]MsPxA֟pӜ;.x9Z^4'RSm$)PX)L+v1Ҝ+n>Sj0 7 CҜKgٙ g|J =r!"xs}p&5p!"Ksv:8wfe,\Ds<9S9<ڳ =Ӝ?KtdYӜ)}9MiА]m(Ks| ΁+ Yǚ"ɞ@7wӵ CC)c-5 S=@Nශg5_MSJ.;ذPx%_jd9=:ӧsY@XYMu^n9H͝r\Ӝ@LOq78ju=М@k=|Ignp9֍97m 5n^koJ)Wש@^e9g+96wOc1ơ9}۽soz:G/l):9F-{;5oSct;ޘ}.iN9{lڛ8Z-A[W@aiN-x(:=- Ff|/>Ge ey}n/ʸy >4EXb(@[4'PcsgmC==SAWPY>B#ԉ cp&М@r5s#.n}uf=hNk]BMo 'Zy FLhi:K4'P]>)8ϙ9ZoNĵ#4'PrpUjmVÞ92צ9;q?$`D5jrm ΂$(pLg Xu& 4'wr+#l$Ŋ) iNO}09o!θ{C [hNߵrPdO(o1L7e yk@Ծ@k?avٶiNWm7NQ˛Gy.&hT5eg7H+cdv5J=E61Psu'mZ8hDZ,#M4'uSmfm'Wm9!.7g>׭SH!-#ќ@J :}^49σt39dӵBxw! xϭ-{\zldGs<orm?9ڣ qJh !+ c4 Xu8A !7 y?7Q5hNA(~uwtn09cSk_1*PQyw5UtVV/9 _c].Gs̛۬Ɩg hꅒ4'vtP:'80 d ҸXnq:Ӝ0OPK_{AhW)лy$Ǻ*4'ē-T4¡g yZ4'1(2x/64[ݧM iN8S$8. c|.k)vUNs-1 x}Dح>NsL)j -М[ S 9=Jk$8Ќb`3ukSM +ڡ9 {3 v0ќ\ho}qAv{Y 00غ W=`4'ep [ jy?'Be}B4'@JDhS#b6iN81h̳.c=6ndeB4'̅'(chcH^툍5[9;Ӯq#+Z9ጏH^%FcΏ%qи?1ϯ=a'iJd3]gMcH͛AϽ})5_6w>hbo.8JX;8=ӔXQhN(ǿ0ߧmԓ /A/4'To{al ڷ ]:9-ů|}i5Ff5Y|On8iصiNhמOd/Ʒ"03axH9k =Y|8: U4YZgF\9, ݛ&7)>ohY31V m?/~e{۳=/@sBv@1p*?sLᬽHͼ@k6=x{ϟҜKxGO;\pyD;{mݲdjyyzw@xCS޽a~xf?U:luМ@.2pϡ]`e1~㼌nՠ_-̑ pNx~>nc t+.@7)xBhN`J.ԣw/~fw-iN聧 NT-`݊Joh4'꺸^j|gWp; jD/5ZV15Jyn\dQ5mnќ'h9jeV;Ep湺}18f6o3ZX9@u~XKAo xM/UA!Yxg>NAsB0FKwќˇYh=!Qc `C5ǔaە; l(O;7+QskV_Χ21 m\м'yNhhA۬t@smX6},lOӛ@έ}nFIDAT a╨Y,& pMWgz(̹PUW>5_pH)zpM N"POWԁ<*Tt~7ٚ@pt#iMέFuBsB%8qj  l9q>-@#s\ޠ؉j 潁hyN!xnڤ9A4ӛ@˜[ )ᴱ'8P\c<{Jڧ91TEnzskyN(+KFs@5CLo=rn-0~;_p2 ;amvx_Efzsk!N*sB)=vmk lS@J1j"ќg,N& wsOӿ4B2 Dvp~ ?>Pky!8sa5^jyNS*;yN8E>6MtOTPs#8јB̌Vsd k?Tp2 %48Fj8N=@9@ڸ?UhN \y9 vfbkd=Mg!57Gp0 Du}?Zm,Ҝ9d ؠ90OMm2EV5 Nƙz=8~8Ds@.N ƵNМplCIN pS,&E\fgɧĺҔpx69~&9ɽnL p/ p(T58&@WPOWmO;xi/c`7%ATc?f0<p'ْkuΧ;MM@&~ ս1s4oN)Mfd$ly%oݸJ.Kkd5 m3΅&8rҜn_M\#mڪdd9hK2ʭ+'@~2}.ƠN-ܻ|8hND֦@suv`; ҜDQ?%]έc[(Ksў|%9Sn m4'm\+kĴ]óׂ H{cB'y\i/ ԣ9>1ZmB}cx6|7Ӝd;A_(c'8O^ʖhu ۍ#6*ԇ1y6}hN8cRJPd-8&@$ns 4)JR Ӝ\%rzY'ks<{&@4'䷑sp^ @a{ґj  ;;DZlݾCN]p@s4'ۗp]J' 6 s{Ɩs4'eUe]׾=_RZ,M XDa'{*t?euϦ4Gpg@|jw8uQI_)erjjќ0)+)]:L'5έ4 96j3 NRJ>2n6Xa ov~ [)M?1)8x9-O}AsI$8X9`ٯ),88<';6ӑќ_DE?v-5$М0+?JHs,1[cj> [hN| ~QGU|ݮ ќM|4'uJH_Z0 }+ϿNpo}T|"Z9'URW NќRAU15@fiNX@fUO!88As1OS3S;[@ N.ҜVV5ۙ4'~g>Nn9Z3'wќBuEٟWFqWw^'Ӝ D_ @7n~ @G=1B ^[W_ňIeҜku3M)))t 2 %7asPV- Oiٙi(Fse[%iN`tnީ]B/-Ҝ1C?4t(,yNМ 6-4qbC?ܸGԢ9'RJOŭ*yێT9N /ΧoSqsIyp}n:sq9YG] ].8МX>ߔRz͹'?$J @#sYm9  @jhIhs0ZgM|Zd'8hyNbgs%G9ciNYLZH)up @/s: ߘ A3yN;8'Y1}Wέy'2 @Q']3 7VF{F].F`5c4'݋fiwέx&o&9'`lm4 * @D 0 i.Ƥ69 s0Jjq'C1 MdQ=Vp0&`d9 # scd6 `P_n<'CRPyN4M֟Ep'Éj'1 MfNyNsǛ9&dyNKmQ9Ʌk'`>PpyN``w\K'\aa|Amu9`[` '9&<'#8^9IN 9  IN Y`\r3 &9Gm& iNF6< jLJgнdZBs4'AMӔU*4'1=Sm@EhL Мʴ @j3MjSU'~YNhyN`H>3׷nڲ @^޺97%g@{ً̆lBA6+<'0$Yҧ Nh>Ep@4'M9h9h|9tJ-@sЊӖ:9pm)Pc  @/K|Z@C4'圯ͽEsP1iNR02 @.jМOm_4'y.Ǵ?]ҜdmKn8+Vp@\HspvҜ6C4'M j8MsJmiN^]M&p/ rќzIͤ6iNМ@a >hNLluiN)5Z4'@djO;@.Ns  @.\4'hNrќ9Es  @.\4'hNrќ7Z IENDB`pyformex-0.8.6/pyformex/doc/html/_images/next.png0000644000211500021150000000031611247665322021714 0ustar benebene00000000000000PNG  IHDR$xbKGD X pHYsHHFk> vpAgXYIDATH 0BQ;{ &xG"CV^ vpAgX\IDATH ;%U'"7A`".p$yI>C{ A`*0XESMvIENDB`pyformex-0.8.6/pyformex/doc/html/_images/scallopdomes.png0000644000211500021150000007514011245557701023431 0ustar benebene00000000000000PNG  IHDR pHYsHHFk> vpAg)qyIDATx$)άu1 KØL-Ɍ!! E_c1c1c1O`1c1c1| c1c1c,c1c1c1@o1c1c11c1c1s c1c1c,c1c1c1c1Ƙ}믿s6c1c c1H3do1c1c9̞?gY7cw{61c1}D1_GHקc1F1c1Щ*(w-⻾c1Ƙ~\Ao1c |Ξ~11oc1N'Ym{@|}F|c1nk7q1c1e̾(} 2"^1àSwƘ@o1cLg*/A^o11FXc1c6YaUDˆg1Vlf{1v\Ao1cL4?J]Hjzc1O,w{Ю c1R^}{|0Ƙayhn5oi1cQwgBޟ`]wf爈?1KϢYD'QOڇF c1B9w>#zc1Ex>\^|=r+_?hLc1cWX|c1w)_*Of-{>O11c1.8 =sǿ= >R=1 |(?=lu g)F c1  vcyqdzc-D`8.T}*P8:|ٗez? 0(ә%c1ٝ MHo}~[+"]i1^$-5xk_!Y2W5쯾1758cAW<+Ǩ\=c1+`=ޟՍ~И,ѓR2:8NcawKqoc̛ؓu>U((c/VA3a|wl懣1Ƙ<{'#|%U15.b>Uyr߻ٻɋQSzZޱq楁^ 1BEg{Wk1ƨ<~5[uԩ_[L~]wy\Gob\-ϛLd6ka}c1_#Z32 x,J sYW8wW x][)m=cX7Q _~WUcJ;IזaK3ezc?)#H&yOsӏz#ʩ sëH }w1ǜg>7!_ƒlں7sׯy{ $z#o6χrqv /~Kr0ÀU؞Y+_cfT]fγZV׭%  !J6NgHꮵ7tɓ3̷=<#D|e2s-cJU_R$ˌQ)1nL' _oy־x~Fd8c̛`D,3;qjDUmf3Oj3=OʘU_W׬:NU{bs{__%G Z,htbsVfY'׭1J5JSuܙܠpҽĿ5>ƽOSzN'γ|׬̦~YY!ѓUiQ+;NhDumQdeafOt1Fg^q-g@dUgDݓk/H,ӛ,Л=}}hC&EGGs\6Fz᪾zxw(#G[ډX'YPw?;cD'3`Ru_tט(ƜyoU|+h2GJg(xUuw)mL?{ŞR6zam6Yy\ՒgD Q;(˾g=0|m^v~~Qw1Sz_>@StW~} kj{30xG*{c.[?_W#ی{;#CVq~*|VfK}ɘ|7q‹ f歹ٹ_; t.uzg&? Ukkue@ˆ́z718yίѣ&1b3[Յ/џ"b |Vf}^ƣJpr|}0z>m_{{?G]+A2=qQ;$Zb1 ',gt^0֙~HPU{GFh D=22ѫ|C.Wy%sLJj#2X{񼠠 CZu,e$F_r5 D(;jՋ1jZZ+FOT]m3G|7G嬃!:7Z4-tHʾYs/_˩# 2-dTgjs{Oڕ1GOR,Sͮ{|=fv޻誧s׍)&NF0 >+% |(vF:zϿ E3+:}“Ha꙽޿ tU O{=YזJԎn>k]Su -+7 kػӪhU>ث[aWL>{ޘFTUUg^ztnm3;g)y7s,91j57zrRwZxSI{^^թj=쏁ziR|m h0y3kȪv=P[z5UHAk_?{ck?T {;uPx-#m_< @˾ \8ok68\5F0SCɗg3@72TyQKz#9rW6jt\[;6oC_ [ ٕy{ϷJ"=2}UM^P*[tc_yμT۾1#*k(g|_(WkgZ>iՈT]TbԂ{Ϸ'{4#o{+zgToy6#7YR{HFFQ .N3# uOK`k]K߻Lyžo2^8 ͎ /Ώe -tʾwzjtG-**@27rT X?o |@>*9j[{Ѻߑ!ϖko׍ч\?;#Tc)g۬zo@$q=3#׶o̓ Y y{/rV6cc¾={0y 2 X?Fm96ayH/d= `?;s]Of,z {0ʰkK;+3c7=\Y"K.:k#1vE1k2T,򮨵l1^Hle~o|ߏ ̼VY?V:kW7a*u+z7ˉ\xN=4͟| xvu-jff^IĬ˻B`Q=gԪk"k'o8K&slrߕ=iΓ'UᷣQP[9{FgDQ>z8BDc1T"X|aY< 1 xsv%2o,y;yos쫚{-U_!V[{YHUΩzMώ+?ƽ"6yXLdX+3=|U mQk+|{֞CpY+Tـv6䝑ukOM1yxR-Xj:%?{yLg8Y3jz^jH S+_}R[=&UB9jw\k><aU&j?sܠ6VXg`BARg,Uk-[`8L51LfWm0IK[=DEE|-N^]t[mʪ92Jv֭Վ.sct U]]gO7y>ٽ!)XX/fOa˸Gʹ O A^a1vF2ڼ#8!-eP2tϑ מ U9^t, 6_Ed,d<8/ԱFGJcpx̅e=2W{wR?|?T,ec沼DY,З]ڞ%UUfo3y yeT][`ͩgSo̚k0ŮL/{H>2G5yVHv3T57r8U˜i:t*čEG{$!/p]\yG`vZU;AAogj:/9-t{{OJ"*ªESggH6[鷹{tѳfy4;s@oҽY20UVO=btRcXYe) `5*`>1nBcam*V܈q|DÞ'z iO>d=ad?e۬<=[ezJ(6|A_r=>OgIm|)R dm LUKĨb8$5Tj8Z"Ja(zYߍ{c_[ybB8j3SWkhN]>Nzs*+ .UVky /c9OɗD6k=scƒ':C=;җ@Q[Hf􄿨m8&S^#=Wᑾõ}$Qn2裯3UDE ay5{*ë=+=H(T.#,ytU=RoOHJw {q{$2Uĵ7r{ʹكk4*.t =elۙdڋL_c:|{O{v7* {2mlos.uJ^yJby!3!5(ʴo,=?x/c~J|g**;Oɾ))-Wg- mf)GkDac٣syGf:[0{\; qN 6V[HاzF|*4/>3@/D gE^۰g*V!Jڟ%/^=DΊV2HUWgG0;_=xeYQXa8,LQk!@yLJLMkV;dg=|i*?2@5jbaALjyo٣Wx w'v+JqV_6g_W]n ?DlMs8lFtm6>u>JT6Bކ%=éuZEhw=9m i~rd( >7HC ~˫j_nc3)0D۩MO;gEKB>;ٷaȔU@TjBՅ}U_w1" cIUw싓ZQj\WyYYs+j[ջz΀3=G5DMk v6[sanWoG@|Dgx~;uT6f#hT|ē#͘~qǤ{7M;-w*֪V*78{γp>ꈣvb!xh;=l,::|sq o9IQ>'-4imXj4iYMdvo# >*|udɍ e@~<1|+tt^x|ˡZ^3Ҳ©OP+E'8/-$*^6U:zL7@_[V%[Vى[,SSNlte*IwU^vҘNePw2|/wV5:5^sPg=s#Wf=F%*{Ru=}0c[~}wcKgwN#|IJeY/<H$1x=O82z+ >_XG۪1tEQX J5ޮǾ{^MD z_ wwIkCK\E my;U{2Ϸ9~+ȭ~FpkJH;ڻә:SVM cBmVKU|{IéfNQ{SxfO:s8Ûy{H'UoE9-"kr<(3c~De^d֩'ڨυ?}+z˚Ƙj #pV֪Ʈ4弝yb=;S#x0S%sd<ԁ#Gd4MO;Oe Nm&փq22{Y-X"ۥk3v<-`qϒmո _5hˌK=OCOY{dFWe!Hp‘[;WK5R[Xkˍ>X?_@DZum2lTv Z9E7,п޹i3 s}\eҽإ#׾&?ƑLAYً^oцZo LZsڥ_=[VbOn^kgfSf!l9Ia-~%{};(Jg:uHY[+Ւe6^ asGO:{omZ̐zy3gl. L7uI24:.:w UGËK="מ%Wc=cỷBo{d5U:ey!/>/A9"p*!aWef?{!c~\,ߝ2t6 %=D޳o>jS2}MٕjFݹS^D'oMznw--Z/\+I{ FFIszG>:x'ʪ`TG ¾؛Wyco\7@GXfE = 33<6W GSwq~ז3"Ԥh 3G<3~N|ns/iЩ0`u~b8,9 zFw=Kx0;^^6wS=Rν)UC&V l*z9g=5o[Ȕ U؅-3jĞڸ=DuX} } jǢ,#DSZԎU7Wv0dzP/#.kyvX҉Y.ֹ*p~=]H=߲ws6-|]3|X֢-㢢6fc.2.9pwqxFь=-HUg֫8Hʽ,B-Uw N 6Sq#g̿R.^4ԑ\,O.de{>BU%Ȼvf^(YWX_ĠUَ>Ny $<Ɉw[@ک=㾪=|{>Y3c̋)ϵ=y}=3ۣ]MHӟE\#WZ6~^W[^m*y V}͛,/Ygzjᑗ6νuĈrUgX?:ZwZSpN.ϖ;T~PiORG9u?Ef(k-/ʯ(T^; {^~3#Nk"wOOأ ^C6g'jT%.LF*Ҍ52sJzF {y2&l3WnQJoQEٯy|fGX"ZK .#kNf=BTׂd+o~D=|gI uRS SDy '{_=+n+Gf+D.bi e' W3/Uso!saojͩ^9{匽wunV}<}!sNtr12ōeEGP>Gο* zF{+}i%g=xiО];f㦅Su(9Qh3X{7ߤ6Ԓ=W{øxYud$^ZW 5w/k|\}v:Y5 - SG^_Buf;p3s,!o?Br|m`$(Q-/N6?O3w3Yk9f{e=υ-BeU#ҙk)SEs<{ZI"񸇻(g̅N=+ͯT ^lgI3Hkx];kfzz#R=%73>^iޱʞ vº׹{,A{0 {ٵ'˾uBAlsgnVܣ'Qi{{ c.<Yꂼ6KꙧVcTyDS{27]潙R^b'|\>C2SuyޱG#ն2) -d|woN#D% ##DE|t-3\'=nؑosґC;rD=59?O!G};;xϜͽ:\4\==;ita>߻&s Q'z=DޒYkdȷLJlg-t֐I{aUmۻd=΢s߳w2{wxB"̚ [#x~ [=q'Qק3_?^Ltd=Cj~;=qU'RU&{ >sFۓ;w=n==#3r* ]A_+Gk^xl+.*HCƂ*_{,JdtDUf,_7=ؙhg5x]7U!C>z~O;[ٝZ ؚ&^n:yFٿ^E|߂٣{{njl i嚻=e9S{h޳x*$)oTFLKOw{am\cgU$-ٳa~򆗁ZeOy埬=7:dݟ+**$Kd{R*mf VrxA"dc,%C?A%/G̷ёkd&uJwKce[j{#Gޫ :"8B%3{ݿ*[Ò&B]{Ҟ-.S뱯uX(|D34N!;nYrOlYAUB{ǂhn|#&3ifʧř̚mLgxk(7L-0=K޶Nt#O0䛒^2Wśٰsm*{i/[]cUkU>U ߪSgEGԿ{>߳ iL$>d5o3 y%h }=>E~}[v?#z3q!nf]{3>pl@L^~Lj ]~M#Z1vϒwl{ΘH=[l[YqG\'̍ھi>#]$gYW0gJlKW=5oaβ3)e:lYpv{W Y>ܳgc~uUW) trKֽg&2;c=f1 Q=ҼgE0s1iÒ=2;Tuͣ}U~źM )8ۿ;oG3)"~|V^r}DRszf닇 ˜܈2CjӔS!Fuس6XRqD!kpvV{v{I=gW{~UЩ㨎}6>@{~R{XQ^=mڃ^_[ky?caP;L NyRN~3>M}Ֆ|rG{[ok={2d=K2dRf}m'qbcPN͘j3^{j-/¸z,cg3Wf_}[-tqkY6y(+K%;gzg&`ܵf%*nkׯgSfT߻PثX 9{ɵ5{p {bIdGW3M١-(#{2W+½ۯ$ƲgSVJ{L{,Vۼ4^ <"Z; ވf`F_KzᡟG)j{<=NLqXߓyrUgۋ s,ϑclsL{fVkT +ٞI/N>H[kٱggZg K_fsAw){XȾxr6EQj#ϟ_2Xv@lYa3OxhsF-fiܓgtm+ذfHE޼KgŲ16"sy.飬/ExC'*X[ vT; v"v^9]!2yݸXHKB*2*Yo3/x+BΜp~a{/lkMƽw}r*ٚ1!j7Uʍ). s`}m8CɛW-9W-O%g} jR`6誃uˆCk(1=gs uY{;{gl:\mߥ) ?~Nx@u,F~y{:ڌDO#_19gxxgtGfYuZ-mLވY,5g8cTsUΫ>C~{_d]X3+~zld$z-ʐyή952[g/t>|Qi#޳'_ףҟ]wf~D9#NL<^0l׈^ͷ=Fm[3Sg:{x<}3#?uKvʸ3f?jUi=k9S[7ۣ;B{}{odX[-/uO֢ZS3{no{uz*z0bEoլ}*ީh itU\؋u<ɷ]Z8"\"<#<ϟګxFv#ó=YsޗJf{rg@OrfȞxl;K;Ww`'#WA^*W-V^Duml4=mZU6:Z"jtg˶>TELVvx_xxe즧56r}J?x`e!Su5NkF`oK |츈Tzv]vc4kj}1"3[j=޽ϱ'{{m'0gU|.|uo+XL~|ٓ2Ϫ 7q'[_66ײ'߾7}:[>`#ѻߪQxq@{'ў~ b? tƲ;2g

:~UG}l=CzyRֶEg:2Xd| {c9\(Ќ O.vҽ~?m|4SmuZx[9)h,Q~6ڱ(<5٢`c="DeF*uf޶zI9!8ozFxb\/W;@Nj+Gn}V;߯B\E+Vb<f X iQq0+s EsG^Tȍ(=FϿkkj<֪oճG+nO|Nb39|.)TsV-0>< {[X!Z?U^bLe63{3ǖϢ` ?'oYjҷYkLIu;<"_Ue:zyG/]k|cX(cі5g@vZHdFtٜߔ@Wq%m3{ƸcZc3£G31[+LqMt{E0ϟnkhN?;?OTy |jb7'Q8&7K0u6(t׼A8%(̓S!P-:/pvT46Kk.eSsTg"beAqg3vwgHmym6b'oDEBvOg{eOI]D}G)\vW=Kw[L3kgyMpϮdTf{2^1&CqMAw{"gsX! jx_y{v:gO&FU7:|ۓnq ;uVG@3%;VǗcAY,iwƩ'^)UyQ~GUmr)=D+2[YGrFX^w|2+BsDt.{=;~G 6Wlx#ʗ ;:?U1lj5O}Yxw&yc1"ԾvY}'ybD.DoOj[l-*WT3xsŐl~f@f~+eD^^* {ܵأP{sixig1S%f^; Qul{2ѩ*"$2oј#"'~e-T3jFx5U̱Z Ɍ}W.Kmՙ!:0سm-[U[/Ut,͕{kaoXϬx2;=|c ˌvފZd;֯g\Z(=͵ k}\(Qx*y Q3';E<=5joDֶ#ϪՑGS~e5o_YwѹRHmKڹu 4㘂Ek}RfsjRz%s3Nc}=73<︅Cm>g{x傚ju {i M2@_k޵ ~U TUB7 1:Q=DO^sounQSy NLkKnܝ#j_YB3h)$П ^ӴFN8n܆yy'RK4xpXYPHjGW+UFk#chZUeb'좁Ff7du+Qh6ƽ5ó^|βGU8{^[{GϬ SExyvSʿ9sk7Ψo$@IQHYl;ϒTs")D ,?,ET%#?Ƴ6&Oƪ Z?k?GD͝Ww=楸%~Zx5?{"o39x/ʷ_z4.匦jKVˌ=*ۃ#7lf5)G6g}GZSW9sS:hTe>?^Mً=*4TR(*ػU„Ui#@ͧ챪qٖXsQ[_ 6̊2'u4s2VE`g}jD/g]~Mgx|fiXn *lo?As _9U8^_epko٤ufK2ڐv-u2ks=WjnAMlRvrlY'5GjOZcvQgXuo%rK^Wcgbp4wyCaVAyº0:=++ aD ?۟-b\;@-Uz)FG#Y  f~@@Tzwיн}vO#N3;v6|qxr1~uل`[ȻY50?fU{ryY{ԼO~ٞ}<|O[Y裎ȣ5_ǥ9Uݲyk(3 z ٙ(>[*gls7X$~->yOo;oWCW:Y;xu<>j5>O~}gk\U9e 8i,ԢsW:vmZ{>uVWHٖk?k$WJ]¨mU½Of=awΤc˳0F~W%U^ʻv[Sz^pkjӳDs͸Fa\Y+wadzVe:j^¯ .ɲ,R3_c6{<"ўwGǎ\<`a=~%Uo>H/x]3Z,=rvw#콩 pφvݢf-\&п!G1#8'>Q^wg%ϳ0R(<|3H=_Ŀ}.(TeAk_JoMkedV lg?'zM%6V;YeRgck]GOK#\&F"+lujбG!,flENBjO.35mF#⏖5.>gV1z:k"xGmE猚nkd*k{$3k2Sܻ8&(gj/jp/վ@eP0obѼڊ6d!ƴO!ȲkCIۉƽvw+Uu Y@eO^2Z0+JR륧 (CY,CZUyԽqJv>H\^י:=j?Sl:tΚMe} ޵UY0ҧUwHPk8S{@jثOmͼѧydo] Q*"43{k%ݪ9P]4{ں>]+6K\,P;nZ#Iq!u˛MNGB%3*` , ao(U5(XR rת9lǏdV>UBCpn1{m!z!}v@NqOa/3"{v}N#[螺~v_g26c}=:{T C5 ;mxGZndwZ~;<\;3ckgs BtVՄ޲f:-?-GkŇ}7Sln̊祼we\3^"3~x G_U c֬7k䟈rՉ2 ѕmÛQjFH̟˯CmQc-wByT~<_#{SY{\dԼwGQ[~:ZqOΞ^B|%ʢRz3$zy G+G:S5g_l-z&'H:d5=v3NVn\59q[DeOx6佄N!4QDJ~g),GqhyU!Zs:C֑3W}Չ *ޓboGDAũb= 8{$ѧmg:vno OٙUK]=kjSK%/eM9̛P.Xf pY.BwDQ> Cm`S5g4:5Y%a U3n?{/*CW ^:g7D;Z{NtSa'zλOL gӳsTUB>Sk™`:BAXAuf='o>m<dzrYªQ^xʩckTUdw䙮ߋ~:oŕпjlvcjTB6w?|!u댙0F,_ gSvTْ;nc_."3kO닊NԎO :[IݲWrB|֍;dzkk5~PU;X>vVj\b=߱ǽ \x\~%_{'nmrK'Qyʵ֮[GdAmȴͮqsNѓžu<½~I7+/_4̖x{7·zv2c|k vjZCyh0w6StF+rYUɫK:5e-N1[s떣BX³vdYوLND˜Uld|Dr W箑f6SϷ_tJǞ%D~G0ۓ{:3Ә()zV%|] 5w?jof[\;'Eɳu5{gv;^U=|vx+XkмԴ. gv~;{o~Z{ͧiذgc^yKt?z)DxѪCz3'L`BjcUՏm=?#%2gYksO*A@җYx]߭uzjU=+lŪ,|2;$|a屛/^35z7T&y.=#Q{^3-Fԓ m UJ^P2}U@7]xaw}ttz3#phc?(D6``MLSHjL JyyV1Sc ;dv6;Eօӿ cxO p=Igz5ݎexH Q|ʱYmvxqkzM_Ʉ`CUj`ppNO~s68 P[_Q{"#yɾʆgIҺوvzF{q}\&Z2LI_1NRuƨxfx S}s`!׫a@cQaG'Q?-ASW ; -v>/%˫h1WͣVd:9,c , wf.@/5gr(2l5iV}q,ef-;6?^J7ѓwa*jhd_֬oJ ]n~™m3s#觬3)V#_qyfO-êl yr VI Ud5""yf Go-vgHycDߌgo!uUgZo+<;0$ڪDy|*U=DY!ۿ U֟ R=ʛ(B^'ւr(FKz=9^'X°[ۼw%{?|[VQtK[ǨIfo!{v^v)8cG|cX5{j6|/G*޽6)؟?[?O\*.Vy%+؀V;tuggNT?7'D{=&7uSD-m >cFK,okm/\&d:I3v]Jx|yF'Jr|KiZp&)Q^a@>,C 0ʄgiJ,97wT.w|L{2T 7aβ7uGgBl;\;%K(yFP5iܝTa>l}yz!=j(ljmM¨7EEXF$|kd`= ~6=ez"Q޺)z }-U?ȣ/Qޗk)P[G- X/v?O}GyF#x[d~cԗ}K~_7MVq3jVyGy]:Z~qͺw|,˘mxKwdXaA}MJU7՝_I ׸B--fc:A:tٟST=/jFGRC-&Yjz؋RJ M_t_Gqnܘd@Oeo b/{Þ?{,U[e1_f "~TEXU4s vFXGmw87=ks/,t+[КX&\`Ww DnGjo>zl8{tQN2y!v1Kk=Mf\)kIB3OS+5;~3[4Ŧ;ɯ^y>򫱵{ՈGG){&6+҉̇9?3S!m{ՙ|ڔ6)Zc=gξd(X/7d}~}}Q^Q_!x+gz㾜߉2=1#ʫD0v!{@v]1[DCl;kT)ר[Cֳޔ֘/[/H;O{YH{n<`~煈{ܒCz&Sd.)TAj-Ty"n1gOմr7i{st˳3ٻǵ,X7ofkΚVi૽cx-) 4W\XDŽu{sԚ㽷]9GQ>#WLu,}'X?L~CEylA? }#ږfR³ҼʫoGkNapY{GD{Oټd۳k|wdrgurNKؗ ܳe82@/b; %JsQ-wUܲW}ó5c-{f0PG,70E/Ͳ|TmUQ; P;:(A}>m2%{_bzH:/r?Z3zbQhf<bQie{<Eg֧lĺp 9SSU6ÍQist-3g#pjg=?sE}I* D=5Q #%(XrKj髽\RD{ljVwnc> 9 OKٿB~ntV1Q?ǗJ6{0@@yVFnv >v];򌞧pـ|~B`%?'k~lq`Dtwfkm&A*k: v=wd읥{*ٍc |W@ 7oOS5RW7>zW(t;/0@9 ]ZfE9D"d'?OGwnSG%iq+AxL_P7`c( bS^PSx5&cfVghe}sKlxW-6}qSzoݩ衐b}UCߑMQaPǶuC0Y#7GA٩?g^ۋ21k=WN({ɦ{AϗW_u?߇WqVW͛м\Qړ۽dE#[gqO9߉NEgh1 g{9 k🽌Uj]>eD'G*zJhkƼ @(9ɣ*=ލz3uB*0}Z(̫S6+zgѡ'мb)*+db|*c>J׌!8w+jD w7a@(Cm0VߔX2v4#qZ0G'ӱĘg~Q  è'}?S5 cz36MENƬ+a1ƌ#C+X/1<7)OlZEyW٘?1IO Z02;MI4j_Ǽ5 ~%N4L/!XYXUe̗щcj忖s cx5o2 b:@oD뉐绾̞7)3cDNG*cx pj1fFWdЌ'VDgEO~3?,Л~?(ue1y/Y^BoZwY{w(_tv;@o$PZ: Y*=9c2"Eyc}fe9o2[Lߗ<ݜej3cވnܖS~iڿw1wo#eWzY=N?[XW٘56 ~P~(YOcL&~Lz{_1&3[S:ƈ*:Jǒ5 7GƙA@o$ǵjEq;1#MmQ1Fc,DV-21&ú;k_'X&#AX_w͗WyуJ5'ŵƘ/Ƙ71}kgc+45^~kKf stNQ-cLGEcލ1'ӿ[(Þ4g%2o1Bx/b'wNc:;N5f2c̏ޛ[ՃBx<$vdv4+c i}1I(p.=M e1Ƙ ƘGc̚Wm4KpSoQ@ox30c1e޵UE̝U_,hz@o1c19;kw2v# WM?1c1c>웵ͣ4=DyX g(obc1c1\,P%F囬}bQi1c1c?r+~r 쏈y+~ؖ1{c1c1LAW"gqj=wCs1c1c ĺ_OT741k,c1c1Ƙ0/]a d>pw[7cc1c1t#oz[ctlryk1c1c:PFg2Q ̻q1c1ciS"%ݽ/&/7v\Ao1c1cg?`| Wc1c1cƘN,c1c1cXp7&ߧ 0c1c1czc1c1c11c1c1ƘX7c1c1c`c1c1c9zc1c1c1QΰkIENDB`pyformex-0.8.6/pyformex/doc/html/_images/square_center.png0000644000211500021150000000123711277771572023611 0ustar benebene00000000000000PNG  IHDR":9 pHYsMMHhQIDATxڱ PK΃,+ zIX$EBX$EBX$EBX$EBX$EBX$E☽V3w0fT& 7k-1\+n,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"q\=MEs܆EBX$EBX$ևl,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,"!,1x;;0X7 a a a a a aWPM*IENDB`pyformex-0.8.6/pyformex/doc/html/_images/helix.png0000644000211500021150000013105411277771572022063 0ustar benebene00000000000000PNG  IHDR Gc pHYsMMHh IDATx}k:,Yw$LL$d;kgee:tߖ%d?j4Fh<4Fh MFh4&XFh4OFFh4' Vh4Ɠh4Fhh4Fd4j4Fx2`5Fh<MFh4&XFh4OFFh4' Vh4Ɠh4Fhh4Fd4j4Fx2`5Fh<MFh4&XFh4OFFh4' Vh4Ɠh4Fhh4Fd4j4Fx2`5Fh<MFh4&XFh4OFFh4' Vh4Ɠh4Fhh4Fd4j4Fx2`5Fh<MFh4&XFh4OFFh4' Vh4Ɠh4Fhh4Fd4j4Fx2`5Fh<MFh4&XFh4OFFh4' Vh4Ɠh4Fhh4Fd4j4Fx2`5Fh<MFh4&XFh4OFFh4' Vh4Ɠh4Fhh4Fd4j4Fx2`5Fh<MFh4&XFh4OFFh4' Vh4Ɠh4Fhh4Fd4j4Fx2`5Fh<MFh4&XFh4OFFh4' Vh4Ɠh4Fhh4Fd4j4Fx2`5Fh<MFh4&XFh4OFFh4' Vh4Ɠh4Fhh4Fd4j4Fx2`5Fh<MFh4&XFh4OFFh4' Vh4Ɠh4F}3e4 u~Id\ sMv;ﯯ0S8'À&&n_C45rn';s]ۭvy "kV<6`5j0ڿi#=0a03uS?2CnvoZ+1Z2V7JzŬuEͮ}_O,?L[Rv0;6-_0rM.3PȴFוķ y˘H+rP.-&L1E,޲~M÷{̊(mLjBx&H2r6I;&o2yk`<)7[4"|2mj1JBYkhx[p@<~$*!~T[v5X_;Հ}r0`}%rP'Ҙoʰ l 8hx3dbʪCq*~7^[dd3e0 kKB2_n0ȤL'?N$YV<\Oa$owTfYC֓66VHc gKh}`XZ/e #թ#*b+l50;mq,oݪvm˔2 O3̅Vtk ViS56 ~VCXKhz=06NuU$HsXU->ꅬV8EJDC+ᐉKYT `]Ö`&7Lc!l 8w:bd'K4Xό:1gB<xdFh}TUxv4R&m֔phDݥ:k9/duۧꨋJNǘX⼘)SBӢ:pԧ1y$Q-WJMK3M҃:X + "*l[D\|2U-`} kB݆IԱ;*8ӣTR(5o P<65%>gPGS  nOňA{{&DTz6Vٗ8̂K ݬDEyTaCY47(=/p Rx³A[S3 -՝tp$XUp=tLkuE؜+`)9xʱMN=sBt< @qL` yE *W妧㕕U__@7KVZY:6du'8B)Axqs'aﲇRZm=+0V=gZ0/.JHFhTJ"JacS,M*^j=s^`G<^G!1}qSK$s A ڢY5.HY92.k2TO>tK{`RA³kYˁc#֕B6M"l_ @'=RV/Ѫo7$+! ^=|Kx,G yЍl$H 4܍F3oiejt@St_n[+r>nǜ҅^rWĞ|ԘF^1hȱ`rSPRNaģY,:s`P~VP k,(fIk;H>B>1,0QO*mhbuTKACҗ+:%O=; NRBy.9:T_j;~NQu1׈4pT7'vhug5ymt>GA"h1p@;LySS<<Jl|of{[餔4W|?Q2l쪸KKG-+2/.cL[N]I%7O@]T033(&옒aa?H\rXLڑXDP 68~5Bޘ(¯;Ǣ MQuP6xPkK$/:Y2-4Z5$)HnjƢ k`Z:AuYGa!Z aJ!8o/&/L-0υlעdUm5ђ!qXQDK(z"_^^|IXq몢FXP\nD9fG"'ʉ=)(,/!}yυ^0R6̀+TBMȎ o<n}j:a ]>1y :$>ϒ&٪BJ]6$bU^WGa1Dž,'.]ڞhr oWtam}Xtyo"+edf6]CfF.Z0az *1J1!̏릀(sp,۬­xThaz`80EK3>wSN%+>_YeiΈ[6d)G ;D&V}B6 o/*%MX (oXh1[Zgǥ,#ȉܴD~H+7h|s ˼E{jd~d2<<ë|CtT1Xzx@6ڂOM̏`@R"Lk"F,,.rxQ# wvw5 "9V+%%wB4~t6N#ZT }U{oQ- [msTA5Y|.r`*g"J\㢊3z[Ԥak˱B8(kglDtޤ ΚRFZN?cunH\ ',:2|8wLtFI5nYM*S?ED&ޗ4g,6-0T[SytiϪؕwuKHU32 *K'h+)KDbMM]Wg=>YDa4-i0G暯oK,#RXԠuk*1Ete1z ZZ!k[OAIO /U) eqRN YKH}Z}VE 41|Jo& 8[DU*%E5~KP%kȟLduѕ&@v R ;=?2!0]+ִwH$CTo3ݒֺ.i 4*EEnq$-kNv4zXkNcwm}p(t_zNYOإLjq:"A¾B⡛OVnG$xUݮᑑ)"%;^QpAe#IY*F+X7EMj24U,,&pSdϳv/(qتs|L??{en^Bқh}lt }9r)0t_eu65E}%P҂RTCdl* wg9EuXR8"[ukƇT#Tih1*G!QŪP(|b)HҷhMEKF{'Z9KĎVJbhիh}A+XL9W28Q,2(Ýk|`z+E7r@u)22Dbc7@UHmaSG!Z(T1.`*`‰RdUR%Jȵ"b9EV,Cni&!GgED)4!.½zfcn3՘wATYڸ*q+i|QNvԍq"4$V!՝|AkuOZԿ]շZpᰳJ}ʴ4`_n'fTW65~(kZʏ5ؚUFw@X:X]s R+XT R8aX_aR(W|h;a Ӯ#jӆ5MdK$3y"{E=x)p|/&5pdl]@qQ$byc e,^j]`Dتs(f!~vzH&d(LŽ5:Ex[T̜H JKIuK5D Z ߕeA+:^-X۵`{}ӌLR˝f4#K3PSu;dVM픗0_@~)N*|i жG+YtLi5Z}WE`DY?E췾H(yymBJT*Z3ŦeJi}):N0k*x" ds͎ʻN[>f{1n) Y_dx UqcAv!Zy<9?FLB"GPTRP(r\^G-I+bgj4H*7(0)ܿF萙{? Ճ|h!2peQbM6`:l!`WXkKۂCM,6]PR> sK̐![/$/bL T(є0ڈ0C[Hv<9pʮm\U&X7NzP*\`C0IaN1 !V놀 iP6pF*MOzNSU쒍E g, N[m;Y5*Ue:j#ȡc9p Yp{4{.7o;AژJ&e+^X tct, (kc ]fhj堳H5g}0PYV-{#Jpjr8Mw$w/lU4.`&pI8tBplT"b>Dױm+7R˳B2FPJ9oZ 93e4/C&ƥ87:6k۝d IDATppeрvN_>I*3sr+(gK*Z''xkخV^ν?m_xG efTncY4)T1f#2<e~XBnȮ,]wK1G <,Uiq%c ҅i (Nsax͂.\YQ1XS Li$;d%kg@A9}~b "G ;1T,Si"؜|y2|q>T]ҳmqAƒJ AU8Q.M:Kmut4p&X+ӑlBU/4 n_Ա0^($ł@M`z15bۼ Aؒ쏨06G5aW[cڠ+ud`a*eADGEVHn/?3/{0` j5:g\&L$qC \UIV(@E7nUC/6 R ]ALe}"K<$I_rVx$؈H..G#ko0L6{CR0~=/lNBPk-"Tg` P`$fC ܻf"_ߦX/vom*aP5וPƥ̴9+W ܤ#E9~e8uE4vH@ FVQ0ȋ^4d^dưJt.誩@6|E͚QYb3*gc럜~dUiWޫ~s\KZe S@~:Q ߘc5 Ka ߌ(&oU_6ХpSkuP;\Ys˃ B=t3&= +r \S.pYc9\ JECzRSLvx/o傝ߗU-ԋu~$!,Pr` Mtqao!e+"8'8(z<GЍ,zh2 WrI\Ѫr, QD"XMn>b6`$%0oBs{<\`U;9D niwV*RaM? U\Sh-p!XTT+roku',$9X\&c6W@9MWB)a[v#QEfQDŽA*λ.4C{k^:gHgL ֘B;n 6hxv%p,^&Xwf|sݕe&K&nwr+FGZְ݊UC] Ȩ,RXKdy ֭`6̙*с'=7a>8@z,/so7JOLg7fqLN!{{ oGF> }M4&X@U2:<+jV./~(XpպOCKM&nS28RAㅋIο opL;@<GYs$uKE/k6"]%\ xLvi> t, w Ma&(tP8KR8C>*Fk4dVu$5MnJ(ޔKvJt#oSJX&Gii`bQ`p>vCgRj9Φ6&JV=wn5皂H\p/s"+x *>Dɓ\KR_ϑzu&tfD,^V9w\6((YVAe;RF ;UiAH {|2;+mI՚C@ZkY߲ fz3 Mn595V"OUhJa˴[b0a*qHhHhw+ҷ0FmlHRm8

҉x#j8ڏn'Ԝ )y3h\!V=_RU:5x=I7Y4b$=Ǝz!QqSS8wa,c+xP{X~)+R*8׺ᎯGf| Mjm3r@ۋ#mhnA" sPFnvXoL-m zk.)vwKlHVbXeGR~ɉ*/_8.K)H1Q2&ڂ<^-'l}C,Th"Zd]#8ւП>G3bBf6UVu[:YX7 h< ΈƏdT ,z3 OJvE9c. 2B!a1*5oá&U]~b4 3A#*xMf)V1/I9a8KwxPL:=Hj@FG`n}: j9< U9aW \;hU|v1rS2ZѐssWri=Uh^/? SW+U[CZT"Ypc\ll#KNYB>C$"\SPtJ܍@ZchO% "f(9OmmO!{~t2Og34,y00+׿tRJ`6v 2%(=;~@[WԶQtOaW&AF}eF3Euz((3ۋUMޝM|V#M}:_ !d䇚U~kAUƙ< HL``wNȁ4#76a':OČ؃.(xfWj j}P@1i>TF>O9VcS&t:G8UY fW43@;XF~w&ԾI T;7*i^Ҩ;# @@IoHBzbZo*aрM't%E$J m(h$X=GHF|ij7+~.ư)UH$tc5y0x{{ئ}mKzcR܉Jyȕ+U;43DMEAwt)3lXɦMAz+?YCxښ.i9K,xKtԁ%-VujؠeD!эas\1ɆHxC4d\Ԁf`{]P`m/)gGv,cCq/y Zؠ-#mO&VwY\)iVwy]׏x,{XdD U+HP KU϶PӽIMa"@a'ϡCFWz 3`SC&vVŸfLIU@f[ -;;ɍ /cYNW_eFU+oy&vպ2'GqU(I%޶Iǖ'vs6^I] vd:k~miK6 qc,*^7hʟV*~*^QG@sHT6Y6KlГe)_OLooؘ^/`}bQn3#h;oeSlXh(SX'4]hA ӏ0eE2a]`&߄cp03M(+CNAF2:e!f%șUL{s sҭgW9PzWki?`}>κY>iENn,aX ?L{bq!NF3 [&m ^^qMb*Qѿr//ِ҃*BAfaA<#oLh?aBI-FJG/sb\^:4яtŌ+7GAe S=t@űܠ⁣3H4F"{9VMA.qMd .̓i\,z4~6.k d=kagLh.9]dbH>-gM.^ ,a"ȝio<ٵ wrpJv/#> SM}F%? ug7{'=MoogJ70"ӛ ղ)O|YXiĪ7NT=}%*+n.6q g7VGznB"p͞q ĻLT.$,j FXS,)ԐLJR>C5'm@9@$qLI\۸ *.oz/lwG| 8r| J$V8S6WtqIGL no\8y}Θ-Ѻsf$ٻ+gY*w\fȮNbK``r r4,+,E \39=;$usV3@9WH3BM/=Mӫ366' QhQ8;!}h{C|eW&{Bg:a=4h$@0c#hu+,6¥q]KaϿ@b2kSQCZܨxiҍM2Չx>k "S[b7A`㏰[9w(tyQ)PհJ.д䡤<>-P1U|VE7jbZޓ_IZ؆gPUK%AQDf~*" lDV9W@qVJ,a9R˨bkf-$s$Mhch(|CO[U7Zk^&|?:~\b8++FL%4 YbD4xT/88铒[ӓ@J=Z*Y LKe+xw da kzRM&,GuU;bR1GpL寔4A L9nuģc;eK } ޴ӈ@:H5Dcq0L6GG_}:< c- nj_q͂cez2- QLG>(5]*;넶~Zf+'cFӗ^#uA5fCpK1Xi4 > IDATJƍ7% \*9A =k6c$J7/+SB3θLM\S>'"MbfZx_9?~RZFSgkmmiUܙ{&k kB@8a윂L uinK OGzHg#BA 'L, P7MVR;_ %=;q:XNOxdܻAk@ve˸* V,Ӆ&<%s}Y#)f/ R|L{Xޅ%IG\s,GWt&X " O9notknC~훕R=0yGhȏ)2c_/޲wtK9P[?MG.KA._uXR5DmeAA~ԫ[gnCe?[|$]bev|@_s~Pa_zFzn٫4bYuȎ|_8S݈'cI\] kAM/RiBǒ4XJe3>;81OP^!7vZk" rIb/L놞x# xa!9!C.B WDIsl;:LOM,+*.n;u˴xF'Qt "OXzW rċ*$[*C[,~SG(G>A5(/Wbug8cBYM?6Gj (%emRMy|s@$%,I4Y&?F㍩k] E{.k {n4j7xe -iZ_z˽W`8ov?/1ψlkSG{#r<;!>ww2Z 6'H Տ RsPh}̺fY8=A(Ze15Qhɓmq\SdHLbȿBg rFϟ]ZC2Q'&浜zJpwJ?-M&aS%#&Y[쏑2b Bಕ -3iNMcȸ`KSlF:sJ.j?@ X?ǩ\ 5ӛ3sv 6" ѰDXK.rϴ* h)I[Ze=͒|!gܤFy9܅|8,j͢Ic%m!@\<߄FL6h}g,NG VHMhIUӐ$Cy|D` ^zVεؘk.N\k-/KQQ5|(,z{m`"2fm&<&ӿށT/$K}?pXM]qޚWP%nZI* |ٹrGj_7jQ|5{sdl~MʯJֿ5&X**e}!Ҩ!Ls(e|՚(ķG(W-AՌa PvjkY8! "`fMOώLɸںGe!]Uli/FW˿?@L(}l"":fUN8$|_]5K[Kac;1䪲  mV&ʳ96FKyt:XxUUxCcgFY1+W`&Zy tJ Krx?*'u`!Nf UWw-DŽ6g94p2B~ i#:bC0O꣛CiWH8 o4殕pcN b3f9>zS2jR񏑮uy B /7oj\ɬ <rg_YVjˠóJۿ;2iqQ qUEq4h? "*;xSJ3Σ݉K Eh{[)kCf3fXm%e `U]s_獫-qJm(yQwkaBqЍl#vn )D51]qn.i 䪤J^La!nckз{(ۼ$۫fmN`4Ȯ\L~:V`I'9e& ?5cFiyUbx3""3hR뷘̠Uz;h* ϩDuMA]`'2PNXX=6OR"xM u/gKn׹fЃOVUi5y2{81\\.CK.8^P`,Qo[ Jgv5=:F06(: Qמs,!"<u~ņbͳs A"pH)`s0Ԭ'~%fi^};$}Ȫ&,teʒO-~CAA0ɝ]?o~: *WW4jjwto6NȈç`6_"ȉ\ogϱ^ė޾){7l{2+sp{Ѧ6˛X*ֵfN#38B ޿W\G"5J>$Y41(ŏ `q!/.)K(SA).6 ҍ_` >Syr` KxR!;~~@%ɼh7Nǝ$lc Ry!|Fs. e1~<3sJ#@:A`dazҩM-'bhx3X3v?ݜ;:? 0hOUMGC^|hX2j{w, Ķ{@Bf(]B^ĆHanV6ərπ,|ZU s<-4d@ÔHG*a0Fk8h;*M*Ҭ`OeZ:4s5 ): ,ak%~q/+%T%)%.?<7j=76?bÏM퉂M2 ^)g9vnz[W*z?=/U/UWPj8']eGQ>a`RJ1ut%b̎o5&N ƈ$S2&X7񝩭wZxNEd'vɦ0&f4ؕv?9 o=;nXPJnף."naM2/vJˆ3Op>GKh["0 naC׼`;Z!^nSw]x >%)»>]4{ncz'WjD6QE?,c)FtTRdh%p\ݍ v&_Nz ~KeC„ɮ_˓xpv1²w&'e(f̶/|1aFGx8d+DVhDDu"]ʪG\5+%1g ֭/\J\4983YlCTŰzZ} E Wf/52j]0]E$"n5P%ٞo!hד }߿:v"u+;œ% ˝υ]](AVQ We"Y j 0o~StkoIcI!tB*(mRm/;WE~ &vy.n@FئB&<' Uap1|Vt~OY?P?B& i^"F.RUe&12_ o{ CʕM=}|i`9WP.lu%BOEᔴ뇊R\TqE5&L<,M5|ϋҧEeT4W^2Κw-ɚ^Ks?Gb?Q;(rSȯUǧ'e ;Ex#*i+j߃Dji*l:R! =$y*]Yd δvD\LZ-$bq=AAȕ%]a݉HKSk@TSnȈ'He~+y`83,Ē(h0LׄS.(c5~lQ6ֶ/lycKl | a~]yP}uֆ+چv<":cAO!cXKmfϊ-|%ʰ1ߘ1L,*< :=O=3y¬ "36 [l2#YF7cl,met5f?HL4Q{HF21+ XX#ny>b9[Hlt]i;Aݙ2L *(V_I`XWTȶ[M[;Lkj4ȸBC\qMZɔ^Z+?RA쯬s`l($cXmNY, '$6&#Ztfe WOM{WΜt}=4/Ww6Zʞ((. K*Va¦cN/=B+(?N?7F(q!KɲEc"NLğ[S8c 찢~"J)|!.ۤJ/?ϓϕ C4-dHxcCJ6^^ݥ;(@-;|%ײ4ɔ6tlxS ;óUa+R&I<^`*P}F)c1X,ruX6D[ʇ<0`5~\&Xn"Y^VщaCbL}W`i <.2P_Zf08Ev#*0u |7: ʅxM|9=jĩUcyZ\5c]-ļ@cWy˼T[=a0w^v): RER:pv ?6x^>gIQ QY֋8,L1ys!h h5:zuN^L+yOA;#P@PձIdР "e9hٺ_amu ^* z#b;R8,:Xy\<8҅TNOP[®<߫Utgc /~|Gmu;(yr:#T`4vL2e@,4wpɵ*?/=&S gx+LCYeL5@'褫?NY&nMG_hp/ > =U>[+ko,kFRXf2]9Yz3#D\HŖXKܫ2[8`ތ8?B,IzY+jM $ Y?W+ .{/b-sS4/)F"i|`cWbkgCi*JAR>G:"%VPyvs93Ze6NN..K,#3]I3.бyS@f<}Uh C2@^A:9ќ4 zf\('>'5+E7. a(LEOEA1jE!3xT(7ߞ IDATfh}wIWIRsh[ju!e Tm;*>9:,u񺩥m .y!Aqq|.j3vW?6n[[ײU8#'M #M0W&{"5 =QU.Wݲ~Y%r ?$ "S!gYWDt):1k}ف5'izn;#/Di*ј1ZDf9 NT6'׻ '⮦H/)#ƻ7&XwKXiPMR_/mK"Gn!;4hZPZH$Rtu%Q*h %_m _V4+^{Ot¸lY∝mVl PeGڿP9ʾV7~]hwbkxzxSPך@T& $6ry#gY-\ܫ qMb9 _,R{1!`O|O]H'ȦϤ- 7g! 2+UwEtymh\}B8HHˮ'זǹOqΘ'j@^%q]}f苶4O}3SV -&F q^$ʻ8[NvWW)Ut۟+"AۧUkɅ=&=' FiScH!/]g9-p8DƖ0sŀ̆GlQ88X[0i>L *6.g`3wj^=RLq[bW.S% K|c?uCNܭ{x7ezT>!·'hR(BǣD kmGr/WgAaU;XhuS@9ȱY%La|^,UefB9#3>T x1G588ġ%񌽲ΰ[)d?PPz?PR]ڲ)>H9jv|Ha9W$:%9ksu=S1y 27:UDdYK #YvoKhugYx=%,3am{\6eh ebneiݡjg7 3p߲~2c0$?uZ&aW(]i=vw)QC,DƌNE._ Yvd]%D8f!ize!1 /a~L>@%~z?Ty1J_37c>ѠY-?6qt06;ƍ{kdSH@֪x""<(Z kvN䬦oǰdg aPDZMTs>fCNC<}w9NorG] uɦI&-)0AyI7C{f*BE/R~߂Gpf$|J[6-^Lf@O)0g!3 k=*YTLyX;oL ye!D(>5 弳SO_|\]8MB%'AtDIޣ ^"q_}%`gSW]Eھ6ry!dB4ϥ\V [~!YL$Y/Aid{DmQ: 5Y"lf=,c LJE ApvϒiYiLW^@oWM#9-ZD@Plm=lwI65:+'iR)lSi`K <+_ⱼpTxDeY"̯$-3!RKt{{rhpbי$y=; 4Vu]f. tJŸS^TC_ul)JtzG|`2 ?} NSFRgϗ?A/xLaFxhN5rmwfu.;tXM=Pmfl eHsƎ?@d*8טsFS5w&P@e X3b杫( >ww"'[{#ZEDn1tpx=e`B阬4hf]jAP.`HSY K2*[h.t@ |f8mX(ёw&ő(m *Lg*,Ru [,}ܶAj/kfS'qo'h4 ShL#XJAtK⺍ϧEV}Od曆,34WVVJ4=q0puNpږ0jBf[%46ztRz ek#Loi@ t%Dd|.80h1캇o&WzD9Uqq%^;o?2P8;S{1rm|VH7$NB#fo1Ð]t NgRz+&7|Ò >"_Yyp7d+|!",!I6,MLbf䘐JbɵYv-6?A'Sj22_;]/"D`Cv˵SDt6 m[CJmg4uٔ%"T)@$0/!1HBLZ)q$hanY(LQ އ/5?BhWJfgZ8r>GW; mƥey°?DZ~8TgZ&tD7[d ~w6MVS4Y,|ShBTMH@Mm0 M;W g)&\J,!yWo]9% n~[(Yp{+?` hw1}ƄL+YJ*$ߞ zę&`u:ŵcȗCq{4R$ ieh [ 'k;s*| W$Ka*j‘xܰ[WL;Y} mr~\̐uЯw3ɏ4K4Q,- YkѯX̷cWʕqUʕ /v wV;5XI\Hi,fȴc ^,+тb?{^:$`1Wg vAYó 6-脈LV~tA7^;ժꑥ{a|1l 'ޙ{2 `6 0O&hX/QnMj-U5aɧDLц/RQ֨hbJUgF|7,Xzu=j%g|و׃jZ2QTUEƮ SM]9Ker2'C8;ѿa"XAlMКd$&е"$Kc1P1L\%\kؾxn>8Vj?~iȷ'IY*ة0VIEqAW?*S(: ULHK#; 'nj~ YwI,T4"T_Y0%dĶ^/d"M= #MtUjDc>2^- :npQ`^V0f0ܻqgSs*C 9WlHFXGF {8i#cr,x kmKrEwkF"M&ݦ⡈n}}07+C _%iy'W,(D.U\wVP*.x$hH.{& (Jk鄂{ ҄\PHg޸(rEkK~tョ}yqc| @uick@T;W3$٬k'o4S*|.V,G ;ʺs.`=TOB:E)Zi!cJ>6heº.5ē{fJ v9I}U#KkEk[ VB+YBDTv >jEqҚ]tcf}y7ajȭ1NjIekeh4,YaVE#z=0ȏɡPಔi;"Bm#bbʶq? ][X/8{ w'ojѦ `w)PbY:5{77&:L-J_h)%¤ N±M VhtMDL8Fc8r K"}fӂI\Rj*P{KmdiXB>:>>XYf5nOS6=#n`ހIXiQAPxA] L(vqz.};]؞]^&@r9 spr=n݀zS.ꨪXh{bF&j=l1єSvAuK> [Q,/Ja xe}65WJy0Hסad x|G ̸~] Fiq*!W0Ic)V˥w!{:PrOO?T9]ZqFEd[XicXw |JbbټYgW9kﬓ?b<@ߌ|UxO#o{\tMyXF؏6 \'`K O!s~K˨ LHơ=佇;UrD%F)RDui6|!x¸DtQs{rq(j.ƿ&#f{k鼖_ܓsyP%/SuqZ"Cۻ,u\CǤ!>@JڮTW]~Ȳ, H;tϔj+dp1 Y5Gur<Wݓʹ/a{Ic@%"=:;f)=YK \3?!ː\v38l4Nm[{[hGtW{2o@6#e#ç:UraqG(4zJC'XaZZsI&1҈ɇԷGE4iF֣ha3i:N X;,TYӀ%ހ`ɉxI`Ժ}yɝ+b+*3-HRFVC^Z_tě*Lj0$U +Ӻ}]V%cJr;OKV?t=-yRī<(Yu3x=?HN՜h]mN,smR'1 !j!2IA!wzHڴ̔تp+4SeV  C).i.X/?fRMUV]Jsȭeq"-;yAdB=yZ&ͩ*#Op$8B94%D]4('L,F#Zo(lL9l ]/}̧YA$;o36KX -x&YΗ3=KQ#Ҭz7QcTvP!V{l IDAT!YgI@[t|宾`땎5~>YnN:\.&6m]q2I|މ`k.I)R\Eҟ[2>#A$@DD2xg zpzS:Szpj6z")K0Ө6+bC< Ji5K3'i0JҐƣv1 /E}'"(D7 KS"=G=BM>#gO9t0ǞwK֏?vmoc'bi{-=>-z@WfGwxcnGGoЦ$KX?|p"FrC[_Ƙt,VyU n~=Py΢>z84Uv?&.\$N%ZV v~ ڃdr"ɷ,A?m|޽p mB)௚ViUAxȱ'5ɫ /!X]'ٸB~m{_89^MJSǖ\eV|ZlJ2i;vþG$TгÔϬ"XѹiyZg'#LMٿ15KZU_h .WqPܒ!N>بgn|sT'4NK/E<d maLD$-EKצfxJb2pR2 ٵ0مg>cw*Q o;s[6KCQ 1%ُ)"F #bl7 5Mb&q_G 9kkwYz\ʕ@5QY?6YM^ AVrD4TJ6MA|:c{@rYǞ:*"6݁hae`%A-V񠈯_` qu-ij2 oo\S,m?dV\'taƪI5hiqT-۰uXRJ (Ǧ ,a^1P}lQM-Z^wHZ!gMtpQmAKPR$*ʱ"' -bu?}ԨXf궪&P_ -{[׽ɸJ=>q,tbLWOly|)_Er'n#R%݂Kk*$w]3;r`<4}G0P)ٹʤ{9rj.dyźYd_ȨJGc"@g\PMx7\Vko~,eO@/; SU`JK (=Nzh?בȞ]N@xbA}~)c;t03[x` Rv\{ Cf #nÎ/~ /L|#wJmlTٷ~f ؕɁF14W/%bmW}|YyjlsAI(i15dJ_8%ǚ9TN,&׽EX3kƨ{LhXūֺĶɁ C kD,[#1Џ:uk D#50a8?RPe>j!s,dl5b\Iӳt \A8OS`ahqk-,@XEa }_~7~}kug K@ь;#Ȗew4÷ iʹGڒ-ZӣRsIA{pQ8`"K2겮p\ۭ[t&xc $ Һ˓@4God1ekk2kدb&7jKM)q잼֓#-$XttHbUa2g E\d-d<1`ʴvEį}x>ZKWo 8ՏoIJ]Ga0Lz:&Q W)@?4-} jVқwZ_.|Wg}a,%Qy)S!.&;D[ⵟiPX#m՟ߚ=^ͱ-[iʕrوN,c3wsD>]k/& tf5x_d` ,Pkc*OJi~u!-x %3F`,RWUn`#'1nk0>:,ݓC$]1tX/z`{œN*6Hi]+]\tbRkH.ȱƦӡG Xn'/8a2`GQJjs▲Z$O'gz//%0>b굧|aQ E}{--jGi*,ec"nyWcTY-ޫX!m(zjX|t;wOD\pwV+s|2u78V*kƐ`j-6/MRi:D' /'Xt pӮ߾sfxJZX^b i7f(GPLz)WXvD[HoF,99Fk y?'6U~^Z|u+W:Q-l˹2ߌ݋$Xv̅1ר#^.V*JgT}ͽ C f$"!R̽RzXU_G'Km&)pF)pH]lx(eP-gŬjh@ִF|h#SCe{8㲛xH3oA$[7]ZHȚ}_-=ʱE|@.dC ݻo׭rn|v/wsG?Q/5MISXv\eEmƧDj1YKzIH]\M}oOXػ^%S9zS*Vj˶#L6TjkpW!D!xi|;b=qjӑB#斶ppyrfk}|7r8Dv CKQar4u~MbC:;Ko+*ėEON o<Ę^C%CI4fb"i0ɻH},fA.{z{=fDiM'IDs>$}L>4cjR|Rw3>ZY̳vZn*`OxvF@Oq8'n-P°HCr,{ղǴ[Ѣ]Amq3Mj-G39.xc)H(9]$ӝEu\3+i-|OVTpK~FO8&L{–&{:,PVF>^-R_%g!E>\~!rVu"fR_*=21i8ΐ _-EJ+ V>g{ŧ ='f:e:: e,p tX4$;]8t$ZҪb022 bq ~2E8\i3?%K?%F,o=氣)N'e@o8Ȃ;)v1ȅıT! 5+Fӳjx@h-cՊ]lO3ٖl{j/ˈ fҁk-Ԟ͉I2XX"V,wX?#ϩ@5{NOZ []ALm*xgZ=&MNa^S*a7젏S%KvNH8jVkY._Ɓr%X}(tY)ę=G96z6!XI؋3BjkmBJ`j?+s`2D\z!~)M>f]2E0d 3&uAV+hI](wp [8xGog&%VlקKfAEs2~P>650,UHi bdk5Iq%4,jֈeeX;D6i p=]uSBD4 #PjGRߑFOZWj3d%}2aBGI(؂0V6S%˵tcměYJ) a3}v˽>LlꇶrHFoUv|0 W06ŲL)6=H]]g=Ƭǃd>{e~0S ". P]6S֔O/$1tnda}ހ`I(d؏O4K7Op$nG(kqbM.\60,^(6Oܕ2=%u{׬Ο4a/;X׃$J7_Y$ BxQx>T#sR5짋)%Q2X\u%OLɨjFoTX Plw0>0InAGh[[G(gh'82*Y 2?>h {,(E 4*rjQ?ra>T .H4]|1~6Vj tǫ& w(?QX^fyaW äU}k*~<«QMAj/Mv@mj]mYG$$-3tivNdk0w4F𤬱+Wbj&[7RjhpȊum^݃~eӺ4{遮+w݁@#nC'|~>u#dTRٕ^sL\RnJtXFߪgFz ak GztZ+ȥqx B);&Ms3AbIY(zUoEW'-Lϴo( fF4t=A*Ii>2?L~e=jcE2 F9OR!0J?qz_ gW,CK=i2OfaER@pxD nZ7aX$IjV$nR`+z`F¯ , z,؀\gk&t%̞Lh>"xEDz& X\j=&ZiJZX_vcj[F?*y~_[[WV4- Vf=ߥ!gEEfi8o OJvaGG>)ŜeR=CK6HyO Tj8etxB¬iJz36~fŘ98&dѿDݜ<u}QUu1QJ(S?gt5Cb- ?$Ʋ-M571KV]-ݾM><1vֈ[*Xem*ϐomwc`XTrLS ej6xK3zktR~t1ft^fWƮ6%5$(K׆hKN݂@O{.z?vl̅4z,GB]҅S,Whk)=@=WōɹL卸 7V[Ja%ֱ1kA[- ӆRF2I+K<:e=LU6+Vtn{q#p ?_x$}Ϯ,)vJ\|4OW*[Jz ~}"upadfM|w2?ǢsޑK9"yI:œ\ѦINMN $zxgsOV7rguO[C߿]2|<՛U}CRmw`P=Uhzm̀S9K%#+gNtE5M;as@P&X /' )zp wYv{"Uit@ U :eYcVG.d1);A`'6,§~G;˶(ϑ L>["@.~8fc̢ɿPc૜<Ma$>,+LI "#{~j# ,%E<;s=`w Z%уW΅C]kn#}=~BӸ6:eWbE+}t”=~خ5^ EHMsYF: _ZD'ެ7lRHSDl܆.% sbO#o&𠿉`  i ‘.^Y5lE,ArJMkK+wDf~!MUÆK{J 'nY\A]ϥqV~ Jn騱 [W1EĒPhrb}.Z$C6-t_WiΞkӒڛ*oH$ʫխ#BK5RVO޼_'#ꥯ"tMm=ηέIWΑ~Sļm!V@lK$2c7K1J*|l"]9#U\b3*"?ErWgNE: ZNef}@߁OK":i/4?d"YNpJW-2IdC>4+J ;nI.LKUﮠP#Ӯ$蔚cDV Zȫ&?F 4').]m&-M;u&|ݥC `/n%)f#d%fۃS%IIF̶lb5{az-aO1UV>nAaJ ) J`l* ?q|Mn}쬓H4=Xİh+XB䬈 ?Ҕuiz(RtC!=M+ѐeƲcԔ:?p,M7৛0獞^?Hfb؜ƴ6'K""DIJÎ+H1A܆` 82%mb%qى*&vK7aqEp&+T]tZ)`X1͕yFElؼ  { .U<?g ҭաw2R /^I*9&7aRkH"@J}? Hq=~ ժJ&$,]doJtؗ+53c?[4,Nn &)O|;>aNjIe&L~Xk=$ WUM-B|a\myr=cžDBoVL{5[Q*`N`B"">QM ǝiok_ b.Taʉv<+WW 6E5)1Jɶ,=!`)2 rn>]y[_Y^-9th^|E96^ɐLm32VN~@5M$ilW.~ֶ8 V-~+ /pN^Bt{ZJ=LtaQW[X1q >Zq4 =&=&'uE}/ѷE_"{Ȕd}1<;ӚkV.g)yS*҉]y & ioorȉc`=:I -3)ؒx.?x2c5.ekǿ#^${Ufbjj,_qA۞UKHA0@skKz/a]/v`qbs|3J0^DHa/BA|{I8'AUky/%d05*(||o}Ws%"bVZq̉+Uַq7ʼn_?RďAEAq1  H  . AA " $XAA  b`AA\ ,  AEAq1H  . AA " $XAA  b`AA\ ,  AEAq1H  . AA " $XAA  b`AA\ ,  AEAq1H  . AA " $XAA  b`AA\ ,  AEAq1H  . AA " $XAA  b`AA\ ,  AEAq1H  . AA " $XAA  b`AA\ ,  AEAq1H  . AA " $XAA  b`AA\ ,  AEAq1H  . AA " $XAA  b`AA\ ,  AEAq1H  . AA " $XAA  b`AA\ ,  AEAq1H  . AA " $XAA  b`AA\ ,  AEAq1H  . AA " $XAA  b?P˾qOIENDB`pyformex-0.8.6/pyformex/doc/html/_images/props.png0000644000211500021150000001222211655052026022072 0ustar benebene00000000000000PNG  IHDR , '?PLTE@@@@@  @@ @@@ @@zJbKGDH pHYsMMHhIDATxv# `ELb;cF\Er.o 8wͧNt:Nt:Nt:Nt:Nt:Nt:Nt:Nt:Nߢ}<">( Ϳ~{PX` ;Hdw kيk@"@@^}mu [ { 7m.d+d䛏@/d+dG~ ?Gz ?fGv &Gr Gn wGj VfGf 7&Gb G^ GZ _eGV _%GR wGN vrGJ wWerGF 7%rGB rG> G: dG6 $G2 'Krيky#lE5HtE& [ { g>‼`7$1yyq#-yl|@=X!@fzj~ [ { >Ҽźȕ,o|$yu# I\HG > G >Rdv )|d"ht H|$"lr " |HD8?z 2@s}`"AD<;r R@SɁ}"A~7b(7y>I D3j @4>h|`H D/b ʁ@t>x|G D+Z q@>Xh}`F D'R 8}p df% @&|0A dj#@f|dn!)@|Adr9|@|dvIt@&}Adz Yl@f}d~ id@}pAday\@}PdeT@|0YAdiL@V|Ydm%D@|YAdq5<@|Ydi,Ȣ Yd U,@?YAde$@_qYdu@P(|P`Ar  @0wq 6x @0>`|Aw$@@>|Drg߆  s{>\"! #ȇ1DC#n8|~^T@ﯤ=B}"!{ej"@>"]D<_dD#W 8  Ⱦ"|bH}_Q_B}DX+>B}P_I|%]n^, @>ݟ{/o b#ʇ8r wbw 6>bo F>܁Xpbg V>b_ f>pbW v>|bO >\XpbG ><b? >pb7 >b / >܀Xpb ' >b  >pb >|8 >\xp ><{ .>ps >>8k N>́x0c ^>[ n>0S ~>l8K >Lx0C >,; > 03 >8 + >̀x0 # > >0 >lH >LD0H >,H<@b|$H< p A>@|D $H40` a>@| $H,@P >@}@D$H$P@ >p@B}+5}D7*R(uv&8 BP2j@q:,T' B^^#x{!@ܞ B= yy ո2)AdVI!o6B܀X ո2'5%yej\"dsc!@>0Yd^=#[! BDM@5 9A@LjH< TCds\ \Ȕo}yԇ|81!2!DȌag z!B@섀jH} "LR؇3qw J!R@j脈}xQ "HJȋ4NGf 6B@5@4BP17ԁ( broo !!@,DB BbA P !A@BD/TC/$ \Hԁ(j(A :-P -8 `!q@BP+$V@@B@5@B"@D AP P!@BB dR X @!@BP2(Tc PH0` 8!@pB@5D 1 &TcLH8p (!@PB@5f 1$TcH ! @0B@5`0a"TcD !K@B@5V p Tc @ u!$@օj,Yd] g,TcȲPE Bh*TcȪPU Bx,N(TcȢPe B &TdM:5!L@f0Y$TdI!T@Vj@@Yda ! B@5 @Cd^ȼP y!l@2-TdZHa: B@5t@nXII!1@N`NI!* oV i9!!@l` 1%ȔF SB@5@jL ~QB(PH?1%TC!DŽ(  dB˩!J =B`   | D-Tc-V[, ?]H D+TCVBEo(j@ŚkFH?W !rK+TC 亇J@TB@5W焀jத kr(5P ܽXB@5`bM wv*7ѩCDr @BP'gD.T\;\v b!3"aD,t0=ԹY հ"D8P̀j ID6:ԩ հ"hv "!C3"aD$$@$BPgf D"TDH 9@)$@'r-ub@j,@.ȥP c BZȕyYa JH 95\I|E" G s!(dr*$%E@΄r%Qȉd@I(dXH6 dr2jy($G95<ȃME@I@"@F drwWF G BR,%Q=!9ٖQȨ䎐@K (dUHV _er2j"$- 95|ȧyE@> I @"@F dagf G BRy45Q{!ۚQȨ䝐@~M(dKHv ?fr2j)$=95E@ )mu G 7!|]QȨ䛐@nK95܄t{iF AI(Qȫ"@95  UG0glB=,\Uc]t:Nt:Nt:Nt:Nt:Nt:Nt:Nt:Nl6rA%tEXtdate:create2010-11-06T12:51:08+01:00s7v%tEXtdate:modify2009-11-16T23:26:11+01:009[tIENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentD16L40d22n10b25.png0000644000211500021150000012150511247035000024541 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?i IDATx*Fɹ?lL+:ɚsdTlk,6~4' @s*М 4' @s*М 4' @s*М 4' @s*М 4' @s*М 4' @s*М 4' @s*М 4' @s*М 4' @s*М 4' МAm_ '8@s aL^K6þ2% 0f$[?X+"ֈ2S o: sLզ`xs?=`# 7D`wh6_^bqC@56WcZqN:׸31# 3hsp~Z}j,2Սwge$*L_UVD?#"vK9S=&G x?kK*m/{lنNrElofd8i+HcFY"btnМѫ֜LWx^YR@*|UTe"FmRl)K;T8+UFm=R[ D57bSJ1^ 70FD}j3|NSI.] 4':͈/yc® wzN1Yc#C|wc)9 ?ܮM9"FSS7߀=w4%)b6Pc>y|*IZV֊;!}@n-(.iǑLz<$[2{МZ*KEzP{߁ssu ɳ,@s6v0çBvBĊ1һ(밸k=,$9k7{i+a4Fd3"bkw3c Ka!Bfpi/ܝBg?@ŊlVD̿T[E`fd _EfG͔jh2erF/6$ܦJe.o씛Ev:C^OYmj%w5d[%٩9 Ysͦb?#"v+(vȃ{F93ZN"ط#}J~-ϴ; j#;~4'LiHk. CsaCYv MNiVo04'LByfFgDnYrѦüCMeBw- c9`ԛlyU4?y-_ Zy`)TU줍`ݝ9ؗ N%8'R?]͸#uEs)L?ֿ/6+,8V<1>xs*~Hzz`wkςv SNZ􅮼ZUYSaׂz1Felxd<״'33?c׏v"h+@ žr"rDތ2Be()-QÌߎernsa?~hNh'Pb3'旦dG,X;_Mo-4ԠAΎO}otGWЂ49ib5eimlQ6m{7s;As8f ?GW䇮Qw*/9Dy|'lcƥ\tGDVz\%}M!9TRƨ_ɶF] =p>'dcѯahԩ1QuD`~rsCR$|Ic.*jBV፡ΠN:wwf {IM˾ MN[΁ M>ʳb@Зߚw[+9 ! ^ qN(FbLҾ|zcg͊2 _Ԛ9X96EߏsnSoVLJO? '-1c3.>2 0]\:ӜJjA^ eCoफ़&YgG{|i Du+v -煞f텾q!x4']w ԦWZuWK#Ln5z g^М]2Gv*JHJ#Ϝrq(O+jtIp A.OgB 9EyJjd3 s{gj B?f]gC9Cpj[:K,/7t*Scq~}wGmJNJ?_WT\w{*—%ꦁga}[ fgLIL4^didw vҽynKw9~V5p۟{wRϋ5_wHaМI/9LVd;Zs*4~%G]v`{o9 _9W-y7bRr]vnڎlIg@ 5o8R2(7`!_TQ9BϘ]s&j爊XwnʊK{hܔxڶGmoqN/א;N 5+hY˞p{|{jp%>طhΜcG3I=I-sh-σ7xI㮰]6 IpfZJ_rpقzD񼖃|w SU_^!\ȭx1Ş*8&kSJlhx09޹Ir>cPZWrlSLHR\r2o33B&6[mu |lasQ=O #"FH31Fjcc<F ev*2yl߆:]yVhy?5Yl!o ֈs=22Jvgj[WTk.v6vl[7/mobOyݮL9`%Gaƚ[W֊圷d #߿=Ky "hNqUVc7êllYfrIP.-mx ɳ%s8XToAe%pvf{P7吞SYY6j,U0kr`9ށS5cKbvtg9Ol`:Y ) :%9^@@>{Ezrfu;qyE~ht,s>:aSYIBМ ]u ~#~BC99BǿuWoĚm7 gAsRa&lx@޲1T`!mHszeZwfb6- qt+Rq>H(ʲuw<4_:ģC bc.;.9i1"ݲs$9uI= . g^f7I&="`k㪥Ү5rʍ"0o3R-}bOw%Të93 }5b'|geKm׻dXL$3YFWo4zN$w 6U*wgu-=fhטfsiUZmy!ok+{+/ s%N%*kK>uKĵ-1IN&{Ϋm]όee@ 0^9vd3)_Jݴb3:@TT*r=3=f5rm+vx~ȭD&?صƑZJlARL.G6糈?-ȃL9]kvZoy4l59f o h x~ƃq!PgƷF+`=[AМl6['Hud֝_UYtS|){8½ 9FIn y+mxժK}4Ɗq{6K[d#Z/hN1/6g-ۧxن4~]y۷âLAs `ΑLH!v}ϲTQ|c ÉK{e_fɹ=5m ?torKv7VoKXaЖܴWܸ]\W+U㌾+rM&ApwD7(-m8" Ў4N rvT(jd9Km#t4+=*;feAӉȊW!6t@z1ɥ17,n&}$m5o\p:YA^М-ɜ ghÍ"?4Sz% wYݣ۠9sr s// H~>H$Z=m\{~^|hNמ$/V,pSxdٮp޿CO5'^C@J|RvaUwgQ56-ܞDݖ:ɲ7!]!ߦFkIwTI\gs&R,s0Iy3sLk<*F8R %ۿ{'kwއSUo]oLu ~UJ+%3"3,Y1j~m[[s^9| 7#gίa}CmLQ]ï^"R i:0*#Us)!}Uͷ[0xMnmنz~M%Y=KqNā͜l n[_6HkC52P%S>xo~6bU|];yE6JY֥<]4's-[+^iJy\V~.~B9nD"b7E<As_9eU\3YV.wnuhyjζ|MyyEoSt1c3dfX rNB9U4?1FfXAk[Ցs ?饰DyF+-cZiW(@7Kxa|fΎG~܄\o 6_G Q뇆oE 04'%9eEՒ8 qcxˤC0NUM@i^;/4 @sK=MG~w ;>?yW_;K_H2:N _mt[uWK㦮4^6c zaaN{"SN^h (#F6{(D1gz28?ڱkljͼYy vݮ Zx^?e_mn57d߽P6]ψyΎ񍩶Lg hNZ~Gs{g"bڸ/_|a}ռo'N>MSg!5B4tiא#An-|!|XF=Mi=n *{c) Su&6C56ݎ\k;G6>FxQ3OBG{ <ΌŠ1bmtJ)8:?Ӛ-ެ𶘸Ԑ[ ߃/ f2V.oy`:vJg?YE/V W/MLً3u =vAû IDAT\ħӠ9pnAl$w\3YtX.wnihy`И1E⺂ZF<Udl] ~z `:ףɏm_uYTYYك1mvQ(OWO-P W_ϙO駄hp۳!|hfќ! ޔׯv NtAMk|*~V{pV O s4l'iNUKgHH~RnFz1O}e/mv\/3+0>fWO9V7;TZ7kN7 v!.D=ƥn__9ٖ\ݤv rk58sSf)_kcN=Wbd3)vlSՐ[g[Vޗ8+ wPwTO>g#^o2]rZ-4_}?ZsoA3޾?jr, / gĩF /biwۡ>rsԮ({sهӼ! g\oS2b&&睲 Hisl\b:6A>԰֥nm 1 e~eB i&|t٦% mN$Y-^+TwQ9rϞDGA}8~@)vYp'49'_4_ZE}- v04O-,u}mu]$nRrl3BxSX;kJfSԦSm;La\Q^iUC9{$ th3PkE6+""6˵Thʍ"0ca os+OpMS$gp9zչ4',&X)y/~8.%skMT`ǀd9kZE<lLstTk  vX s2ʫ㜋geQdzIg"wҘ`McϽLpG.RO9-CD+$ װގΞoT.jZ9ث->X  s%ҥQ99Y/Sn $=a] ً2 ujycZU\Q/9wu S͘r=k?SDǛQQPK4NhipWN~o ে;$ZG&?ص&0ဎ'̵rds^o>֫YFQ!:{qBTE{Es98 p_Y N}A ϥ]BMM3I[,vw_T[Xbl s=6^]-y-;}W_UYtS|YJhSXT{3gф]mT5{eIS pz~ 4'L % 9isp+ƅenG9ss7?ř7aY8TLS y&4'4ڷ$".nAy"?4cu݂#(/ ZۓX|/4=& -NXr s/zpu397o(G]/Pݸڬ~_- f󯫕%?%tY`E4:{=~N`596u~k$'Ԉc" U}|nGG>~h畒=A!H9t}d{ኽAƽZМaxۘV){n3" dZYrW;q7Aw} $=գϞ9:oE KLRu rX  ¢"L{ݺZdʶ9?8^*•]JdVgwY͘^g/s_|%&=hN8 xҁ)i&OLfwkDHdStz /֊Lxq8ӮoLʤ㊏>"Ͼi7ѲKMB45 &üoΖPkar-B_נs,{_65jozN?O*gW&ݬ#6mֹ `j_4 eWxJ½X ~kkň1Fw%$MR?T0qN?+9?6f̿n( ƃTM+6sCsN Pg*-2}tKg- -SUlUi7L9p>'_l*vqUڸwi^/C׶vj܏Fu[=8!vTW R>#IV>iAO o^iܟ?YRjXhbFww_TFQ-t=6-*МlMzZW2UUH5FqE'+s7>Ըũ"fI%&oAzo '4'J\ZHZ1kY"7Ґb[cs~ZvNk'e ; {KF;qlg|1(6^6c zmRj1^蝐i3iaf41*h6l9`!/-OS;P70ǁT`_ϦeKwswVk (n ;*U^$.͎1eݕ~cDd۱stvJw-Ԧ3,}'x<;kӌuVdY/8 k4' Pbv|2-/WUڲxs&k39xkljSB.M.v8Av޵3׭rf޾Mk=47^ȭ8=LO8 QTV]g˹ﻘmhu:3O5.bLk/',cL(RBkn|cnIsќ8 s:Υ|Y)Ci|u"H|haA3雴 B83HD\|Z67lQ\F 9~8fmyKЃTՁli5I<[ϣ񐔪OWN^f,ğ\o >^r* Τ7J5ʳX6CCt'5tǼC)s+ThS?V|hN#D^QM!$՝o"oc՝&Ɋsdtv5d=TlWDtLHv}RC-ULahҩ۵IM<V)P9k5M#2_;UWT*= P|J5bߪm u7hN! Df"M^逅R ސas﮲(qFsvCzeO94ޙKE`*+ƈ} :wـx4'7NH9*̰fE9q_~k+goṌpop*&=Ęs SBZk݉X~5 OԲg 0I!2&TfbG#C0)mEޛ848ITBl]53uvJ(N*3\\;s ?=cҭ4cN#;UX`=u[QO\Ymk)jx=\َR"l0Ip""gnBe9{[޾Y8SجE7G6||iМ/1HMmjDi>=b+}jNt91-}1B9-1ANzW=vQ乖IZRfKtme%yqJAs/ 0ΑJ 0͑]Fӊ_U)>QN; CZjP,?'Mbl'GEeR2ͺk')VD?S?dփx%}}MxV)fO:)fQ9hW*෢tSi1ɥ;Uu'9T&C\}Ǿ_P*Ym`OֵG\o1p'2ogg|)\|]jxd/_bdiO^VM.Gny슙|ksz?UXvV@}~v g /Szzy ^5)g֢x _+&==ZdJ~c+]n^7 fX_yksi 2NOgZ1n""7Cp6՚ Yfo-M 3Ouڌ̽̕h|H^G~DI;͏Ɯp_8j`) );oלM}>~&a1 ˹oj WtWi7/ޠ "F\;zjSڕC%U[i*Ÿ,z]i#s4'HEuՑRR^X3 *j6e۽v ɯ5jKIL4Z{gd`c[4m3ey`HKv &g>j[ytUn}rr)Y%'9A=pxPYm|O;Qtn裊~(>XU5f\o-{}I|PJy^ =u4'0r qd*}N6s\oocKrN nnAv]My溹0yNc.IαEp\/ *;j\]Wl38jëo\$W[U)P9ZՊRGSWuq)1e*71vMoՆkwƨmrKlEi^ 8wA^v'vs2N ?<S4!6nHI6]ፏOa^2CN0*-G#[d޸δ} p R$ˋ@Z+Y1A_享#fRټ[xIr OUE4MyRԧξKSv]s^qA`-y|fy& p'hNN!2^AX.;4J΀dc7(L dCTmlmcNo8i>6y_8kk;$6%P3G}a?BxFX twMk[5qĝ4&@`p B8K6s1zxu[:ur_xt7bA5 ]`ʐ/ar[:)܋"ek[K8{"bWXi/aeƚ2kN@\D9cski?p nB|u;$ݲkG&2OJ#)%[ 0LGyݜƃq>I.ИifAk[Rycd`}_)LU}oW|̹R ۙL9ר:]c4'$65t wsVƁzֆmBDlF?sk`zkQ\RF >|?*7S09R(|P/ 0sgWy7YygXyxR@{NyNsҜy0 Bgsں+5 fO$[G *؃$}Q@ټ "~7d ZМ\=+FlaUfoZhMie4&u* F~zQvq @D".)L3 n^TRقz`$j+aT~TWq뫻NRph!;r\@X`m卨zT{5 Nt=2M3_p9௢ oa&Mәq ϡx0?B5x66!5vbTfUNkBC 4wL.}="V=LoLԯ? (-^_ft4պU{:S r-ajUX:Ơ^|Wm0U ^Q' 'sn:KVUv}3pߣ7ƄH}_BW(ǝZӘ˙R|{ js|jՔ7K9$*^iҔMj 5(rˣL/svf_w[O+FawfHUjRS$3=k6)DeϦfr(L {Óϴ]sLljcN^mj˥s}ĖSG`R@?"ݾMƥ;(5 ^2=ezY~4+ɍ[zŘ6NL9aҽbkȞɹ}mg,pbKvvo<,}Kn0!HkڰH)T-77 4WܸLR8v2]Mi"3y2S$:XIL0#W7m$ٞjw1uxxO=Z5 yĉL2ҋ.?xK@p5ȭh'9;*u5sEU$8˹ﻘU*|WWi5.y/j^ $b}Ƶ|X5r;(e]G!uҍdbf E/ߛ:3~/ϱN. [>Yڸ\Pg}N^LG3fIvbTpkY&YEmME3gF#Ѵ $(/@x VgjS5|b=/ UփJ'_T׽5'O ^ f§OhլFa%wECEL>?a=Ր:Gco8'4q^_kP3#'͏$ϜGlӒqz:< Aەɣ ;d,k;7M:Kfo(7({0|hN~_,DZ*䳌p/S"ys kdSv'X~5yN~1nd&8ȭҌu`TXG  rs)!}dz5Ҟڲ nR ;JƆtuӛbOڞ`o=sIqL"km˵8)HOp#7~8'|!q`3'ۖנ%aK&> Xɧ7 մMgnTFe@S"{ss@έgҊ?R/0ť %?\Мm(dYzّ=Ŗf{~dekڋ:ZitIܼFOèYqOgl |wE TÛruAa`ΎGDQ܄rP?MmKDn;B.J2~IZ HM=\>ϸ?Z(ΐ֜8S U/}]r+ogS_hrjͳXsrDI`Q}L{Ɋ1Gmb Gfs+ٳj71'pFގSzz95^4J [#)ƵTlO5w\4+]WII_3 fK[5 N?)#!q9O`ӝflZar7R#lgrBp}9Z&=6_:Wih|Hȏ( xQvТ4φKٽTyU #8`rk}8OG8Os6rj g?Zy.k4Utj]鵱04N62pEzm!1Vy :vlbQkb~wBFV3"~KڜՃ4'֣%__Uly?7/.VlnsȞ Q|}'tdN]+k '#IJ<-_`hNx)75ũʯ֜߬9NgV[JCZpRތ$I?&#ߡVTgJR~[a겴CloJ?l[C e˖+_;UW7eks^Ff5oՆkwƨmG½W>,\ss2N ?b<MP0!6nQ6]ψyYH9CW%Dk?óbIQ|LyJ|ON \@&uEUc"ǁ|iSK -#T6AQI4˹B֚rvxcMV^qA`-{F݉m]γa2!no\ZLު' }܏ݘiY#EqfiG,Kj찙y]}1A@f*8Jl2V1i̗wtjz4_>HZA%gbEn2`=',Up꾈.уPS|,se8Xy\\{+jq\hp۳!Dy,{W ̿Q>qUH?e`l ^2bW N sPr5ݟ 1堡|46@kORycd`}_ƉPs( Vf])]LaҜy0 Bgsں+Z_ܺKo\Xq5m$N5&w|aVįL'| hNtsΙڞ|v!y֊k61VuMm3Oul[7mFJ߮n /Ecy\Y_@ҹ۵_O {sO656'Uen2N8paU=.mtjsM]I-4A|$S!ݸUڍ7j/g[f${ZsH裁 (eaT/wvVGi DW@su]묨,;WjΝOpBjW+նRw*me="{ 5o֜.̻!ŪzdgnZʂ'4'rc+ҫ)NVnAsΫm_4q͞5lTv4$OmMv>9ս/eg9tm]n _9@\#eQ";*C*M"7f6^>bELxzPgk_m;n 6^P&ݻlru=_rzN|y]"8As@ gf; 6,W:=lv7#[[ ۚU{ݱNcs.b;-9H Z>otzXOn~5eG: 6.u(,SM\WUS&:f W-# cʖ%9~?Vmhvgږzd1li}"9' fyU)NzQꝱ)Awg觰v֔DSm;LJ ~7'_/ߺx}6'^NJu. MNX2.KuQ+9kE6+""6˵\z su,#l%ɉJTiɳu5sCXtz8wrNW+n75:7Lx4'DN!2IH"Z(X1 Y3bWRYc9: ir~O$*/~G=sHx4'xj3gѥd!(c6]? YDd\Æ[P918Myr~U벡ɓcJ?ES>U,~2Kfx|)hNيn \?;ˇnՙUxѭlSk\ʷ >KgǛI)lƊ1F* SU1#{֘K͎u m]f?wz%{Gp4O6I˘Џr`6)>=H|׭r6jjpV &,vqxقzD`8>W4Uщv֫YFQ!:{qBTEs 7[c7\-)?4 |zSN_X̗"xgK8ߧ޵ N}A ϡ[BMԙP$_eL;/@ay-x2lκ 5JjXnEi1uͭjM!0KI&yij4ed.?ުwd]CXJS۶b _`vz G]g<4UEwrPc9)r)y׽2z;~c*l1K=հ7٤ZP2<\π8'5Pް:ٹeq#_rWɦ&( & ,' !U9)'CI.&J3VN}6}2  =/h٬1ސ>k+T6łhSׁkl]iowbﵙ6Q-SϹ}(1jbeVK*P+}ݞ$[ 72 X%c&E`CɼE?P)︂@[/KD**'_%ݔl)kh_S5 wqvU㩅c?7%/+H/ȌBMv-[p>TQF\ O.rEه$PK&%o81U61%&X G gMh^@SOsE~NfvGbv >|h_^׮Ek֋&(T{$.- RVh,@8'g%_:qA#-uÛo^ u]0 n:-ƗBz"dGRwߗ\WoX!8bs.#֦0cƢ.79hszl畖+BGg:t4i5X1&]yu00&ew';+m8I"RE Ik"1OuO^-}+%{C߷swgN5-$}EO%!ղb~" C잖Bp[U{n3wj\jYrW¸h]w_I;etlg|[76:M *% u /OOͽ( lL&6~k6*R%R":c&rfLl{.=/7LfOT R=~H As|7E%>]2>YqtA.]^h9CˮDA綵"-^δdf_g&_7GiY&2䥵Ͽ ;i l\`(0KCrjiΩz,'fjx8ݹM19 G˙hN׳?F;ql/8fK'3sAOMCʳ_-$ 6S$.^ tf0:V@j "Pֈ܍C<|.oSaOz`WtLr^{V^?ɶYO<࡮*dur+5`I͊1sbuףd7n8'1FD_L.rJQ:Wsy7&yVK4<;k؞khm*X| þǞ yAqC.Мo%Pb{TYʙR5>ūλrenF㭅⳩N yrLz@CV ?9G~D%I J획K/(t_Eɑ `x% <;#Qq!U.|hhLxwjpCucqA#MU^rƲ',dZsmƭ]!W-]J, 2s $gx)Ha-^$Y묨,;9~n^]URGjSݲ/>Ӹk64α Q~7h.̻<rerjG3fƲ<-_pxZ]zTav͙Q,Ҝל;௵Fe(w.HMGƴ^cM3.S_/;֑)T'=Mۍ.DRbZP5:̗waϱ:m(0zfgky nURImu%xܕũх_u^N Koxޏ4*m|BJlno4͝Nku]C1زMS*Ct\rkų5>0! lNȭxDN^ ݠ>ԹUS>G6Ope¢߶tWl[Cѥ;*pکjUo2!A[.Mk_ k:ٞ-~VIb>M,eng3~1)|М ]^W.ɣq';^ƝBh ) m2jm8U.uMd33GZnh@7VWÞ'yAj`1Y *+-<$eaiZk?.2bndTvZ&lm1kᢸatw-kC5~\yJ>As,9g=)3=Ls͎u*A7}횝_0)"G7vؿrޭ=b 2.iȣ " iPm  JXC ZCcSuZL2,?]hLkvIYGuk4cN?r a5lS!?5޶)S4y߁9P ofL??~37oԏ׽-ٜz{n* G_u(n8!`=M櫓PLj>Ap<`*.'OOmYcWW^v88Bf-aAKyEu:AUOo9]~es;вyqJYqZ2jqМPW/ۍZP#;[~G|V.g(F!-5Đ;f6ωD?[v~Q {zʙCџ@I&@sLBOE.ut3 S*4ңsN+ѮUjM$~խϰPK8j Ѓ۬UVVi0r}>G'ٶ%@:0 7KhیLdx6xod\yʬ޶~yTg9\q܋LJxGsTeogǯQZPTyUۂx% 0ө=9d?]!y:ΥtWi7/ޠmNI9BknUɜ;pŸkC~)hE/ąV%9JМcDQ]\ Kޢ9&Q4I'Y-mrTD㮵w*me=CoӜ9qeys0mcuؤ5%;or[z<zNUsUؠVV@*$ٯ<+ghp2E-{<[j!urJ)'eX>x2ȏ+KNx4bN@YMf[_Mܫ5Sd18}PjPa|\.>u]Lb_KmD 룶hQ.sY֥xGֽ,őzdQW|y4QŪ+]5T(輠=z5_ ޱy- pOAoI\qخfk^\uݛT3Rۆ]oPƸ1D!~ns$bNteϫ>wSEC~u@4La@վJtwyմeB5,$μ_LzPv#4(E7&%m{ZG'Nmvj~OoyͿ:Y~?S8֯ZcoHocѼik6:\ϙhc]2פ3nc>54Jп-f߈2;ODZ*H XyN@'yժVzn¡mzmjei9@>3. uyZfS= <0C{.| ùF3"o@IKXRʜ;`Wڡ1ဥkGghz j^3՚OrTvdR?Zsül䍎ԏw=!9)@7*jɟq n$v!:q4=ط8 5Ng 3m&|1bN`J'|/E//a qj"T7ޗ#KV pyCo5a,}.O~ {cͤg# ݺV,UHz /gԶKk|}W{G\=c}DW :^'%ȷ(S&\Z]p`s\nf-@0}R7fyU׿}Cu/wyHj[&Y ^A%}S{vI^kN?2 ^ x8jke^`_ƾk G߫j;]y*x͍::˵2ʛ_-Z{^,׹__,19%u'jkm͡V)ld@<:z=T瑬(I+F^mYC yT,V:V0QSW5@$r^v zݿt}oP/EL:Σv?'i4˗?-"2g_|m9q&[/if{RYW &E/Jfc.=8Ƚ1&?$\f9U0G1a /YX֐+iͰ #Y7HaGK45{[3aˮ†=Ƅ]tw`}9M֘9kcxd Ӟy5/kj߸{eY+ |Kyg, a^Qx"y뒀 o[Ib㸓}{yEWSD'2kƹcV ]h9H𛨭UaZ(02G+LB:b.ޛsX&rtNYNûuyN@I + #@a<))o9ǯ+6Դבs&&w$g _,8-q8ٓ5s9 M㽥q(4ha5"1aS[N*щCȓ_ƀ8/s~956Xߚ]Ù}{&rQ{ _gIlm4UJLطɓr~ j[4`?!ϿQ3߸fpB3cio#Ϯ8Y!}=av&C:_ZZ^6>bN[ֱ m/Z[Mz¹S dKŬ BP?jॵ'>BE 3n ;o{+"Oe4[Yw>ٽv_+B~rPmqLz8p:gXzgI-Tu&DV2fѮ$TiE%DΖ^ߙmw]r#_VތWѦػI(?Dӛ%: :uh]Cj#CoI]VhFqeVFǏTrbN ) |"sfݒ ڷѤy Hukkx_UcxTe5AEN X9R%U3l_զͬRrKGGA;,MvIp-gǓ\9Ye>9}XRR*b zݿٚ7 '`eĜo䍼fgWʨC#ovgŜQ|8ͭ1׮S&KsN_>C x(jk5Ki(e͸Jlq]ЛNXs;衔M|Br ,u?Yѣ%'่9}DzoKod`m^YMVvZus/N3tBFܯpGm- 3bu؂E~F-\ީ%Y[}vVkS/?v9c/KW]hg\o~ 8sVOԑsjM?ή83x} 2K7w.͍i*#X-X~rs#<\7{7;3K,ĥr:uXٷm_f9ߩc).Zq)_Z<1'0֚aoՔ 9cgBh+%}vz^l6}C}%zT_.!/(Y)Lm_)ʷm_1WиN>͏5~E+QŃWތRF-5wɽPasbxzN@oʫ'Mݫ~gn/tv׼6 e5iP[uXzk$9l(Gtc&U }zW3w6)y#|bN>`o.u;ݤG}h1\h_YnCWϮ,BeSYA{DoE ~,vKѪe1똥AR*cY8ܞbA3:#\M@w7|VU]V0MO'e{#e\VU;7_ wtw I9 89IѬ|7_5wG)&Zhs.eLvT [k)@3_]X]`jk9LWn"kdёOyYhYi6uקUKLKc_FL9zƻ BE=Տ_<֫uhǺ\ت&m8\ S?& E#[_}c _\y:fxgq@Ŀ_o,v2l/UC82Јڧh5]V Ӈ%%zX1,^03uZ'N!y{jރU,u׺hŽ:'s^GK)s{-A)|3'u1ڭ^:_9Nk\t%" >GA~k]!h>~8L6vOYY!ERsgқ9iH) O_찪3pzjk'\(A;7Ym'XjPs+=vU#.h bNy݋YWֽ`9DOui j2cէ:Y_O )YºRg{= 'AR*uy8촯ϦftDtb!`2Ȟ= [>{=e^tI2hxwSېhs@~QQ;& 9IaÈn4kĜ&v̳?2v*Ɯ=&NcK;Mv#+]b1ȌԺfyR됢ѝuD6,1 8ت35hJvKXUxOSYzuG/-r{ 6z=5pt)u}0U9Tuz5ד)Ms SJʯ\!,luCj E4/_{ֆ)h^4 l[k\KEdzk!5:(/[cIp݆'3a׮YkJز13ܶk?mo*s_h_;K9Nd|5><@֫J$'ԨsR3L0˹?NF#&pK:DtzϕN/OmE|~5cL>U 90Bk%@Z7D*NO"Xh襶^x=(/&)˙1'嶻gB|f dPGj 輫 %q{L8Zb?I .bN]1aSQ\rk"@:bNW(•9D_Gu >][jM i \i#M!~"i- 1=]naDŽ[oH#:Kik`'&sVؙ%cǓ9wb"Y֤6B \-i1/MnB rP{gxiONVCm-p`mv[C@jbtgQm8"Xy+UpĜ>q¹*x >#}} 4V}9Ebهe5AEN jQJn @:k3ɋNG(FՐO5ƚcڄ1X%ZݿiILesS}QD;qIQ8*M2BaօDCJ9-<l/kj唪^+lқNtLѩݸ2Rr560z$sS3~BMG::$VaVƬ7X: N<'{AHsN`)s>pPĜB4,MAA)5nS{򐍡%h@ L׏/DN~soŇ]0& z$i3'B H9ﻒ[eM6w7KNgEk~^?Ķ+oE[OHvFܱwcl.OiN,;rΥ˺qfaZ`}Zls.S\3>C0$䴜2\w5/^[9J~IЎtHܰrt#J2WqX V*N}I3uaڂω;N3QB`8bNxp{'9I{FIEg0A ,@v(n`ƒCb'|zVn+ZV]?s sOu;>sPknNJM5N3r3mhóRy}yŨmmv#'Mmx89_껯~1'`ެÕ~HS9.gsvUZzxV G\ !OxVt7+JݕVhJ=}olǫ{t@ /`='lu+ zY#7OGV沭ξbsDk,cJ$;Em-x~;̺LJH.PrR\o?WM/5gZ`呛Bz5scU{ ;MeÊ1gI^UjE ,CN\|B4s&T:RZ9⪰ؑt%`vݷK t N5cͫs v,&r{> 2Bsj{d7nJ|6H21yNy#6R8Hr+r`o3^ Ii6iX "9_U "T9ă+{ if|~Tc`#}AtX5s,@yN3`ٿYz 5N}jU?zr@<'k/&x~[OI un 8 Z/}k'xgƝ*uWҮ΍9i}~Q x1'dDTsm'=}clK;Kt3_GGz`='^o~jwѵǢG}w>[-y~S mX Nw`ZWy?4wBb0r,^c^[w Em-jO_W'.G4tؙZSk8@?bNCv܉1몰8Ll[ }wMbNЏwNN) ꪆWql,}a`='e?ϓcf6)_NvbZD5._n:|le̻`ZƘ;*)+v -.nc&9MXAk;$ЃZ7كwfX1xs jOksoǰFm-c̰"hWƚ5tl7.oрV4zz B=1sZ29/Oiph=r.9 6[-mH},R*/٧_Ě9"xȊ :3cn ĜfȆXyC k]s.~h܁gܙXxJ|> 1<'iYh#p#[\_=SOj&̌OAQ[ s=}=VE3gA֯䶖}?q3uAWcQ[ s.;}=å-⡣1f.ӑ_]@T絥x%,3  9Qz=^|nEyEK9ٿelU jkw$X?=IĚʲAo{,xOnz֭I,y}ͤAfA [k\ zN]U5Mf{0IǞ=]G3׼5s?-)̬inv=$zNsJ7ohDL6Hh6`9|kn w`;(q9D >D6u&Rk6P[ (}鵑}6sHJ:_c^crX3QӹpI'?sȊ#dsS:U(?^Gm- >fe6-gekVt <9dS'| dbcc̗ ň9?Wu2oOG @@)/3ڧ ϭLjs-\ALk97 |Q3dny 1'}CTHJi9a~Dm-lJs8s SC*S<[sq*ǽaYUfs18NmjZs2Yc2k6?@{sXc5Iyσ/cԩ" ~1'J&[v ~@_Fm-.?p3s \IDAToildXotD ~fN!Q[ Wzk3/^ƘU0yNf8_9]PZ XCc͎"!8! f݋9 B 2[mpR@ՇѨf~$W ) 8jT#bwBcT!'@bNƖZ2O9:$hCm-bNяaa's:,ڶhg!(!;@ Kv,"gj$0yN(ĜQ9sF!B 0 1'`bN(ĜQ9sF!B 0 1'`bN(ĜQ9sF!B 0 1'`bN(ĜQ9sF!B 0 1'`bN(ĜQ9sF!B 0 1'``vB2IENDB`pyformex-0.8.6/pyformex/doc/html/_images/WireStentD16L40d22n10b50.png0000644000211500021150000005532311247035000024543 0ustar benebene00000000000000PNG  IHDR9j pHYsaa?i IDATx뒫*P85OHbQеkfw:QDbUUp[2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s.Յ8.Ȝhͪ9ҫ < <919Q <~Nuډi[Ȋ hӷ s0՗xi I73Z:(#dNS&$` 0e`Ҧ9lɜY. .@>_ 1n8 0z5 036w/` s ɜm1P%9h03-s~i)oAӷǟ(|#ȜU] G9Clj9O7YwdU,hɜx0lμ9gX6c,i \! s3l]+4 򉂮uGȏ Ԫ[7c ua9[IngDAdGtv $g Mɜ]us(&'sЌ')@d5QdNp*5BL~28(6_>B9`7(6_X269L#ؖ Yo߫)gJq ~^W !W~9C'rKqPO ߾_|PU5;?3{}0un?'rQ\) `3s@&Ӷn쩜fܞ˜3-$ RQZ(PWdq&C)}\3mZ[! /4YVUc0mk .Ϻl&22ֈ,WU AYZwMC($RQZUN6 m:[P2rc謚fkV=ao ݶǍEÓɜ'mo(vL؝vuF08#Q]˴p.]`s:\+CތzA žEMɜp8C U[;6"7 XC* s%}U#؝6֧:9gMux֍F1eڼĉs57^Cwuf-V=*=&_l~l`Q2'lH&!c,|' ZZƿ bR5:- pX|\ij#8niNiu,?LAWVtCȜpOyBj!9K8A/Y33UeP7[qfU 䅓ƿQwYt2',|aCg?8ϙhgI_΀5HyU85\o8p8wn4ZC1 0ZϦF+E-wub-ysjyWycl\idk.gl-z !YQktP=F>}E>PvفS /x _wlf798#n>]rS-nfs:p~~O:q#odüyɜd#М&i{V9p_.LȜd1EjUz޵M{1Sns@\ tbo#O09T?h*湕c'v;ԵP4SEN{'l8B.΁ m|ЉgO_%& ŏ_,$pnR%arAT۩{6׆&Whs(2_Bx L/i)^YU'U< 98xlKn'-c_8Piw5 2I)vjȨ '8BWψˤ̯̋$sҒc5qmMo?p%2hOW_xyVyT&dH ŷnƤmkOv`^gku 9k?:e!O2' ,(5'7}D;NU>?&spUCg3Oŝ,>uݸZbl?6/ r.{e98ھJi pLJi'.gf^R;ɜ5.lAon0lQVNlP^ě r&sM̳V%Y\¿`_]UN/起+A`=ipfD$]V]%Bk# 9ۂo2'+ۜmϙY@*̠KXNh[kNY_ux|sSW4A90pizٳ n̓;p WU U|NȬBl4|p2M8I9Xb,q9@>jɬIMOB%UfƵ;[ђOlj^зUamS|J2 aU}זZ)Kx/SEy)+Zʁi2'siO ̃j}k:99dž5i"EY2'n*#:3l~fXAZ}:o[^.F C;gV$*տ*^z`鵎S(Xa1gkoQKw76hWi<$(m+êΤH2gl-m{gZdy6,U_%d!g%W&ŀɜ|mNcәȎli=GzBM΂}dnKxr2'!sbL UGK&ŀ"CFia3CD&][[p^]&"1bv V5* Sv&-JaZ[67iHz$r{Qo~%FK(cIk8x29J{ab8ChwheXX"d59ՕJ ~NGufYU: wo{sn|o56831T789j^O2Izbl-L.aVo/3nyp/wxis 2GxpeUxKN\2)ZyGWk\7m~Ynʒ[COYp}w}T~༺^tLx&ŀ[ $S=R 푴[XxڶhqYYh?lfl-3zlnk 5ڦgwرsUikx6gwΡ P.ck85fr}ƟLGp.oy!Gz( iydO.q'g o{;$v[Ow#/;ܰw?}ҽ-vT\8/ћo!(lKck*p>qn (~NNޒLr#{mvY9-a% dI^[侮>\V8~:9/W^cp\ZTo$io_425dZ)7Ռ1A{l.0. J,mR XXM_q-U33. BU}~ڪ:ҔM}Cβ1XF;Ys5:?b249N> wg :BW[8MJJ Ykƣͽ*T%+5;-ʛ~}]&87*pwƠl\gy/ާZk МH-C'e,6$w4r V_r1dN+?P&j ٖ--_fZס=6C~pմ=M :p'\H8C&ݧ!VP:f-ZqUyV[O%pi]8˺Ơ,2'ǰ IDATn~pNg64.ŋ7ERU uf =ZCڀ94rmuɯ9M\yx컓V b%#.?'CHlMK|h[0W&hۏj`8ib+Qp'IH&yf,\UR20TVmF;I[=Yn&(;4Eσ*?[ '9٨U ɪ0/aZ~ktV]>y-^kIqxZȍ p_wD1~i)i4ivY1T4DO9.&.!s9qH[)fWn!Eۤ{S] !_ {Qd&K=k'8SS{cl~0zpM#`iRޘoYyfLJul1Z#Z6k7ǎl\K8ckϻysq!ZG*q"pֳ'ck6:5#ch+vKf!x}bdȜ+E4M;vrr[tr^\A?ms&E:ݑ%O/~N56Ih7=,N@Koͤy9ෝvc !`ig{Dm.p#+# a|9,`+J!s,f\)zl03 SC9r86`+ck O׶~n3|7q &0kB ۺ@! ~(;K܉ T)R7prS}m`i yBZ%.!B*<̹g#K.ܯ ɜ5dμmh^8nu_/˜ʿj3\B|Wekv![+=4߉&2殶jT-@ Egs>μpǵ+O<9-mbiTI cz솭 !ck %ANT{ƚY&Nݣ8$͔Gk[ZG2O"p!@Du;]`SBGpk8Rb$JpG9b3@'#Jͽsڡ =w;([R_'p (|94RM`l/^^%ZE[jbe"aްprVV-v׻ȕ eIk2gDzz)zٖ_q4 VW\ugUeT3@dNKZC=`Hk!o{)'쫯ITyz'{~cbA`-(c uafl tU[r*;,ݕwi7By7hnI=sk&izZ묠T|lQwD`;͜.tf7Ӛv zgY9\M0,}tDZBo.-a 8dN Kkol [JFKxvTs``̙WyN';ɜ@N-t6gv6m^vf]X{7ŌX0V9,lLA)w1˽vՖ-X׾m'"髗xYD˻1m +SSͧ;}6?ְ<fݛIdNR;5vYߦvxv|^}Z1TBgg[3&׆Xn9WƋ+pNJ)wHfbC>N Asw{^%*=sYbfE'ΜU_ !s>^:=C;6s;jl$8[eM&L>-v oz:|>Gts':Qӟsn2ȧsOU^'gxX?FRBTxQ)mrԘ^{l^x^jeX$2-'֘TބޛU5\Ng8WxmF:_WU ^Q z'nN&1a'Pf%-`w>Nl~mLK7SGkg8vo)2X;6iML9|Qin$i+)FpW9gJ83Ep 946ovnƓhG)kpPoq L֮^` [ht8Am16Qz͐cOe#p|7s86 ꏡ$p^~m:/a7s3s_l^{fNsf{N8ΆF{*'~'%zC>~)o`Y8K &s\&̙m(d 9Ϝ81:cN[ٿpO΂.sGk15́!8CGyQ/y OSp|VÑ : u QQ@dN`S1ٟb=4lقU\9hz^}29Nl[ 6hh+Xknu5K۞3;Ϥ]<Ϳ J~_='^y4ps+W!Vyz,Ywe=Ze%RO0>nC7er 8dvZ 3;-^U|20Glyz|Ő罈RyQ66H+8Ϟ`%ck5o?n _}5/r懢(}`mhTH+_>o!]//jO?'Eu vh=WNO{YM1z3cia0繼Ȝ 3Mi6w_ӓA9P2sNfnx8-ʹ@54oSAxwIN!䷄eyFkA)X>?74Ow)<2'0I`3¾ʦ꾻:61BNYe:t`94z@iD2835µ,PVlK,ҦAO}d-y{wx?3~l=H|=(9Q߬wAᠶd!}6Zz| ai<{B=7z1V!t-SU <1N/.UHJVI'~Kw}# #KW;p Σn z Ѧ.\To3 ~uSn//"E|¹SbnU'wp396mfAS(2yY5NVnz8RS߷Zd [="ACgSPo =SgU}go^ս0#89>ȽɼYx }䭖!ݒT@:9yk^?D ~p ? < g )M|$Qc N{^f^[OtNmdN(Vsn U645t~w}$I?K7نyքh3xZ12'ܖxz۹~"Ԯ<.^Pt Eٓ6WMۘ $V/w6_R\]Uiڷᴤsf]96dN(ξͅ :m* Pu↹;B'2> ;uwW fbBto,!sB vke5IVm=/+WwN"mb[OsÁ,|t}է~}v1 ciqfpC5vٱcOfN2Nt9{1]oGK>&zANȜy їNKqz 2v` C642u]tmY~Uc|B/텽jťk]8uoruH{j(gP;b?ܠsm[}ݔZyr,E*8slfcS/0%WEty[5ߪro-w؅c( 9d&<9/%{xN3=5M=@C&۶\ 4nAHy} {Mߌύdr/Fsq$Q=-#j6?- 8&ɜ{̹Eٯ睋"P 8;7pr'z?faq秊?A}*_!OV6s?'\m[rx UMz}ٿg3h9]~al-\jCjL42&2_!Πo6urh 0vs4xSN~ѱPO=g`9[ I:-1s>R2XjoK !zw.] @;Π9n~!o;0vEUi\@#hӽYտB.FQP':ƾoߎWR)xsn'4|n9ff33I w9½'@sBB >~,a9ӫ(\|.g!~q^s$Z&Ne/x%ZR?a`ŻO_ <9a["4_.л"#[yt8ưF͹ Pk,2a|tJlobHV~%f,mU{7m5aq,uXI:j~V޺ 2'lmDNB_(QUD_ O`!XƁuoq>lFi_E%L7򎝃 "sJws3cw5n2/vs@/ߩA`l-!pd} rH|+e7d¯~2'd %]/ɢ&6 6{[Xq~i84a)mIDATZXƷqSnR̰ɜk5 3u(^#ivhؽ[@á%ldb[}!e+ޒRdNȃV&֞]0zݎ;AԺ,w;b yh WdNZ&Q1 D6h7ul,lB dufcȜ@ .v~Y)Z)zqWEajzLZCj%.](km?/΢lvݝUՒu'ȜpLg&ŸE;%wƪMĚ:{]&sE$ tvvO˳*C9̹J^1zެk dN4U?oc BkMNȜp|\W\KF-f/j-9*^2POeʤ}bk|m?T?#86uVc`}ɜTYj8Ig*WYP1ZY>C7fr,eY~4Eσ*?Q` *dUA|Cuz F Z+JK(NߵdrIIi<-cdN8fj]q$ȟq`y9 'ɜp.271h;̞t]mW!TҾњG11+p&oDhٺ+K͜\W̅[Wu !})Ӛi ?!b?p GEms⊳"\Yg{Y=U2]&9!iP%clnw1ٿ7Όɜ|;WjhnkbI&bH6dNH,'\gLtZNIxV%d4!gF8+ĿW ,ɤՉ)RbnosF '0ȡ9(ۂb !q[t&=dV)V)d-X-~ħ\<娎9rH:3O Ǡ9!OWp§cy\Y5jvj 9F09r/֯p;I8郱Ӎ\pfmϫX{%{hz̀uIq- Vy}P2F .3 Woz{˦A-Ȇ 3:HzZb80A|\1uTdNXg&~2~23nAn\gܫg\俫 ekW[!ƙh>>ÿIk0x.ū}s?' 1GHyym&:6ġ'hh8j 69! Bj=)P7!:֚0/ @:A|tjp ÃckcCYΫW{u|* {˴䳂`%-9 O[A?wz"4 ۘج ?0Ȝ[d4aNjlYW_!PsDB-v:2i#{4u9N< v~?˝t_Uv-޷xύypqyqp2'siSnpʒ1J[(eԉWU{Z}K歹o &"]%W PemQsE 72'Ԛ,71nGٙܺ @dN侽sF>G7s60'[8QWWZgy?ݥ|`ԞSSsh [oE@sz7pbnk",ӿ7O۱k@ =8s._Ӿ/8/vYs/✆p9a%qM˜/LJjT3Ȝp;4BLZ0"Qx''H4֪U9a M|P5?Y&obvUI,vea/ߌ܏ \av|?!/)n^I]NnFo_5:sܫs[pzmO( `ǫ:Oð͵3fzb!"Ȝ gi>uGh8ͮ+zi{<%lɜpGM: 0j̙uƎ iHfZNAl$s2}je4nf;3'p2'܂a(t1j;u[<3~ 6'9a:-"fz|N wu2^E3C|C/m59űj<.F.44(]1bLquGrUƯs: eM%݆;KrM[׽ -ɜPüJ;9+VS>}eqIs}-8+WV+}; ;ɜa5RHl-N3y}[sAJpŚAC D.s!Om2o\tRwNL?'.r9vqu oUC"J-3 8W0&?u~;pM{g9.d΍;dإa9o'ȜP⛛ =*lR+m@]]UcL$ߤ3>5:W5I?'k&9cįW}F0ZW|)4u|s x J&I\Z0A1WŜHHbelraW!Wnvdve{5Q12'|"C ~/?L\ I}T'5瀫 i6z2˼B )P[+j>6Qc=*p6?s@ %DD,z86p0 IirWB,KIC=t9Ȝ4L,v 䫒=ứ r%s,U͜hm. Gׁ3o. XA-%KRCp;OKQ:kN]]JkV'鈱*nغKؗ)0PB۸)ƲW9'bvW)9E7m6T4.ȜPr۸#-aOFyՒW aizE+W6<_U;tUA?'fƄۙ~hi'oHz>X`1sB9ZܒtSE=0mF7܀YY[ f˧:kDdN Ng媓 P\dN& Fz2!s;9ߋ4}kkޥMHJ}Yх!+m|ЙxvpiG7XҪP*2)yZ%dN8X}9 ߫S;pw(@愄@G& jg1&1^:B2wx@.!CXZGyw1Pغ 99<#hmr?L2ͯ0, \Z8OE_χAyaKz5Wmjy,dNF;9V!l.ߠCHo~2 sB[}ZτĜ@Gm|vSdNWUZyק 9mzw>MjϪ~dN(JSwrer6x%~N&U'sM#QLz˗N 2'dlw7Vߔʴe^B / 2'<ȪBh dy (3ϨC1T; dK愇MMOJ]@ɜ@sY%)z7:9c aqtA@dN`؆Y g .Խ E9y fi=(~k߆6xZ8_u*el~Agv3o; &y L9!W7hV!Œͅ^@fqR sv7xg[ Hk=iP"DdN~y_3!턌WV +-%D3iVa4?g__'pbB1UB7'9-~ `Mz@np+~2'@עѳ pȜ?-4 5x o2'Dߜٍ΍܌ <9mV9GQQ'~Ԍ V@Yzش s7^yQ(i 5`!@ZJ =(yNdN Ц kɜ@CXE2$jn '62'f!Pɜ@< 8AM9HEA''!< p"s @*2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s @*2'Ȝ"s :o=}e\spqN. 98'\spqN. 98'\Ke~R;IENDB`pyformex-0.8.6/pyformex/doc/html/_images/iso.png0000644000211500021150000000034511300456345021523 0ustar benebene00000000000000PNG  IHDR$xbKGD X pHYsHHFk> vpAgXpIDATHA ?sڍJv.NtZRė8*G w$8ޑhSp6$> qm.xW輰A8ݙ)50"x.4:Bc-ΚIENDB`pyformex-0.8.6/pyformex/doc/html/install.html0000644000211500021150000010514711705104246021160 0ustar benebene00000000000000 Installing pyFormex — pyFormex v0.8.6 documentation

Installing pyFormex

Abstract

This document explains the different ways for obtaining a running pyFormex installation. You will learn how to obtain pyFormex, how to install it, and how to get it running.

Choose installation type

There are several ways to get a running installation of pyFormex, and you may choose the most appropriate method for you, depending on your needs, your current infrastructure and your computer knowledge. We will describe them in detail in this document, and advice you on which method might be the best in your case.

Note

pyFormex on non-Linux systems

pyFormex is being developed on GNU/Linux systems, and most users run it on a GNU/Linux platform. The Running pyFormex on non-Linux systems section holds information on how pyFormex can be run on other platforms.

Let’s first give you an overview of the most important pros and cons of the different install methods.

Official release:

PROS CONS
  • Stable
  • Well supported
  • Easy install procedure
  • Site-wide install
  • GNU/Linux required
  • Root access required
  • Installation of prerequisites required
  • May be missing latest features

Alpha release:

PROS CONS
  • Easy install procedure
  • Site-wide install
  • GNU/Linux required
  • Root access required
  • Installation of prerequisites required
  • Latests features

Development version:

PROS CONS
  • Latest features
  • No root access required
  • GNU/Linux required
  • No install procedure
  • (Usually) single user install
  • Manual installation of prerequisites (and root access) may be required
  • Less stable

BuMPix Live GNU/Linux system:

PROS CONS
  • No GNU/Linux required
  • No root access required
  • No installation required
  • Stable version
  • Easily portable
  • Missing latest features
  • Difficult to upgrade
  • Somewhat slower loading

To sum it up:

  • Unless you want to help with the development, or you absolutely need some of the latest features or bugfixes, or you just can not meet the requirements, the latest Official release is what you want to go for. It gives you the highest degree of stability and support and comes packed in an archive, with an install procedure included.
  • If you need some recent feature of pyFormex that is not yet in an official release, you may be lucky to find it in the latest Alpha release.
  • If the install procedures do not work for you, or you need the absolutely latest development code, you can run pyFormex directly from the anonymously checked out Development version.
  • Finally, if you do not have enough permissions to install the prerequisites, or if you do not have a GNU/Linux system in the first place, or if you just want to try out pyFormex without having to install anything, or if you want a portable system that can you take with you and run anywhere, choose for the BuMPix Live GNU/Linux system on USB stick.

Official release

pyFormex is software under development, and many users run it directly from the latest development sources. This holds a certain risk however, because the development version may at times become unstable or incompatible with previous versions and thus break your applications. At regular times we therefore create official releases, which provide a more stable and better documented and supported version, together with an easy install procedure.

If you can meet the requirements for using an officially packed release, this is the recommended way to install pyFormex. All the software packages needed to run pyFormex can be obtained for free.

To install an official pyFormex release, you need a working GNU/Linux system, root (administrator) privileges to the system, and you need to make sure that the prerequisite packages are installed on the system.

If you need to install a new GNU/Linux system from scratch, and have the choice to pick any distribution, we highly recommend Debian GNU/Linux or derivatives. This is because most of the pyFormex development is done on Debian systems, and we will give you precise install instructions for this system. Also, the Debian software repositories are amongst the most comprehensive to be found on the Internet.

Most popular GNU/Linux distributions provide appropriately packed recent versions of these prerequisites, so that you can install them easily from the pacakge manager of your system. In case a package or version is not available for your system, you can always install it from source. We provide the websites where you can find the source packages.

Prerequisites

In order to install an official release package of pyFormex, you need to have the following installed (and working) on your computer:

Python (http://www.python.org)
Version 2.4 or higher (2.5 is recommended). Nearly all GNU/Linux distributions come with Python installed, so this should not be no major obstacle.
NumPy (http://www.numpy.org)
Version 1.0-rc1 or higher. NumPy is the package used for efficient numerical array operations in Python and is essential for pyFormex.
Qt4 (http://www.trolltech.com/products/qt)
The widget toolkit on which the pyFormex Graphical User Interface (GUI) was built.
PyQt4 (http://www.riverbankcomputing.co.uk/pyqt/index.php)
The Python bindings for Qt4.
PyOpenGL (http://pyopengl.sourceforge.net/)
Python bindings for OpenGL, used for drawing and manipulating the 3D-structures.

If you only want to use the Formex data model and transformation methods and do not need the GUI, then NumPy is all you need. This could e.g. suffice for a non-interactive machine that only does the numerical processing. The install procedure however does not provide this option yet, so you will have to do the install by hand. Currently we recommend to install the whole package including the GUI. Most probably you will want to visualize your structures and for that you need the GUI anyway.

Additionally, we recommend you to also install the GNU C-compiler and the Python and OpenGL header files. The install procedure needs these to compile the pyFormex acceleration library. While pyFormex can run without the library (Python versions will be substituted for all functions in the library), using the library will dramatically speed up some low level operations such as drawing, especially when working with large structures .

Installing prerequisites on Debian GNU/Linux

Debian users should just have to install the packages python-numpy and python-qt4-gl. The latter will install python-qt4 and python-qt4-gl as dependencies. Also, for compiling the acceleration library, you should install python-dev, python-qt4-dev and libglu1-mesa-dev.

Other optional packages that might be useful are admesh, python-scipy, python-numpy-ext, units.

Download pyFormex

Official pyFormex releases can be downloaded from this website: Releases. As of the writing of this manual, the latest release is 0.8.4.

pyFormex is currently distributed in the form of a .tar.gz (tarball) archive. See Install pyFormex for how to proceed further with the downloaded file.

Install pyFormex

Once you have downloaded the tarball, unpack it with the command

tar xvzf pyformex-VERSION.tar.gz

where you replace VERSION with the correct version from the downloaded file. Then go to the created pyformex directory

cd pyformex-VERSION

and execute the following command with root privileges:

python setup.py install --prefix=/usr/local

This will install pyFormex under /usr/local/. You can change the prefix to install pyFormex in some other place.

The installation procedure installs everything into a single directory, and creates a symlink to the executable in /usr/local/bin. You can use the command

pyformex --whereami

to find out where pyFormex is installed.

Finally, a pyFormex installation can usually be removed by giving the command

pyformex --remove

and answering ‘yes’ to the question. You may want to do this before installing a new version, especially if you install a new release of an already existing version.

Install additional software

pyFormex uses a large number of external software packages to enhance its functionality. Some of these packages are so essential, that they were listed as requirements. Others however are merely optional packages: interesting for those users who need them, but not essential for everybody. The user has the choice to install these extras or not.

Some external packages however do not come in an easy to install package, or the available packaged formats do not collaborate well with pyFormex. Therefore, we have created a set of dedicated install script to ease the installation of these external packages. Currently, there is an install procedure for the following packages:

Warning

We provide these installation procedures for your convenience, but take no responsibility for them working correctly.

gl2ps
This package allows to save the OpenGL rendering to a file in vector format. Currently supported are eps, pdf and svg. Our install procedure provides the necessary Python interface and installs the gl2ps library at the same time.
gts
This package (Gnu Triangluted Surfaces) implements a library of powerful functions for operating on triangulated surface models. It also delivers some example programs built with the library. The pyFormex surface plugin uses these for many of its functions. Because the examples programs are usually not installed from distribution specific binary packages, and pyFormex uses customized names for them, we advise you to use our install procedure.
tetgen
This package provides a high quality tetrahedral mesher. pyFormex has some import and export functions for the specific tetgen file formats. Since tetgen is only distributed in source form, we provide this install procedure to help with the compile/install.
calpy
Calpy is an experimental package that provides efficient Finite Element simulations through a Python interface. It does this by calling into a library of compiled Fortran routines. There is currently no distribution to the general public yet, but this install procedure grabs the source from our local FTP server, compiles the library and creates the Python interface. pyFormex comes with some examples that use Calpy as a simulatiopn tool.

To install any of these packages, proceed as follows. Go to the directory where you unpacked the pyFormex distribution: cd pyformex-version. Then go to the pyformex/external subdirectory, where you will find a subdirectory for each of the above packages. Go into the directory of the package you wish to install and execute the following command (with root privileges): ./PACKAGENAME.install all

All these procedures will install under /usr/local. If you wish to change this, you will have to change the install procedure. The install procedures can also be sued to perform only part of the installation process. Thus, ./PACKAGENAME.install get unpack will only download and unpack that package. See the README files and the install procedures themselves for more info.

Alpha release

Official release are only created a couple of times per year, because they require a lot of testing. pyFormex is however developing fast, and as soon as an official release has been made, new features are already being included in the source repository. Sometimes, you may be in need of such a feature, that will only become officially available within several months. Between successive official releases, we create interim alpha releases. They are less well tested (meaning they may contain more bugs) and supported than the official ones, but they may just work well for you.

These alpha releases can be downloaded from the developer FTP site or from our local FTP server. The latter may be slower, but you may find there some old releases or release candidates that are not available on the official server.

They install just like the Official release.

Development version

If the install procedures for the packaged releases do not work for you, or if you want to have the absolutely latest features and bug fixes, then you can run pyFormex directly from the development sources. Obviously, the pyFormex developers use this method, but there are also several normal users who prefer this, because it allows for easy updating to the latest version.

To run pyFormex from the development sources you need to have the same prerequisites installed as for the Official release. Furthermore, you need the Subversion revision control system. You can check whether you have it by trying the command svn help. If you do not have the command, you should install Subversion first. Debian and Ubuntu users can just do apt-get install subversion.

Now you can anonymously check out the latest pyFormex version from the Source code repository at the Project page. You can do this with the command

svn co svn://svn.savannah.nongnu.org/pyformex/trunk/pyformex MYDIR

in which you replace MYDIR with the path name of a directory where you have write permission. Most users choose ~/pyformex as the checkout directory, but this is not required. You can even check out different versions under different path names. If you leave out the MYDIR from the above command, a new directory pyformex will be created in the current path.

Now change into the created MYDIR directory and execute the command ./pyformex. The latest pyFormex version should startup. The acceleration library will however not be available yet. To create the library, goto to the lib subdirectory and execute the command ./configure;make.

You can make the pyformex command in your checked out tree executable from anywhere by creating a symlink under one of the directories in you PATH setting. An example command to achieve this: ln -sfn MYDIR/pyformex/pyformex /home/user/bin

You can update this pyFormex installation at any time to the latest version by issuing the command svn update in your MYDIR/pyformex directory. You can even roll back to any older revision of pyFormex. Just remember that after updating your sources, the compiled library could be out of sync with your new sources. Rebuild the library just like specified above.

BuMPix Live GNU/Linux system

If you do not have access to a running GNU/Linux system, or if the above installation methods fail for some unknown reason (remember, you can ask for help on the pyFormex Support tracker), you can still run pyFormex by using a Bumpix Live GNU/Linux system. Bumpix Live is a full featured Debian GNU/Linux system including pyFormex that can be run from a single removable medium such as a CD or a USB key. Installation instructions can be found in BuMPix Live GNU/Linux system.

Alternatively,

  • if you do not succeed in properly writing the image to a USB key, or
  • if you just want an easy solution without any install troubles, or
  • if you want to financially support the further development of pyFormex, or
  • if you need a large number of pyFormex USB installations,

you may be happy to know that we can provide ready-made BuMPix USB sticks with the pyformex.org logo at a cost hardly exceeding that of production and distribution. If you think this is the right choice for you, just email us for a quotation.

Further guidelines for using the BuMPix system can be found in BuMPix Live GNU/Linux system.

Running pyFormex on non-Linux systems

pyFormex is being developed on GNU/Linux platforms, and most of its users run pyFormex on a GNU/Linux system. Because of that, there is no installation procedure to run pyFormex natively on other systems.

Currently, the easiest way to run pyFormex on a non-Linux system is by using the BuMPix Live GNU/Linux system. We use this frequently with large groups of students in classes having only Windows PCs. We also have some professional users who could no install GNU/Linux due to corporate regulations, that are working this way.

Another possibility is to run a virtual GNU/Linux instance on the platform. There is currently quite good virtualization software available for nearly any platform.

However, as all the essential underlying packages on which pyFormex is built are available for many other platforms (including Windows, Mac), it is (in theory) possible to get pyFormex to run natively on non-Linux platforms. There has already been a successful attempt with a rather old version, but with recent versions nobody has (yet) taken the bother to try it.

Note

pyFormex on Windows Lately there have been some successful attempts to get the basic functionality of pyFormex running on Windows. Thomas Praet has compiled this document on how to proceed. Submit a request on the Support tracker if you need any help.

There may be a few things that have to be changed to successfully run pyFormex on other platforms (especially on Windows), but it should all be rather obvious. If you have some programming experience on your platform, and you want to give it a try, please do so. We certainly are willing to help where we can. And we are certainly interested in feedback about your attempt, whether successful or not.

pyformex-0.8.6/pyformex/doc/html/_static/0000755000211500021150000000000011705105304020236 5ustar benebene00000000000000pyformex-0.8.6/pyformex/doc/html/_static/plus.png0000644000211500021150000000030711503465564021744 0ustar benebene00000000000000PNG  IHDR &q pHYs  tIME 1l9tEXtComment̖RIDATcz(BpipPc |IENDB`pyformex-0.8.6/pyformex/doc/html/_static/basic.css0000644000211500021150000002026611661223601022041 0ustar benebene00000000000000/* * basic.css * ~~~~~~~~~ * * Sphinx stylesheet -- basic theme. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /* -- main layout ----------------------------------------------------------- */ div.clearer { clear: both; } /* -- relbar ---------------------------------------------------------------- */ div.related { width: 100%; font-size: 90%; } div.related h3 { display: none; } div.related ul { margin: 0; padding: 0 0 0 10px; list-style: none; } div.related li { display: inline; } div.related li.right { float: right; margin-right: 5px; } /* -- sidebar --------------------------------------------------------------- */ div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; } div.sphinxsidebar { float: left; width: 230px; margin-left: -100%; font-size: 90%; } div.sphinxsidebar ul { list-style: none; } div.sphinxsidebar ul ul, div.sphinxsidebar ul.want-points { margin-left: 20px; list-style: square; } div.sphinxsidebar ul ul { margin-top: 0; margin-bottom: 0; } div.sphinxsidebar form { margin-top: 10px; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } div.sphinxsidebar input[type="text"] { width: 170px; } div.sphinxsidebar input[type="submit"] { width: 30px; } img { border: 0; } /* -- search page ----------------------------------------------------------- */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* -- index page ------------------------------------------------------------ */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* -- general index --------------------------------------------------------- */ table.indextable { width: 100%; } table.indextable td { text-align: left; vertical-align: top; } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } div.modindex-jumpbox { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 1em 0 1em 0; padding: 0.4em; } div.genindex-jumpbox { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 1em 0 1em 0; padding: 0.4em; } /* -- general body styles --------------------------------------------------- */ a.headerlink { visibility: hidden; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } div.body p.caption { text-align: inherit; } div.body td { text-align: left; } .field-list ul { padding-left: 1em; } .first { margin-top: 0 !important; } p.rubric { margin-top: 30px; font-weight: bold; } img.align-left, .figure.align-left, object.align-left { clear: left; float: left; margin-right: 1em; } img.align-right, .figure.align-right, object.align-right { clear: right; float: right; margin-left: 1em; } img.align-center, .figure.align-center, object.align-center { display: block; margin-left: auto; margin-right: auto; } .align-left { text-align: left; } .align-center { text-align: center; } .align-right { text-align: right; } /* -- sidebars -------------------------------------------------------------- */ div.sidebar { margin: 0 0 0.5em 1em; border: 1px solid #ddb; padding: 7px 7px 0 7px; background-color: #ffe; width: 40%; float: right; } p.sidebar-title { font-weight: bold; } /* -- topics ---------------------------------------------------------------- */ div.topic { border: 1px solid #ccc; padding: 7px 7px 0 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* -- admonitions ----------------------------------------------------------- */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; } div.admonition dt { font-weight: bold; } div.admonition dl { margin-bottom: 0; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } div.body p.centered { text-align: center; margin-top: 25px; } /* -- tables ---------------------------------------------------------------- */ table.docutils { border: 0; border-collapse: collapse; } table.docutils td, table.docutils th { padding: 1px 8px 1px 5px; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #aaa; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } th { text-align: left; padding-right: 5px; } table.citation { border-left: solid 1px gray; margin-left: 1px; } table.citation td { border-bottom: none; } /* -- other body styles ----------------------------------------------------- */ ol.arabic { list-style: decimal; } ol.loweralpha { list-style: lower-alpha; } ol.upperalpha { list-style: upper-alpha; } ol.lowerroman { list-style: lower-roman; } ol.upperroman { list-style: upper-roman; } dl { margin-bottom: 15px; } dd p { margin-top: 0px; } dd ul, dd table { margin-bottom: 10px; } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px; } dt:target, .highlighted { background-color: #fbe54e; } dl.glossary dt { font-weight: bold; font-size: 1.1em; } .field-list ul { margin: 0; padding-left: 1em; } .field-list p { margin: 0; } .refcount { color: #060; } .optional { font-size: 1.3em; } .versionmodified { font-style: italic; } .system-message { background-color: #fda; padding: 5px; border: 3px solid red; } .footnote:target { background-color: #ffa; } .line-block { display: block; margin-top: 1em; margin-bottom: 1em; } .line-block .line-block { margin-top: 0; margin-bottom: 0; margin-left: 1.5em; } .guilabel, .menuselection { font-family: sans-serif; } .accelerator { text-decoration: underline; } .classifier { font-style: oblique; } /* -- code displays --------------------------------------------------------- */ pre { overflow: auto; overflow-y: hidden; /* fixes display issues on Chrome browsers */ } td.linenos pre { padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { margin-left: 0.5em; } table.highlighttable td { padding: 0 0.5em 0 0.5em; } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; } tt.descclassname { background-color: transparent; } tt.xref, a tt { background-color: transparent; font-weight: bold; } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } .viewcode-link { float: right; } .viewcode-back { float: right; font-family: sans-serif; } div.viewcode-block:target { margin: -1px -10px; padding: 0 10px; } /* -- math display ---------------------------------------------------------- */ img.math { vertical-align: middle; } div.body div.math p { text-align: center; } span.eqno { float: right; } /* -- printout stylesheet --------------------------------------------------- */ @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0 !important; width: 100%; } div.sphinxsidebar, div.related, div.footer, #top-link { display: none; } } pyformex-0.8.6/pyformex/doc/html/_static/pyformex-logo-2.png0000644000211500021150000052547311661223601023734 0ustar benebene00000000000000PNG  IHDRm7sRGB pHYs  tIME )%"sz IDATxU|3f͚3f3f ͚3f ͚< IDAT3f  ͚3f͚3f ͚3f͚3f͚3f ͚3f0-͚3f͚3f ͚3fA,  (( ͚3f!͚3f  ͚3f73 ͚3f,8͚3f"͚I IDAT3f͚3f Q ͚3f0&(͚3fQ(͚3f(͚3f  ͚3fR͚3f u+͚3fk͚3f2͚3f ͚3f2A ͚3fAb$͚3f PV͚3f*]͚3f1͚3f͚3f-H͚3fpE͚3f"͚3fG]1͚3f`͚3f/͚3f͚3f1E ͚3f*͚~3f<yB͚3f q͚3fw͚3f2͚3f͚3f8<͚3faa Y͚3f 7   IDATz͚3fTg͚3ft͚3f-͚3f͚3f$P͚3fcd͚~3fi k͚3f)p͚3fn͚3f"͚3f͚3f͚3f͚3f͚3f͚3f͚3f\ ͚3fi_ ͚3f/ \͚3fZ͚3fx͚3f:͚3f͚3f͚3f͚3f͚3f͚3f͚3f͚3f͚3f͚3f͚3f͚3fg͚3fj\p͚3f~  %͚3f!^͚3fy͚3fB͚3f͚3f͚3f ͚3fQ'͚3fB͚3fc  >͚3f Z͚3fS)͚3fI͚3f͚3f ͚3f͚3f ͚3f#3f ͚3f͚3fU 3f8͚3fXm (3fR IDAT͚3f   3f 3f͚3f  3f͚3f͚U|3f 3f͚3f ͚S ͚͚3fT 3f'͚3fH   3f͚3f        3f͚3f  3f͚3f 3f͚3f ͚3f 3f͚3f3f 3fo!X3fOo  3f͚3f  3f͚3f  ͚3f#͚3f͚3f 3f ͚3f ͚3f  3f͚3f%3f͚3f ͚3f ͚3f͚3fP)͚3fx͚3f  h͚3f͚o3f]*͚3f ͚3f  ͚3f ͚3f ͚3f͚3f ͚3f  ͚3f ͚3f  ͚3f ͚3f͚3f.͚3f ͚3f5+͚3f͚3f"N͚3fkY͚e3f/  "͚3f<͚r3f[)1< ͚3f (͚3f ͚3f,9͚3fM ͚3f N͚3f͚3f ͚3f+< ͚3f%͚3f ] ͚3f1͚3f  ͚3fF! " ͚3f͚3f$I ͚y3ff͚3fT    u͚3fs͚3fW% ͚3f!8͚3f# ͚3f  ͚3fI   aX IDAT͚3fN ͚3f@"( ͚3f?0͚3f͚3f ͚3fE% ͚3f"8͚3f$͚3fY  ͚3f,͚3f͚3f79 ͚3f)"!͚3f͚3f%K͚3fMx͚C3f    Q͚3f:y͚3fd͚3f ͚3f26͚3f͚3f ͚3f B,͚3f&c/ ͚3f͚3fG+͚3f! ͚3f6D͚3f͚3f ͚3f&A͚3f  ͚3f 59͚3f͚3fS͚3f p8͚3f͚3f ͚3fX ͚3f 5,. ͚3fa͚3f͚3fE2͚3f͚3f {@v͚3f=    ͚/3ffni͚3fd͚3fMR͚3f@>͚3f͚3f ͚3fY ͚3f 1)!͚3fc ͚3fA\͚3f"Y͚3f Dg͚3fb ͚3f ͚3fB. ͚3fFU͚3fC9͚3f*L͚3f ͚3f͚3f x?͚3f3f͚3f4: ͚3f-h! ͚3f[͚3fJ4͚3fPn &D͚3f1   q͚3f͚3ff͚3f=m +.A{͚3f^͚3f( ͚3f89 ͚3fw: ͚U3f͚3f8 8@"͚3f͚3fcT !27l͚3f5W͚3f ͚3f` ͚3f3{ -/Fy͚3fb͚3f-U͚3f) 1X͚3f>FE͚3f3f͚3f@7͚3fKT,͚3f-B8͚3f0X͚3fM7 v@   J͚3fL͚3fX͚3f2m(͚3f3O>͚3f:G͚3f' ͚3f;:͚3fhQ ~͚3f͚3fOi͚3f=g ͚3f$x-͚3f 6AZ͚3f4V͚3f͚3f ]͚3f,o/͚3f4N<͚3f;O͚3f/Z͚3f%V͚l3fBIH͚3f3f͚3fG1 ͚3f.͚3f ͚3fZ`͚3f j;8 ͚3fF 3͚3f͚o3fg  oy ͚A3fuXj͚3f5N͚3fjZ͚3f0D3$͚3f-v͚3f-R͚3f"͚3f5?͚3f' ͚3f ͚3f$u͚3f;͚3fUmy͚3f!CI#&͚3fec~͚3f$CM6ı IDAT͚3f#͚3f4K͚3f͚3f b͚3fd^͚~3f*E7(͚3f2x͚3f!`]͚3f ͚R3fL͚3f ͚m3fDKK͚3f 3f ͚3fM*͚3fCy ͚3fEw>͚3f;͚p3f_= ͚w3fp͚53f@ 8%͚3fq͚3fS ͚v3f/T͚3f ͚3fk͚3f q͚3fc q͚3f͚3fn͚3f ͚3f@4 ͚3fWi `͚3f}i͚3f1s͚3f{D͚3feF͚3f"͚W3f7 ͚g3fE͚3f<E͚3ff͚3fq͚3fn q͚3f ͚}3f w'͚3f ͚M3fQ͚3fU͚3f/͚k3fDKL͚3f 3f ͚3fG*͚3fyO͚3f<+3fz͚3f5L:3fr͚͚3f^%/'͚v3fQe͚i͚3f E͚3f]st͚3f#Y3f^͚3fF 3f͚p͚@3f`tE3fp͚3fl'3fP͚͚3f04͚3f}Hw͚3f[$  3fCw͚3fR 3fP͚U3ffm͚3f < e3fT|͚͚3f^I3f͚͚3f 3S3f`͚3ff͚3f F3f͚t͚?3ffv 3f͚k͚3fq ͚3f>2 ͚3fB ͚3f] &3fr͚3f3fu͚͚3fb" 3f73fo͚Z͚3f Hw͚3fDD͚3f@͚3fq o3fbT͚͚3f13fo͚3f`#3f@͚3fC͚3frG͚3f133fiBl͚3f~ A3fZ͚͚3f͚3fY 3fRH͚3fhO3f͚7 70͚3f# Y͚3fp s3faV͚͚3f33f`͚3fn3f 3f'7 ͚3f? c͚3fHY/3f͚͚?3fA͚͚|U3f,3f:͚n͚3f3fw9͚3f ͚H͚3f3f3f͚U4͚23f'U͚3fuS ]͚3fb,3f?W͚3f}3f͚3f=͚X3f͚ 73f J͚3f 3f 3f͚B"3f͚3f ͚3f G ͚3f4X͚3fI3f3f͚W3fC͚k_͚3f+z͚3fP3f[3f͚F)3f3f͚ C͚3fTOs͚3f͚3fNy3f3fkH3f͚ ͚ ͚3fH͚3f2~} IDATm3f͚H͚ ͚R ͚3foGO͚3fU3fY͚3f/͚ ͚}U͚3fR^ ͚͚K͚3f4"3f͚].m͚ 3f)o͚3fN]͚3f5A3f;F3fb#3fR͚3f ͚[͚3fJ)3f3f͚C.3f͚3f 3f/ 3f-Rr͚3f 3f͚͚}3fB3fO͚͚3f ͚3fq'3fj :3f$ 3f'͚3frW3f͚͚3f͚|͚3fK Y3fYt  3f͚3f|~J͚3f͚a3f+ 3f͚3fW;3fE͚͚ ͚5 ͚3f ͚3f  3f͚͚y3fe 3fD%͚3fi ͚3f.Y m͚  !3f#͚3fhs3fZ 3fkW͚  Qo*3f!͚3fX4A͚3f)   (͚3fjz3f͚ 3f.͚j ͚3fE 3fBL-3fY3fX3fY3fY3fX3f7͚(͚3f1͚o͚3f3f 3f͚R ͚3f!}3f P3f!͚t͚3f c͚3f '3f͚ o3f 3f8 3f+Lp3f,j  3f͚3fsb3f}3f͚3f.͚; 3f=+}c& u3f͚3fj33f ͚3f$͚͚3fV   `3f͚Z ͚  Q3f;͚lB3f)͚vv' IDAT͚3f ͚`͚3f :͚3fVEi\Ta3f ͚3fe͚3f  %͚3f0le3f>V S   L͚3f 3f` 3f7E 3f ͚3f3gpU|3f6wt  93f,͚h͚3fH͚  $3fn͚L3f3    3f4͚E"͚3f   tp-͚  3f͚,` ͚  93fTQ 3f]    3f͚3f1͚3f(  3f!   fG2( y?   ͚3f0zQ }͚3f'3fN ͚3f  3f0͚3f3f $ Q 3f    3f1͚3fN6T    $t2   &     Wf/  ͚*3f͚V3f- 3fw͚.V3f͚K yW͚  ͚3f 3f͚3f͚O   [W 3f 3fY     Jv7_   ͚3f     3f 3f5  +3f33fq͚3fM a3faS V3f" T. } 3f  3f 'U IDAT   8 ct ͚3f   9I(+      ͚3fT  t͚͚< ͚3f;b3f͚3f*  ͚J[   )͚3fn 3f ͚3f- S͚3fk 3f;͚3f,     Ob   [h  >͚3f E )8c .3    A͚$j  ͚(͚3f]3f3f͚! 6  J  $͚3f43f3f ͚$  .'   ͚( ͚͚͚  &͚͚͚͚" KT 3f͚I   ͚c3f͚3f 1 X3f'3fvK3f3f͚g/͚= C͚3f 3fJ3f3f ͚a "  ͚N͚3f7͚͚a͚3ftp͚3fd} - ?͚w  83f@v"͚3f9͚ '"5͚f3f63f3f3f93fQe͚3fU͚͚-͚Z-3f͚J  ͚3f ͚8 ͚  ͚3f=^͚zU$3f(x IDAT͚3f;0c͚nW-3f2 3f=3fJ3f ͚}H3f͚͚3f|3fES͚3f͚u (͚͚\͚@͚H͚G͚F͚J #0 3f| ͚3f  3f͚L v͚  ͚(   3f43f- 3f 3f3f 3f͚X3f@  3f͚[ K3f:3f͚3f3f"3f8> 3f͚  3f ͚3f͚B ^3f SX3f    ͚ &  !}͚3fe 3f͚3f  Ou"< e3fK.(5Z j _͚3f  (3f͚vy3f ͚3f3f *y:l͚ /͚Mx> IDAT  (Q3f͚d3f<G͚3f '-Z͚3f͚3fIL   = gv͚3f 3f͚{3fP  ͚3f%3f!3f:c 3f*  3fP͚3f? 3f-? P3f'͚R3fq 3fh#3͚3fO/ ͚3f 3f@͚q" 3f|͚3fdv '  A3f # ͚3f @t4͚3f d͚3fk `l3f͚3fv-([[3f  b  Y͚3f ͚3f3f"͚3f%  46 3f͚3f  33f͚͚3fH͚3fC͚3fR͚3f ͚N 3fq,3f   NI 3fc DDy   M6x 3f!p͚3f :3f͚3fRO3f͚3f͚3f ͚3fKq͚3f    ~͚3f$- >A͚3f  IDAT    n1o}{ YY3f)͚3f|_3f͚3fk!3f$͚  ͚[}~~g͚.413f"    Y͚3fGW3f͚  '3f͚3f-͚ U|    3f] ͚͚͚͚͚3f]3f3f͚͚     ͚b͚͚Q͚B  J3f{͚ 3f ͚͚    ͚m͚3f3f.͚3f    3f1g͚͚3f ?A3f͚>    b3fJ-~wz 3f͚^3fV͚3ff33f  :3f5s7P    ͚3f0-͚͚    \͚3f|͚3f͚{  3fC͚N͚*   \͚3f͚3fO͚   ͚{͚͚͚3f3f.͚3fG 3fr͚p      ͚͚<  IDAT͚p73f<[ 3f͚b03fu͚  ͚zG"3fLz     3f3f3f,3f73f ͚oh3f6͚6    ͚N3fX?3f  ͚3f͚   3f ͚X R3f9  ͚J3fkAm͚3f@3f͚t3f͚q   @E4͚]͚3f"  @3fNx͚3fz  3f ͚3f͚*    ͚     ͚_3fOf͚3ftD͚y  ͚3f3fA͚3f͚ ͚L͚3f8~͚O  3fr3f͚$   .3f    d͚3fD͚O,3f͚V3f͚53f9 ͚&3f͚3fMF3f͚͚p3f3fC͚)3fM  {͚3f<͚3f=3 o3f͚3fT3f@͚3f4 |  ͚3fu͚3fG/s3f͚3f;͚: 3f3f ͚#͚a3f_3f ͚N; !͚3fe͚3f6B  16j IDATn3f͚Y3f͚7M  s͚V   6͚K͚3f 3fa͚3fB:@͚3fA͚WM 3f 3f͚t3f͚W3fb͚  X͚!͚3f3fm͚3f1I  x3f͚X3fZ͚   ͚͚3f<3f  ͚3f F͚3f9>͚3fLG3f͚͚{3f>3f{͚3fQu$͚z3f>#͚3f3f͚͚3fWi3f͚j3f<n3fJl͚3fwc  }͚3f6͚3f0RN 3f͚V3f0v3fCz͚3fli ͚3fpXj͚3f ͚3f *͚3fidf͚3fK5͚͚3f7[3f͚͚}3fpO~q3fS͚͚3f͚! ͚3f%3f͚Yk3f͚͚w3fJv3fo͚͚3f)M?)͚3f' a͚3fD<͚3fP ͚3f8y3f͚͚m3fVj)3fe͚͚3fP@1͚3f 8͚3fr ͚3f3f,@,%͚3fd͚3f#͚͚3f8Q3f ͚͚3fK3f͚3f -3fj0,3fy͚͚3f8͚3f)3f͚3fN(p3f͚͚3foA33f͚3f.R6͚3fZ%͚n͚M3f͚a͚3fP͚3f?]q3f͚͚3fp@,3f͚3f*E5-͚3ff-3f?͚Z͚3fD3f ͚3f#Y͚3f 3f͚͚3f /3f1XN[3f P3f, 3U3fj͚3f#3f͚͚3f'M3f͚z3f)c.%w3f ##4͚3fZ͚3f-3f 63f@3f;` IDAT͚3fU!O3f͚u3f&h) n{3f?͚3f=J͚3f!3fC3f͚3f h͚3f% %3f͚5,r͚3f 3f ͚͚3f$3f͚͚3f87#}3f͚͚t3fLU36͚3fQ/͚͚>3f͚K3f͚h3fg|͚}͚3f:4.73f͚͚r3f?W'+1͚3f!^͚͚3fAQ5~͚R͚3fB-͚3f+"͚y3f C͚3fV   ͚3f ͚N3f0U͚3f'3f͚͚3fNU3f͚͚3f ͚3fF͚3f'͚3f͚͚C|3f͚͚QEA3f8N3f͚͚3fOV3f͚͚3f ͚3fK. ͚3f͚M3f͚͚C3fH<͚͚3f3f!͚3f #͚3f,3fI2͚3f7xP   <3f 5 F͚͚͚3f3f ͚͚3f21:3fE͚3f 3f '͚3f&m3f{Ln3f P͚3f*3f͚͚3f3/5>3f͚3f 3f &͚3f͚3f8L3f 9͚3f7?͚3f͚}3f )D  x;E͚3f ͚͚3f 3f4͚͚3f%=3f͚3f#7͚3fm F͚3fXJ͚3f3f͚͚3f"<3f͚3f(3f͚͚3f47͚ ͚͚3f#͚LO  y͚3f*5͚3f͚͚3f&3f͚͚3f # ͚3f3f8͚͚3f ͚3f  +3f͚͚3f# ͚3f(3f͚ܺh IDAT͚3f͚3f' ͚3f3f ͚3f͚  ͚]͚3f ͚3f͚͚3f-3f͚3f͚3f3f6͚͚͚3f͚3f,3f͚3f  3f͚͚3f% ͚3fG͚  e3fT+3fT͚͚3f !3f ͚͚3f3f6  ͚͚3f 3f  ͚3f3f ͚U|͚ ͚͚S3f͚͚3f͚͚3f͚͚3f͚͚3f͚͚3f͚    p3f`q3f;͚͚͚3f͚͚͚3f͚͚͚3f  ͚3f)y͚3fE3f ͚3f͚͚3f͚3f͚͚3fk IDAT͚3f͚    &͚3f͚d͚3f͚͚3f͚͚3f͚3f͚3f͚͚3f͚3f͚   '͚3f,͚3f ͚͚͚͚͚͚͚͚  6͚3f X͚3f^͚͚ ͚N3f^͚3fk͚3f&͚  4͚3f$Y͚s3f C IDAT   ͚3fatd͚3f r͚3f *͚/ y͚3f͚u3f s͚3f)͚ 3f͚@3f M.'%#͚3f ͚3f t͚3f.͚<+3f͚3f4E:%͚3f7M͚3f,͚c3fIj!M[3f('2͚3f P$͚3f,͚4U\o3f͚Y3fbB!(Y IDAT͚3fD<͚3f-͚ 3f ͚3fH͚͚͚'j3f͚͚3f ͚3fA"͚3f͚͚3f23f͚3fG4 3f͚3fW!l3f͚3f 3f$*͚3f͚͚3fC.3f<͚͚+3f͚3fJ%3;3f͚3f )͚3f͚3f'= ͚3f% ͚3f ͚3f  ͚3f͚3f͚3f͚3f&͚3f ͚3f͚8 IDAT3f͚3f"͚3f   ͚3f ͚3f͚3f͚3f͚3f͚3f͚fP IDAT3f͚3f͚aIENDB`pyformex-0.8.6/pyformex/doc/html/_static/README0000644000211500021150000000221111666766507021142 0ustar benebene00000000000000# $Id: README 2114 2011-12-04 21:59:35Z bverheg $ ## ## The files in this directory are part of the pyFormex project. ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # pyformex-0.8.6/pyformex/doc/html/_static/scripts/0000755000211500021150000000000011705105304021725 5ustar benebene00000000000000pyformex-0.8.6/pyformex/doc/html/_static/scripts/WireStent.py0000644000211500021150000001202011705104656024227 0ustar benebene00000000000000# $Id: WireStent.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Wirestent.py A pyFormex script to generate a geometrical model of a wire stent. This version is for inclusion in the pyFormex documentation. """ from formex import * class DoubleHelixStent: """Constructs a double helix wire stent. A stent is a tubular shape such as used for opening obstructed blood vessels. This stent is made frome sets of wires spiraling in two directions. The geometry is defined by the following parameters: L : approximate length of the stent De : external diameter of the stent D : average stent diameter d : wire diameter be : pitch angle (degrees) p : pitch nx : number of wires in one spiral set ny : number of modules in axial direction ds : extra distance between the wires (default is 0.0 for touching wires) dz : maximal distance of wire center to average cilinder nb : number of elements in a strut (a part of a wire between two crossings), default 4 The stent is created around the z-axis. By default, there will be connectors between the wires at each crossing. They can be switched off in the constructor. The returned formex has one set of wires with property 1, the other with property 3. The connectors have property 2. The wire set with property 1 is winding positively around the z-axis. """ def __init__(self,De,L,d,nx,be,ds=0.0,nb=4,connectors=True): """Create the Wire Stent.""" D = De - 2*d - ds r = 0.5*D dz = 0.5*(ds+d) p = math.pi*D*tand(be) nx = int(nx) ny = int(round(nx*L/p)) # The actual length may differ a bit from L # a single bumped strut, oriented along the x-axis bump_z=lambda x: 1.-(x/nb)**2 base = Formex(pattern('1')).replic(nb,1.0).bump1(2,[0.,0.,dz],bump_z,0) # scale back to size 1. base = base.scale([1./nb,1./nb,1.]) # NE and SE directed struts NE = base.shear(1,0,1.) SE = base.reflect(2).shear(1,0,-1.) NE.setProp(1) SE.setProp(3) # a unit cell of crossing struts cell1 = (NE+SE).rosette(2,180) # add a connector between first points of NE and SE if connectors: cell1 += Formex([[NE[0][0],SE[0][0]]],2) # and create its mirror cell2 = cell1.reflect(2) # and move both to appropriate place self.cell1 = cell1.translate([1.,1.,0.]) self.cell2 = cell2.translate([-1.,-1.,0.]) # the base pattern cell1+cell2 now has size [-2,-2]..[2,2] # Create the full pattern by replication dx = 4. dy = 4. F = (self.cell1+self.cell2).replic2(nx,ny,dx,dy) # fold it into a cylinder self.F = F.translate([0.,0.,r]).cylindrical( dir=[2,0,1],scale=[1.,360./(nx*dx),p/nx/dy]) self.ny = ny def all(self): """Return the Formex with all bar elements.""" return self.F if __name__ == "draw": # show an example wireframe() reset() D = 10. L = 80. d = 0.2 n = 12 b = 30. res = askItems([['Diameter',D], ['Length',L], ['WireDiam',d], ['NWires',n], ['Pitch',b]]) if not res: exit() D = float(res['Diameter']) L = float(res['Length']) d = float(res['WireDiam']) n = int(res['NWires']) if (n % 2) != 0: warning('Number of wires must be even!') exit() b = float(res['Pitch']) H = DoubleHelixStent(D,L,d,n,b).all() clear() draw(H,view='iso') # and save it in a lot of graphics formats if ack("Do you want to save this image (in lots of formats) ?"): for ext in [ 'bmp', 'jpg', 'pbm', 'png', 'ppm', 'xbm', 'xpm', 'eps', 'ps', 'pdf', 'tex' ]: image.save('WireStent.'+ext) # End pyformex-0.8.6/pyformex/doc/html/_static/scripts/README0000644000211500021150000000221111666766507022631 0ustar benebene00000000000000# $Id: README 2114 2011-12-04 21:59:35Z bverheg $ ## ## The files in this directory are part of the pyFormex project. ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # pyformex-0.8.6/pyformex/doc/html/_static/scripts/example1.py0000644000211500021150000000022511702526022024013 0ustar benebene00000000000000# $Id: example1.py 2146 2012-01-09 08:57:52Z bverheg $ *** pyformex *** """Example 1""" F = Formex([[[0.,0.],[1.,0.]],[[1.,1.],[0.,1.]]]) # End pyformex-0.8.6/pyformex/doc/html/_static/scripts/SpiralDeclared.py0000644000211500021150000000652711705104656025200 0ustar benebene00000000000000# $Id: SpiralDeclared.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Spiral""" #This is a small function that is only defined in this script. It clears the #screen and draws the Formex at the same time. def drawit(F,view='front'): clear() draw(F,view) #These are the parameters. They can easily be changed, and a whole new #spiral will be created without any extra effort. m = 36 # number of cells along torus big circle n = 10 # number of cells along torus small circle #The first step is to create a basic Formex. In this case, it's a triangle which #has a different property number for every edge. F = Formex(pattern("164"),[1,2,3]); drawit(F) #This basic Formex is copied 'm' times in the 0-direction with a translation #step of '1' (the length of an edge of the triangle). After that, the new #Formex is copied 'n' times in the 1-direction with a translation step of '1'. #Because of the recursive definition (F=F.replic), the original Formex F is #overwritten by the transformed one. F = F.replic(m,1,0); drawit(F) F = F.replic(n,1,1); drawit(F) #Now a copy of this last Formex is translated in direction '2' with a #translation step of '1'. This necessary for the transformation into a cilinder. #The result of all previous steps is a rectangular pattern with the desired #dimensions, in a plane z=1. F = F.translate1(2,1); drawit(F,'iso') #This pattern is rolled up into a cilinder around the 2-axis. F = F.cylindrical([2,1,0],[1.,360./n,1.]); drawit(F,'iso') #This cilinder is copied 5 times in the 2-direction with a translation step of #'m' (the lenght of the cilinder). F = F.replic(5,m,2); drawit(F,'iso') #The next step is to rotate this cilinder -10 degrees around the 0-axis. #This will determine the pitch angle of the spiral. F = F.rotate(-10,0); drawit(F,'iso') #A copy of this last formex is now translated in direction '0' with a #translation step of '5'. F = F.translate1(0,5); drawit(F,'iso') #Finally, the Formex is rolled up, but around a different axis then before. #Due to the pitch angle, a spiral is created. If the pitch angle would be 0 #(no rotation of -10 degrees around the 0-axis), the resulting Formex #would be a torus. F = F.cylindrical([0,2,1],[1.,360./m,1.]); drawit(F,'iso') drawit(F,'right') pyformex-0.8.6/pyformex/doc/html/_static/scripts/template.py0000644000211500021150000000241511700416072024116 0ustar benebene00000000000000# *** pyformex *** ## ## Copyright (C) 2011 John Doe (j.doe@somewhere.org) ## Distributed under the GNU General Public License version 3 or later. ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Script Template This is a template file to show the general layout of a pyFormex script. In the current version, a pyFormex script should obey the following rules: - file name extension is '.py' - first (comment) line contains 'pyformex' The script starts by preference with a docstring (like this), composed of a short first line, then a blank line and one or more lines explaining the intention of the script. """ print "This is the pyFormex template script" # End pyformex-0.8.6/pyformex/doc/html/_static/scripts/WireStentParametricExample.py0000644000211500021150000000272411705104656027565 0ustar benebene00000000000000# $Id: WireStentParametricExample.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## from examples.WireStent import DoubleHelixStent for De in [16.,32.]: for nx in [6,10]: for beta in [25,50]: stent = DoubleHelixStent(De,40.,0.22,nx,beta).all() draw(stent,view='iso') pause() clear() pyformex-0.8.6/pyformex/doc/html/_static/scripts/python_intro.py0000644000211500021150000000107411702367556025055 0ustar benebene00000000000000#!/usr/bin/env python """Python intro A short introduction to some aspects of the Python programming language """ for light in [ 'green','yellow','red','black',None]: if light == 'red': print 'stop' elif light == 'yellow': print 'brake' elif light == 'green': print 'drive' else: print 'THE LIGHT IS BROKEN!' appreciation = { 0: 'not driving', 30:'slow', 60:'normal', 90:'dangerous', 120:'suicidal'} for i in range(5): speed = 30*i print "%s. Driving at speed %s is %s" % (i,speed,appreciation[speed]) # End pyformex-0.8.6/pyformex/doc/html/_static/scripts/WireStent_Demo.py0000644000211500021150000002120511705104656025200 0ustar benebene00000000000000# $Id: WireStent_Demo.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** # $Id: WireStent_Demo.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Wire Stent Demo""" ## ##This Demo is intended for educational purposes by rewriting the WireStent.py ##example and adding lots of drawing instructions and comments. More details ##regarding the used definitions can be found in the Pyformex reference manual. ### # needed if we import this from another script from formex import * class DoubleHelixStent: """Constructs a double helix wire stent. A stent is a tubular shape such as used for opening obstructed blood vessels. This stent is made frome sets of wires spiraling in two directions. The geometry is defined by the following parameters: L : length of the stent De : external diameter of the stent D : average stent diameter d : wire diameter be : pitch angle (degrees) p : pitch nx : number of wires in one spiral set ny : number of modules in axial direction ds : extra distance between the wires (default is 0.0 for touching wires) dz : maximal distance of wire center to average cilinder nb : number of elements in a strut (a part of a wire between two crossings), default 4 The stent is created around the z-axis. By default, there will be connectors between the wires at each crossing. They can be switched off in the constructor. The returned formex has one set of wires with property 1, the other with property 3. The connectors have property 2. The wire set with property 1 is winding positively around the z-axis. """ def __init__(self,De,L,d,nx,be,ds=0.0,nb=4,connectors=True): """Create the Wire Stent.""" D = De - 2*d - ds r = 0.5*D dz = 0.5*(ds+d) p = math.pi*D*tand(be) nx = int(nx) ny = int(round(nx*L/p)) # The actual length may differ a bit from L # a single bumped strut, oriented along the x-axis bump_z=lambda x: 1.-(x/nb)**2 A=Formex(pattern('1'),3) GD.message("\nThis Demo is intended for educational purposes by rewriting the WireStent.py \n example and adding lots of drawing instructions and comments. More details \n regarding the used definitions can be found in the Pyformex reference manual.") GD.message("\nStep 1: Create a Formex: a line of length 1 (with property 3) oriented along the X-axis\n A = Formex(pattern('1'),3)") draw(A,view='bottom') pause() B=Formex(A.replic(nb,1.0),1) GD.message("Step 2: Copy the Formex nb times in the X(0)-direction\n B = Formex(A.replic(nb,1.0),1)") draw(B,view='last') pause() clear() base = Formex(B.bump1(2,[0.,0.,dz],bump_z,0),1) GD.message("Step 3: Create a bump in the Z(2)-direction\n base = Formex(B.bump1(2,[0.,0.,dz],bump_z,0),1)") draw(base,view='last') pause() clear() # scale back to size 1. base = base.scale([1./nb,1./nb,1.]) GD.message("Step 4: Rescale the base line to size 1\n base = base.scale([1./nb,1./nb,1.])") draw(base,view='last') pause() clear() # NE and SE directed struts NE = base.shear(1,0,1.) NE.setProp(1) GD.message("Step 5: Skew the base line to NE-direction\n NE = base.shear(1,0,1.)") ## The nxt two lines serve to rotate the camera up over 30°, i.e. 6 times the rotUp definition from cameraMenu GD.canvas.camera.rotate(30,1,0,0) GD.canvas.update() draw(NE) pause() clear() SE = base.reflect(2).shear(1,0,-1.) SE.setProp(3) GD.message("Step 6: Create a mirrored base line and skew it to SE-direction\n SE = base.reflect(2).shear(1,0,-1.)") draw(SE,view='last') pause() clear() cell=(NE+SE) GD.message("Step 7: Create the base cell by combining the NE and SE formices\n cell = (NE+SE)") draw(cell,view='last') pause() clear() # a unit cell of crossing struts cell1 = (cell).rosette(2,180) GD.message("Step 8: Create the base module (cell1) of two crossing wires by replicating the base cell by an angular rotation\n cell1 = (cell).rosette(2,180)") draw(cell1,view='last') pause() clear() # add a connector between first points of NE and SE if connectors: cell1 += Formex([[NE[0][0],SE[0][0]]],2) GD.message("Step 9: Add a connector between the first points of NE and SE of the base module\n cell1 += Formex([[NE[0][0],SE[0][0]]],2)") draw(cell1,view='last') pause() clear() # and create its mirror cell2 = cell1.reflect(2) GD.message("Step 10: Create a mirror in Z(2)-direction of the base module\n cell2 = cell1.reflect(2)") draw(cell2,view='last') pause() clear() # and move both to appropriate place self.cell1 = cell1.translate([1.,1.,0.]) self.cell2 = cell2.translate([-1.,-1.,0.]) # the base pattern cell1+cell2 now has size [-2,-2]..[2,2] # Create the full pattern by replication dx = 4. dy = 4. module=(self.cell1+self.cell2) GD.message("Step 11: Extend the base module with its mirrored and translated copy\n module = (self.cell1+self.cell2)") draw(module,view='last') pause() clear() F = module.replic2(nx,ny,dx,dy) GD.message("Step 12: Replicate the base module in both directions of the base plane\n F = module.replic2(nx,ny,dx,dy)") draw(F,view='last') pause() clear() # fold it into a cylinder C=F.translate([0.,0.,r]) GD.message("Step 13: Translate the full patern over the stent radius in Z-direction\n C=F.translate([0.,0.,r])") draw(C,view='last') pause() clear() self.F = C.cylindrical(dir=[2,0,1],scale=[1.,360./(nx*dx),p/nx/dy]) GD.message("Step 14: Roll the nearly planar grid into a cylinder\n self.F = C.cylindrical(dir=[2,0,1],scale=[1.,360./(nx*dx),p/nx/dy])") draw(self.F,view='front') pause() clear() draw(self.F,view='left') pause() clear() self.ny = ny def all(self): """Return the Formex with all bar elements.""" return self.F if __name__ == "draw": # show an example ## The following default values are obtained from Jedwab and Clerc (except for L=87.5 and b-30.85) D = 16.71 L = 40. d = 0.22 n = 12 b = 40 res = askItems([['Diameter',D],['Length',L],['WireDiam',d],['NWires',n], ['Pitch',b]]) D = float(res['Diameter']) L = float(res['Length']) d = float(res['WireDiam']) n = int(res['NWires']) ####### The following 3 lines are commented by MDB as n seems to be the number of wires in one direction and thus, this number may be uneven!!! ####### ## if (n % 2) != 0: ## warning('Number of wires must be even!') ## exit() b = float(res['Pitch']) H = DoubleHelixStent(D,L,d,n,b).all() # clear() draw(H,view='iso') # and save it in a lot of graphics formats ## if ack("Do you want to save this image (in lots of formats) ?"): ## for ext in [ 'bmp', 'jpg', 'pbm', 'png', 'ppm', 'xbm', 'xpm', 'eps', 'ps', 'pdf', 'tex' ]: ## saveImage('WireStent.'+ext) pyformex-0.8.6/pyformex/doc/html/_static/scripts/Helix.py0000644000211500021150000000327411705104656023367 0ustar benebene00000000000000# $Id: Helix.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Helix example from pyFormex""" m = 36 # number of cells along helix n = 10 # number of cells along circular cross section reset() setDrawOptions({'clear':True}) F = Formex(pattern("l:164"),[1,2,3]); draw(F) F = F.replic(m,1.,0); draw(F) F = F.replic(n,1.,1); draw(F) F = F.translate(2,1.); draw(F,view='iso') F = F.cylindrical([2,1,0],[1.,360./n,1.]); draw(F) F = F.replic(5,m*1.,2); draw(F) F = F.rotate(-10.,0); draw(F) F = F.translate(0,5.); draw(F) F = F.cylindrical([0,2,1],[1.,360./m,1.]); draw(F) draw(F,view='right') pyformex-0.8.6/pyformex/doc/html/_static/scallop_dome_small.png0000644000211500021150000000521511503465564024615 0ustar benebene00000000000000PNG  IHDRdd[bKGD pHYs+tIME 12Yi IDATx]#+ k+wUHb; L[F:{1au;9istqSu H`_; ;fX!1 |`dpN(! aY㠲wtXr'1~{l7/Ɵ 0z*05c, 0V#kXeFpd30l˝oxGfwe9"[)wC=vj %;/&Gfދ+1`xyvͪxM[hV%ŲfLi=iHF&!v:B5,#5¨9޳ƈy"1o~dCUY1ka:ki(Dz sG-ӱ=Y! ;=^3Xmb+_ bkfD٘9np-҉Be {YExJGjԢį Zo4Go‘2 3h' 1-=11ޔw-vVԉlVbuN ^i2k%WovUc!"=f/;FKD !>>1:Fz,p tR_ :WYq飰Vǭf}񾞟( RQɺ9{X.cUEl/ѭE2+0+Dsdq͢Y™ftf @,SJCTzff֒VYttLEY2ys3`)ͣLw\6ia h2=EG)d)~ּd3ER'.9[=$CDl*Ŕ09~h7YYcuPvh[(հz:e,ό1Ҍuo3vt"1v^dlA뼣fhZن Ug֒0>Z2)=#ҳB] *ذnϒVdG67!2F ̍N˦'ٺ(ߊW:HnRnʺvf:NbƄU=p=VKVɢ"_Vʴq;tvf+[PkxlqV1tNcUY'0a ٲj(>VuƷR #)-kfP FƖ)8c"vRWfID2jѯ T#eHgiLmTGobCҋ+[dZfYRQ#[A^fdcэh % s]^xM:63є^]s -eFKuGRM?մCh?[,U:|ng #[0Y\foeIL]aUݒY4Z}X:UQe^tm,~Z^!:N]rX@V1Q=Q;4VmmD20Ud3 *hLRdXtl3yVhLt{Yu3SY!=.  & afs]V8:q?KW0JIENDB`pyformex-0.8.6/pyformex/doc/html/_static/pygments.css0000644000211500021150000000753411503465564022644 0ustar benebene00000000000000.highlight .hll { background-color: #ffffcc } .highlight { background: #eeffcc; } .highlight .c { color: #408090; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #007020; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #007020 } /* Comment.Preproc */ .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #303030 } /* Generic.Output */ .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0040D0 } /* Generic.Traceback */ .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #007020 } /* Keyword.Pseudo */ .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #902000 } /* Keyword.Type */ .highlight .m { color: #208050 } /* Literal.Number */ .highlight .s { color: #4070a0 } /* Literal.String */ .highlight .na { color: #4070a0 } /* Name.Attribute */ .highlight .nb { color: #007020 } /* Name.Builtin */ .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ .highlight .no { color: #60add5 } /* Name.Constant */ .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #007020 } /* Name.Exception */ .highlight .nf { color: #06287e } /* Name.Function */ .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #bb60d5 } /* Name.Variable */ .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #208050 } /* Literal.Number.Float */ .highlight .mh { color: #208050 } /* Literal.Number.Hex */ .highlight .mi { color: #208050 } /* Literal.Number.Integer */ .highlight .mo { color: #208050 } /* Literal.Number.Oct */ .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ .highlight .sc { color: #4070a0 } /* Literal.String.Char */ .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ .highlight .sx { color: #c65d09 } /* Literal.String.Other */ .highlight .sr { color: #235388 } /* Literal.String.Regex */ .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ .highlight .ss { color: #517918 } /* Literal.String.Symbol */ .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */pyformex-0.8.6/pyformex/doc/html/_static/file.png0000644000211500021150000000061011503465564021675 0ustar benebene00000000000000PNG  IHDRabKGD pHYs  tIME  )TIDAT8˭J@Ir('[ "&xYZ X0!i|_@tD] #xjv YNaEi(əy@D&`6PZk$)5%"z.NA#Aba`Vs_3c,2mj [klvy|!Iմy;v "߮a?A7`c^nk?Bg}TЙD# "RD1yER*6MJ3K_Ut8F~IENDB`pyformex-0.8.6/pyformex/doc/html/_static/jquery.js0000644000211500021150000074465311662602303022141 0ustar benebene00000000000000/*! * jQuery JavaScript Library v1.7.1 * http://jquery.com/ * * Copyright 2011, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * Includes Sizzle.js * http://sizzlejs.com/ * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * * Date: Mon Nov 21 21:11:03 2011 -0500 */ (function( window, undefined ) { // Use the correct document accordingly with window argument (sandbox) var document = window.document, navigator = window.navigator, location = window.location; var jQuery = (function() { // Define a local copy of jQuery var jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context, rootjQuery ); }, // Map over jQuery in case of overwrite _jQuery = window.jQuery, // Map over the $ in case of overwrite _$ = window.$, // A central reference to the root jQuery(document) rootjQuery, // A simple way to check for HTML strings or ID strings // Prioritize #id over to avoid XSS via location.hash (#9521) quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, // Check if a string has a non-whitespace character in it rnotwhite = /\S/, // Used for trimming whitespace trimLeft = /^\s+/, trimRight = /\s+$/, // Match a standalone tag rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, // JSON RegExp rvalidchars = /^[\],:{}\s]*$/, rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, // Useragent RegExp rwebkit = /(webkit)[ \/]([\w.]+)/, ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, rmsie = /(msie) ([\w.]+)/, rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, // Matches dashed string for camelizing rdashAlpha = /-([a-z]|[0-9])/ig, rmsPrefix = /^-ms-/, // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { return ( letter + "" ).toUpperCase(); }, // Keep a UserAgent string for use with jQuery.browser userAgent = navigator.userAgent, // For matching the engine and version of the browser browserMatch, // The deferred used on DOM ready readyList, // The ready event handler DOMContentLoaded, // Save a reference to some core methods toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty, push = Array.prototype.push, slice = Array.prototype.slice, trim = String.prototype.trim, indexOf = Array.prototype.indexOf, // [[Class]] -> type pairs class2type = {}; jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ) { var match, elem, ret, doc; // Handle $(""), $(null), or $(undefined) if ( !selector ) { return this; } // Handle $(DOMElement) if ( selector.nodeType ) { this.context = this[0] = selector; this.length = 1; return this; } // The body element only exists once, optimize finding it if ( selector === "body" && !context && document.body ) { this.context = document; this[0] = document.body; this.selector = selector; this.length = 1; return this; } // Handle HTML strings if ( typeof selector === "string" ) { // Are we dealing with HTML string or an ID? if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = quickExpr.exec( selector ); } // Verify a match, and that no context was specified for #id if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; doc = ( context ? context.ownerDocument || context : document ); // If a single string is passed in and it's a single tag // just do a createElement and skip the rest ret = rsingleTag.exec( selector ); if ( ret ) { if ( jQuery.isPlainObject( context ) ) { selector = [ document.createElement( ret[1] ) ]; jQuery.fn.attr.call( selector, context, true ); } else { selector = [ doc.createElement( ret[1] ) ]; } } else { ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; } return jQuery.merge( this, selector ); // HANDLE: $("#id") } else { elem = document.getElementById( match[2] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } // Otherwise, we inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || rootjQuery ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray( selector, this ); }, // Start with an empty selector selector: "", // The current version of jQuery being used jquery: "1.7.1", // The default length of a jQuery object is 0 length: 0, // The number of elements contained in the matched element set size: function() { return this.length; }, toArray: function() { return slice.call( this, 0 ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { return num == null ? // Return a 'clean' array this.toArray() : // Return just the object ( num < 0 ? this[ this.length + num ] : this[ num ] ); }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems, name, selector ) { // Build a new jQuery matched element set var ret = this.constructor(); if ( jQuery.isArray( elems ) ) { push.apply( ret, elems ); } else { jQuery.merge( ret, elems ); } // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; if ( name === "find" ) { ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; } else if ( name ) { ret.selector = this.selector + "." + name + "(" + selector + ")"; } // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); }, ready: function( fn ) { // Attach the listeners jQuery.bindReady(); // Add the callback readyList.add( fn ); return this; }, eq: function( i ) { i = +i; return i === -1 ? this.slice( i ) : this.slice( i, i + 1 ); }, first: function() { return this.eq( 0 ); }, last: function() { return this.eq( -1 ); }, slice: function() { return this.pushStack( slice.apply( this, arguments ), "slice", slice.call(arguments).join(",") ); }, map: function( callback ) { return this.pushStack( jQuery.map(this, function( elem, i ) { return callback.call( elem, i, elem ); })); }, end: function() { return this.prevObject || this.constructor(null); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: push, sort: [].sort, splice: [].splice }; // Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn; jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // extend jQuery itself if only one argument is passed if ( length === i ) { target = this; --i; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.extend({ noConflict: function( deep ) { if ( window.$ === jQuery ) { window.$ = _$; } if ( deep && window.jQuery === jQuery ) { window.jQuery = _jQuery; } return jQuery; }, // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, // Hold (or release) the ready event holdReady: function( hold ) { if ( hold ) { jQuery.readyWait++; } else { jQuery.ready( true ); } }, // Handle when the DOM is ready ready: function( wait ) { // Either a released hold or an DOMready/load event and not yet ready if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if ( !document.body ) { return setTimeout( jQuery.ready, 1 ); } // Remember that the DOM is ready jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be if ( wait !== true && --jQuery.readyWait > 0 ) { return; } // If there are functions bound, to execute readyList.fireWith( document, [ jQuery ] ); // Trigger any bound ready events if ( jQuery.fn.trigger ) { jQuery( document ).trigger( "ready" ).off( "ready" ); } } }, bindReady: function() { if ( readyList ) { return; } readyList = jQuery.Callbacks( "once memory" ); // Catch cases where $(document).ready() is called after the // browser event has already occurred. if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready return setTimeout( jQuery.ready, 1 ); } // Mozilla, Opera and webkit nightlies currently support this event if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", jQuery.ready, false ); // If IE event model is used } else if ( document.attachEvent ) { // ensure firing before onload, // maybe late but safe also for iframes document.attachEvent( "onreadystatechange", DOMContentLoaded ); // A fallback to window.onload, that will always work window.attachEvent( "onload", jQuery.ready ); // If IE and not a frame // continually check to see if the document is ready var toplevel = false; try { toplevel = window.frameElement == null; } catch(e) {} if ( document.documentElement.doScroll && toplevel ) { doScrollCheck(); } } }, // See test/unit/core.js for details concerning isFunction. // Since version 1.3, DOM methods and functions like alert // aren't supported. They return false on IE (#2968). isFunction: function( obj ) { return jQuery.type(obj) === "function"; }, isArray: Array.isArray || function( obj ) { return jQuery.type(obj) === "array"; }, // A crude way of determining if an object is a window isWindow: function( obj ) { return obj && typeof obj === "object" && "setInterval" in obj; }, isNumeric: function( obj ) { return !isNaN( parseFloat(obj) ) && isFinite( obj ); }, type: function( obj ) { return obj == null ? String( obj ) : class2type[ toString.call(obj) ] || "object"; }, isPlainObject: function( obj ) { // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } try { // Not own constructor property must be Object if ( obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { return false; } } catch ( e ) { // IE8,9 Will throw exceptions on certain host objects #9897 return false; } // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. var key; for ( key in obj ) {} return key === undefined || hasOwn.call( obj, key ); }, isEmptyObject: function( obj ) { for ( var name in obj ) { return false; } return true; }, error: function( msg ) { throw new Error( msg ); }, parseJSON: function( data ) { if ( typeof data !== "string" || !data ) { return null; } // Make sure leading/trailing whitespace is removed (IE can't handle it) data = jQuery.trim( data ); // Attempt to parse using the native JSON parser first if ( window.JSON && window.JSON.parse ) { return window.JSON.parse( data ); } // Make sure the incoming data is actual JSON // Logic borrowed from http://json.org/json2.js if ( rvalidchars.test( data.replace( rvalidescape, "@" ) .replace( rvalidtokens, "]" ) .replace( rvalidbraces, "")) ) { return ( new Function( "return " + data ) )(); } jQuery.error( "Invalid JSON: " + data ); }, // Cross-browser xml parsing parseXML: function( data ) { var xml, tmp; try { if ( window.DOMParser ) { // Standard tmp = new DOMParser(); xml = tmp.parseFromString( data , "text/xml" ); } else { // IE xml = new ActiveXObject( "Microsoft.XMLDOM" ); xml.async = "false"; xml.loadXML( data ); } } catch( e ) { xml = undefined; } if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { jQuery.error( "Invalid XML: " + data ); } return xml; }, noop: function() {}, // Evaluates a script in a global context // Workarounds based on findings by Jim Driscoll // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context globalEval: function( data ) { if ( data && rnotwhite.test( data ) ) { // We use execScript on Internet Explorer // We use an anonymous function so that context is window // rather than jQuery in Firefox ( window.execScript || function( data ) { window[ "eval" ].call( window, data ); } )( data ); } }, // Convert dashed to camelCase; used by the css and data modules // Microsoft forgot to hump their vendor prefix (#9572) camelCase: function( string ) { return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); }, nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); }, // args is for internal usage only each: function( object, callback, args ) { var name, i = 0, length = object.length, isObj = length === undefined || jQuery.isFunction( object ); if ( args ) { if ( isObj ) { for ( name in object ) { if ( callback.apply( object[ name ], args ) === false ) { break; } } } else { for ( ; i < length; ) { if ( callback.apply( object[ i++ ], args ) === false ) { break; } } } // A special, fast, case for the most common use of each } else { if ( isObj ) { for ( name in object ) { if ( callback.call( object[ name ], name, object[ name ] ) === false ) { break; } } } else { for ( ; i < length; ) { if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { break; } } } } return object; }, // Use native String.trim function wherever possible trim: trim ? function( text ) { return text == null ? "" : trim.call( text ); } : // Otherwise use our own trimming functionality function( text ) { return text == null ? "" : text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); }, // results is for internal usage only makeArray: function( array, results ) { var ret = results || []; if ( array != null ) { // The window, strings (and functions) also have 'length' // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 var type = jQuery.type( array ); if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { push.call( ret, array ); } else { jQuery.merge( ret, array ); } } return ret; }, inArray: function( elem, array, i ) { var len; if ( array ) { if ( indexOf ) { return indexOf.call( array, elem, i ); } len = array.length; i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; for ( ; i < len; i++ ) { // Skip accessing in sparse arrays if ( i in array && array[ i ] === elem ) { return i; } } } return -1; }, merge: function( first, second ) { var i = first.length, j = 0; if ( typeof second.length === "number" ) { for ( var l = second.length; j < l; j++ ) { first[ i++ ] = second[ j ]; } } else { while ( second[j] !== undefined ) { first[ i++ ] = second[ j++ ]; } } first.length = i; return first; }, grep: function( elems, callback, inv ) { var ret = [], retVal; inv = !!inv; // Go through the array, only saving the items // that pass the validator function for ( var i = 0, length = elems.length; i < length; i++ ) { retVal = !!callback( elems[ i ], i ); if ( inv !== retVal ) { ret.push( elems[ i ] ); } } return ret; }, // arg is for internal usage only map: function( elems, callback, arg ) { var value, key, ret = [], i = 0, length = elems.length, // jquery objects are treated as arrays isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; // Go through the array, translating each of the items to their if ( isArray ) { for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } } // Go through every key on the object, } else { for ( key in elems ) { value = callback( elems[ key ], key, arg ); if ( value != null ) { ret[ ret.length ] = value; } } } // Flatten any nested arrays return ret.concat.apply( [], ret ); }, // A global GUID counter for objects guid: 1, // Bind a function to a context, optionally partially applying any // arguments. proxy: function( fn, context ) { if ( typeof context === "string" ) { var tmp = fn[ context ]; context = fn; fn = tmp; } // Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if ( !jQuery.isFunction( fn ) ) { return undefined; } // Simulated bind var args = slice.call( arguments, 2 ), proxy = function() { return fn.apply( context, args.concat( slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; return proxy; }, // Mutifunctional method to get and set values to a collection // The value/s can optionally be executed if it's a function access: function( elems, key, value, exec, fn, pass ) { var length = elems.length; // Setting many attributes if ( typeof key === "object" ) { for ( var k in key ) { jQuery.access( elems, k, key[k], exec, fn, value ); } return elems; } // Setting one attribute if ( value !== undefined ) { // Optionally, function values get executed if exec is true exec = !pass && exec && jQuery.isFunction(value); for ( var i = 0; i < length; i++ ) { fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); } return elems; } // Getting an attribute return length ? fn( elems[0], key ) : undefined; }, now: function() { return ( new Date() ).getTime(); }, // Use of jQuery.browser is frowned upon. // More details: http://docs.jquery.com/Utilities/jQuery.browser uaMatch: function( ua ) { ua = ua.toLowerCase(); var match = rwebkit.exec( ua ) || ropera.exec( ua ) || rmsie.exec( ua ) || ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || []; return { browser: match[1] || "", version: match[2] || "0" }; }, sub: function() { function jQuerySub( selector, context ) { return new jQuerySub.fn.init( selector, context ); } jQuery.extend( true, jQuerySub, this ); jQuerySub.superclass = this; jQuerySub.fn = jQuerySub.prototype = this(); jQuerySub.fn.constructor = jQuerySub; jQuerySub.sub = this.sub; jQuerySub.fn.init = function init( selector, context ) { if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { context = jQuerySub( context ); } return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); }; jQuerySub.fn.init.prototype = jQuerySub.fn; var rootjQuerySub = jQuerySub(document); return jQuerySub; }, browser: {} }); // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); browserMatch = jQuery.uaMatch( userAgent ); if ( browserMatch.browser ) { jQuery.browser[ browserMatch.browser ] = true; jQuery.browser.version = browserMatch.version; } // Deprecated, use jQuery.browser.webkit instead if ( jQuery.browser.webkit ) { jQuery.browser.safari = true; } // IE doesn't match non-breaking spaces with \s if ( rnotwhite.test( "\xA0" ) ) { trimLeft = /^[\s\xA0]+/; trimRight = /[\s\xA0]+$/; } // All jQuery objects should point back to these rootjQuery = jQuery(document); // Cleanup functions for the document ready method if ( document.addEventListener ) { DOMContentLoaded = function() { document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); jQuery.ready(); }; } else if ( document.attachEvent ) { DOMContentLoaded = function() { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if ( document.readyState === "complete" ) { document.detachEvent( "onreadystatechange", DOMContentLoaded ); jQuery.ready(); } }; } // The DOM ready check for Internet Explorer function doScrollCheck() { if ( jQuery.isReady ) { return; } try { // If IE is used, use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ document.documentElement.doScroll("left"); } catch(e) { setTimeout( doScrollCheck, 1 ); return; } // and execute any waiting functions jQuery.ready(); } return jQuery; })(); // String to Object flags format cache var flagsCache = {}; // Convert String-formatted flags into Object-formatted ones and store in cache function createFlags( flags ) { var object = flagsCache[ flags ] = {}, i, length; flags = flags.split( /\s+/ ); for ( i = 0, length = flags.length; i < length; i++ ) { object[ flags[i] ] = true; } return object; } /* * Create a callback list using the following parameters: * * flags: an optional list of space-separated flags that will change how * the callback list behaves * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible flags: * * once: will ensure the callback list can only be fired once (like a Deferred) * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( flags ) { // Convert flags from String-formatted to Object-formatted // (we check in cache first) flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; var // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = [], // Last fire value (for non-forgettable lists) memory, // Flag to know if list is currently firing firing, // First callback to fire (used internally by add and fireWith) firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // Add one or several callbacks to the list add = function( args ) { var i, length, elem, type, actual; for ( i = 0, length = args.length; i < length; i++ ) { elem = args[ i ]; type = jQuery.type( elem ); if ( type === "array" ) { // Inspect recursively add( elem ); } else if ( type === "function" ) { // Add if not in unique mode and callback is not in if ( !flags.unique || !self.has( elem ) ) { list.push( elem ); } } } }, // Fire callbacks fire = function( context, args ) { args = args || []; memory = !flags.memory || [ context, args ]; firing = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { memory = true; // Mark as halted break; } } firing = false; if ( list ) { if ( !flags.once ) { if ( stack && stack.length ) { memory = stack.shift(); self.fireWith( memory[ 0 ], memory[ 1 ] ); } } else if ( memory === true ) { self.disable(); } else { list = []; } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { var length = list.length; add( arguments ); // Do we need to add the callbacks to the // current firing batch? if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away, unless previous // firing was halted (stopOnFalse) } else if ( memory && memory !== true ) { firingStart = length; fire( memory[ 0 ], memory[ 1 ] ); } } return this; }, // Remove a callback from the list remove: function() { if ( list ) { var args = arguments, argIndex = 0, argLength = args.length; for ( ; argIndex < argLength ; argIndex++ ) { for ( var i = 0; i < list.length; i++ ) { if ( args[ argIndex ] === list[ i ] ) { // Handle firingIndex and firingLength if ( firing ) { if ( i <= firingLength ) { firingLength--; if ( i <= firingIndex ) { firingIndex--; } } } // Remove the element list.splice( i--, 1 ); // If we have some unicity property then // we only need to do this once if ( flags.unique ) { break; } } } } } return this; }, // Control if a given callback is in the list has: function( fn ) { if ( list ) { var i = 0, length = list.length; for ( ; i < length; i++ ) { if ( fn === list[ i ] ) { return true; } } } return false; }, // Remove all callbacks from the list empty: function() { list = []; return this; }, // Have the list do nothing anymore disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state lock: function() { stack = undefined; if ( !memory || memory === true ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( stack ) { if ( firing ) { if ( !flags.once ) { stack.push( [ context, args ] ); } } else if ( !( flags.once && memory ) ) { fire( context, args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!memory; } }; return self; }; var // Static reference to slice sliceDeferred = [].slice; jQuery.extend({ Deferred: function( func ) { var doneList = jQuery.Callbacks( "once memory" ), failList = jQuery.Callbacks( "once memory" ), progressList = jQuery.Callbacks( "memory" ), state = "pending", lists = { resolve: doneList, reject: failList, notify: progressList }, promise = { done: doneList.add, fail: failList.add, progress: progressList.add, state: function() { return state; }, // Deprecated isResolved: doneList.fired, isRejected: failList.fired, then: function( doneCallbacks, failCallbacks, progressCallbacks ) { deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); return this; }, always: function() { deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); return this; }, pipe: function( fnDone, fnFail, fnProgress ) { return jQuery.Deferred(function( newDefer ) { jQuery.each( { done: [ fnDone, "resolve" ], fail: [ fnFail, "reject" ], progress: [ fnProgress, "notify" ] }, function( handler, data ) { var fn = data[ 0 ], action = data[ 1 ], returned; if ( jQuery.isFunction( fn ) ) { deferred[ handler ](function() { returned = fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); } else { newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); } }); } else { deferred[ handler ]( newDefer[ action ] ); } }); }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { if ( obj == null ) { obj = promise; } else { for ( var key in promise ) { obj[ key ] = promise[ key ]; } } return obj; } }, deferred = promise.promise({}), key; for ( key in lists ) { deferred[ key ] = lists[ key ].fire; deferred[ key + "With" ] = lists[ key ].fireWith; } // Handle state deferred.done( function() { state = "resolved"; }, failList.disable, progressList.lock ).fail( function() { state = "rejected"; }, doneList.disable, progressList.lock ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }, // Deferred helper when: function( firstParam ) { var args = sliceDeferred.call( arguments, 0 ), i = 0, length = args.length, pValues = new Array( length ), count = length, pCount = length, deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? firstParam : jQuery.Deferred(), promise = deferred.promise(); function resolveFunc( i ) { return function( value ) { args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; if ( !( --count ) ) { deferred.resolveWith( deferred, args ); } }; } function progressFunc( i ) { return function( value ) { pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; deferred.notifyWith( promise, pValues ); }; } if ( length > 1 ) { for ( ; i < length; i++ ) { if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); } else { --count; } } if ( !count ) { deferred.resolveWith( deferred, args ); } } else if ( deferred !== firstParam ) { deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); } return promise; } }); jQuery.support = (function() { var support, all, a, select, opt, input, marginDiv, fragment, tds, events, eventName, i, isSupported, div = document.createElement( "div" ), documentElement = document.documentElement; // Preliminary tests div.setAttribute("className", "t"); div.innerHTML = "
a"; all = div.getElementsByTagName( "*" ); a = div.getElementsByTagName( "a" )[ 0 ]; // Can't get basic test support if ( !all || !all.length || !a ) { return {}; } // First batch of supports tests select = document.createElement( "select" ); opt = select.appendChild( document.createElement("option") ); input = div.getElementsByTagName( "input" )[ 0 ]; support = { // IE strips leading whitespace when .innerHTML is used leadingWhitespace: ( div.firstChild.nodeType === 3 ), // Make sure that tbody elements aren't automatically inserted // IE will insert them into empty tables tbody: !div.getElementsByTagName("tbody").length, // Make sure that link elements get serialized correctly by innerHTML // This requires a wrapper element in IE htmlSerialize: !!div.getElementsByTagName("link").length, // Get the style information from getAttribute // (IE uses .cssText instead) style: /top/.test( a.getAttribute("style") ), // Make sure that URLs aren't manipulated // (IE normalizes it by default) hrefNormalized: ( a.getAttribute("href") === "/a" ), // Make sure that element opacity exists // (IE uses filter instead) // Use a regex to work around a WebKit issue. See #5145 opacity: /^0.55/.test( a.style.opacity ), // Verify style float existence // (IE uses styleFloat instead of cssFloat) cssFloat: !!a.style.cssFloat, // Make sure that if no value is specified for a checkbox // that it defaults to "on". // (WebKit defaults to "" instead) checkOn: ( input.value === "on" ), // Make sure that a selected-by-default option has a working selected property. // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) optSelected: opt.selected, // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) getSetAttribute: div.className !== "t", // Tests for enctype support on a form(#6743) enctype: !!document.createElement("form").enctype, // Makes sure cloning an html5 element does not cause problems // Where outerHTML is undefined, this still works html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", // Will be defined later submitBubbles: true, changeBubbles: true, focusinBubbles: false, deleteExpando: true, noCloneEvent: true, inlineBlockNeedsLayout: false, shrinkWrapBlocks: false, reliableMarginRight: true }; // Make sure checked status is properly cloned input.checked = true; support.noCloneChecked = input.cloneNode( true ).checked; // Make sure that the options inside disabled selects aren't marked as disabled // (WebKit marks them as disabled) select.disabled = true; support.optDisabled = !opt.disabled; // Test to see if it's possible to delete an expando from an element // Fails in Internet Explorer try { delete div.test; } catch( e ) { support.deleteExpando = false; } if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { div.attachEvent( "onclick", function() { // Cloning a node shouldn't copy over any // bound event handlers (IE does this) support.noCloneEvent = false; }); div.cloneNode( true ).fireEvent( "onclick" ); } // Check if a radio maintains its value // after being appended to the DOM input = document.createElement("input"); input.value = "t"; input.setAttribute("type", "radio"); support.radioValue = input.value === "t"; input.setAttribute("checked", "checked"); div.appendChild( input ); fragment = document.createDocumentFragment(); fragment.appendChild( div.lastChild ); // WebKit doesn't clone checked state correctly in fragments support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; // Check if a disconnected checkbox will retain its checked // value of true after appended to the DOM (IE6/7) support.appendChecked = input.checked; fragment.removeChild( input ); fragment.appendChild( div ); div.innerHTML = ""; // Check if div with explicit width and no margin-right incorrectly // gets computed margin-right based on width of container. For more // info see bug #3333 // Fails in WebKit before Feb 2011 nightlies // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right if ( window.getComputedStyle ) { marginDiv = document.createElement( "div" ); marginDiv.style.width = "0"; marginDiv.style.marginRight = "0"; div.style.width = "2px"; div.appendChild( marginDiv ); support.reliableMarginRight = ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; } // Technique from Juriy Zaytsev // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ // We only care about the case where non-standard event systems // are used, namely in IE. Short-circuiting here helps us to // avoid an eval call (in setAttribute) which can cause CSP // to go haywire. See: https://developer.mozilla.org/en/Security/CSP if ( div.attachEvent ) { for( i in { submit: 1, change: 1, focusin: 1 }) { eventName = "on" + i; isSupported = ( eventName in div ); if ( !isSupported ) { div.setAttribute( eventName, "return;" ); isSupported = ( typeof div[ eventName ] === "function" ); } support[ i + "Bubbles" ] = isSupported; } } fragment.removeChild( div ); // Null elements to avoid leaks in IE fragment = select = opt = marginDiv = div = input = null; // Run tests that need a body at doc ready jQuery(function() { var container, outer, inner, table, td, offsetSupport, conMarginTop, ptlm, vb, style, html, body = document.getElementsByTagName("body")[0]; if ( !body ) { // Return for frameset docs that don't have a body return; } conMarginTop = 1; ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;"; vb = "visibility:hidden;border:0;"; style = "style='" + ptlm + "border:5px solid #000;padding:0;'"; html = "
" + "" + "
"; container = document.createElement("div"); container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; body.insertBefore( container, body.firstChild ); // Construct the test element div = document.createElement("div"); container.appendChild( div ); // Check if table cells still have offsetWidth/Height when they are set // to display:none and there are still other visible table cells in a // table row; if so, offsetWidth/Height are not reliable for use when // determining if an element has been hidden directly using // display:none (it is still safe to use offsets if a parent element is // hidden; don safety goggles and see bug #4512 for more information). // (only IE 8 fails this test) div.innerHTML = "
t
"; tds = div.getElementsByTagName( "td" ); isSupported = ( tds[ 0 ].offsetHeight === 0 ); tds[ 0 ].style.display = ""; tds[ 1 ].style.display = "none"; // Check if empty table cells still have offsetWidth/Height // (IE <= 8 fail this test) support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); // Figure out if the W3C box model works as expected div.innerHTML = ""; div.style.width = div.style.paddingLeft = "1px"; jQuery.boxModel = support.boxModel = div.offsetWidth === 2; if ( typeof div.style.zoom !== "undefined" ) { // Check if natively block-level elements act like inline-block // elements when setting their display to 'inline' and giving // them layout // (IE < 8 does this) div.style.display = "inline"; div.style.zoom = 1; support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); // Check if elements with layout shrink-wrap their children // (IE 6 does this) div.style.display = ""; div.innerHTML = "
"; support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); } div.style.cssText = ptlm + vb; div.innerHTML = html; outer = div.firstChild; inner = outer.firstChild; td = outer.nextSibling.firstChild.firstChild; offsetSupport = { doesNotAddBorder: ( inner.offsetTop !== 5 ), doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) }; inner.style.position = "fixed"; inner.style.top = "20px"; // safari subtracts parent border width here which is 5px offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); inner.style.position = inner.style.top = ""; outer.style.overflow = "hidden"; outer.style.position = "relative"; offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); body.removeChild( container ); div = container = null; jQuery.extend( support, offsetSupport ); }); return support; })(); var rbrace = /^(?:\{.*\}|\[.*\])$/, rmultiDash = /([A-Z])/g; jQuery.extend({ cache: {}, // Please use with caution uuid: 0, // Unique for each copy of jQuery on the page // Non-digits removed to match rinlinejQuery expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), // The following elements throw uncatchable exceptions if you // attempt to add expando properties to them. noData: { "embed": true, // Ban all objects except for Flash (which handle expandos) "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", "applet": true }, hasData: function( elem ) { elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; return !!elem && !isEmptyDataObject( elem ); }, data: function( elem, name, data, pvt /* Internal Use Only */ ) { if ( !jQuery.acceptData( elem ) ) { return; } var privateCache, thisCache, ret, internalKey = jQuery.expando, getByName = typeof name === "string", // We have to handle DOM nodes and JS objects differently because IE6-7 // can't GC object references properly across the DOM-JS boundary isNode = elem.nodeType, // Only DOM nodes need the global jQuery cache; JS object data is // attached directly to the object so GC can occur automatically cache = isNode ? jQuery.cache : elem, // Only defining an ID for JS objects if its cache already exists allows // the code to shortcut on the same path as a DOM node with no cache id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, isEvents = name === "events"; // Avoid doing any more work than we need to when trying to get data on an // object that has no data at all if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { return; } if ( !id ) { // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache if ( isNode ) { elem[ internalKey ] = id = ++jQuery.uuid; } else { id = internalKey; } } if ( !cache[ id ] ) { cache[ id ] = {}; // Avoids exposing jQuery metadata on plain JS objects when the object // is serialized using JSON.stringify if ( !isNode ) { cache[ id ].toJSON = jQuery.noop; } } // An object can be passed to jQuery.data instead of a key/value pair; this gets // shallow copied over onto the existing cache if ( typeof name === "object" || typeof name === "function" ) { if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name ); } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name ); } } privateCache = thisCache = cache[ id ]; // jQuery data() is stored in a separate object inside the object's internal data // cache in order to avoid key collisions between internal data and user-defined // data. if ( !pvt ) { if ( !thisCache.data ) { thisCache.data = {}; } thisCache = thisCache.data; } if ( data !== undefined ) { thisCache[ jQuery.camelCase( name ) ] = data; } // Users should not attempt to inspect the internal events object using jQuery.data, // it is undocumented and subject to change. But does anyone listen? No. if ( isEvents && !thisCache[ name ] ) { return privateCache.events; } // Check for both converted-to-camel and non-converted data property names // If a data property was specified if ( getByName ) { // First Try to find as-is property data ret = thisCache[ name ]; // Test for null|undefined property data if ( ret == null ) { // Try to find the camelCased property ret = thisCache[ jQuery.camelCase( name ) ]; } } else { ret = thisCache; } return ret; }, removeData: function( elem, name, pvt /* Internal Use Only */ ) { if ( !jQuery.acceptData( elem ) ) { return; } var thisCache, i, l, // Reference to internal data cache key internalKey = jQuery.expando, isNode = elem.nodeType, // See jQuery.data for more information cache = isNode ? jQuery.cache : elem, // See jQuery.data for more information id = isNode ? elem[ internalKey ] : internalKey; // If there is already no cache entry for this object, there is no // purpose in continuing if ( !cache[ id ] ) { return; } if ( name ) { thisCache = pvt ? cache[ id ] : cache[ id ].data; if ( thisCache ) { // Support array or space separated string names for data keys if ( !jQuery.isArray( name ) ) { // try the string as a key before any manipulation if ( name in thisCache ) { name = [ name ]; } else { // split the camel cased version by spaces unless a key with the spaces exists name = jQuery.camelCase( name ); if ( name in thisCache ) { name = [ name ]; } else { name = name.split( " " ); } } } for ( i = 0, l = name.length; i < l; i++ ) { delete thisCache[ name[i] ]; } // If there is no data left in the cache, we want to continue // and let the cache object itself get destroyed if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { return; } } } // See jQuery.data for more information if ( !pvt ) { delete cache[ id ].data; // Don't destroy the parent cache unless the internal data object // had been the only thing left in it if ( !isEmptyDataObject(cache[ id ]) ) { return; } } // Browsers that fail expando deletion also refuse to delete expandos on // the window, but it will allow it on all other JS objects; other browsers // don't care // Ensure that `cache` is not a window object #10080 if ( jQuery.support.deleteExpando || !cache.setInterval ) { delete cache[ id ]; } else { cache[ id ] = null; } // We destroyed the cache and need to eliminate the expando on the node to avoid // false lookups in the cache for entries that no longer exist if ( isNode ) { // IE does not allow us to delete expando properties from nodes, // nor does it have a removeAttribute function on Document nodes; // we must handle all of these cases if ( jQuery.support.deleteExpando ) { delete elem[ internalKey ]; } else if ( elem.removeAttribute ) { elem.removeAttribute( internalKey ); } else { elem[ internalKey ] = null; } } }, // For internal use only. _data: function( elem, name, data ) { return jQuery.data( elem, name, data, true ); }, // A method for determining if a DOM node can handle the data expando acceptData: function( elem ) { if ( elem.nodeName ) { var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; if ( match ) { return !(match === true || elem.getAttribute("classid") !== match); } } return true; } }); jQuery.fn.extend({ data: function( key, value ) { var parts, attr, name, data = null; if ( typeof key === "undefined" ) { if ( this.length ) { data = jQuery.data( this[0] ); if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) { attr = this[0].attributes; for ( var i = 0, l = attr.length; i < l; i++ ) { name = attr[i].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.substring(5) ); dataAttr( this[0], name, data[ name ] ); } } jQuery._data( this[0], "parsedAttrs", true ); } } return data; } else if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); } parts = key.split("."); parts[1] = parts[1] ? "." + parts[1] : ""; if ( value === undefined ) { data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); // Try to fetch any internally stored data first if ( data === undefined && this.length ) { data = jQuery.data( this[0], key ); data = dataAttr( this[0], key, data ); } return data === undefined && parts[1] ? this.data( parts[0] ) : data; } else { return this.each(function() { var self = jQuery( this ), args = [ parts[0], value ]; self.triggerHandler( "setData" + parts[1] + "!", args ); jQuery.data( this, key, value ); self.triggerHandler( "changeData" + parts[1] + "!", args ); }); } }, removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } }); function dataAttr( elem, key, data ) { // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : jQuery.isNumeric( data ) ? parseFloat( data ) : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {} // Make sure we set the data so it isn't changed later jQuery.data( elem, key, data ); } else { data = undefined; } } return data; } // checks a cache object for emptiness function isEmptyDataObject( obj ) { for ( var name in obj ) { // if the public data object is empty, the private is still empty if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { continue; } if ( name !== "toJSON" ) { return false; } } return true; } function handleQueueMarkDefer( elem, type, src ) { var deferDataKey = type + "defer", queueDataKey = type + "queue", markDataKey = type + "mark", defer = jQuery._data( elem, deferDataKey ); if ( defer && ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { // Give room for hard-coded callbacks to fire first // and eventually mark/queue something else on the element setTimeout( function() { if ( !jQuery._data( elem, queueDataKey ) && !jQuery._data( elem, markDataKey ) ) { jQuery.removeData( elem, deferDataKey, true ); defer.fire(); } }, 0 ); } } jQuery.extend({ _mark: function( elem, type ) { if ( elem ) { type = ( type || "fx" ) + "mark"; jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); } }, _unmark: function( force, elem, type ) { if ( force !== true ) { type = elem; elem = force; force = false; } if ( elem ) { type = type || "fx"; var key = type + "mark", count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); if ( count ) { jQuery._data( elem, key, count ); } else { jQuery.removeData( elem, key, true ); handleQueueMarkDefer( elem, type, "mark" ); } } }, queue: function( elem, type, data ) { var q; if ( elem ) { type = ( type || "fx" ) + "queue"; q = jQuery._data( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { if ( !q || jQuery.isArray(data) ) { q = jQuery._data( elem, type, jQuery.makeArray(data) ); } else { q.push( data ); } } return q || []; } }, dequeue: function( elem, type ) { type = type || "fx"; var queue = jQuery.queue( elem, type ), fn = queue.shift(), hooks = {}; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { fn = queue.shift(); } if ( fn ) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) { queue.unshift( "inprogress" ); } jQuery._data( elem, type + ".run", hooks ); fn.call( elem, function() { jQuery.dequeue( elem, type ); }, hooks ); } if ( !queue.length ) { jQuery.removeData( elem, type + "queue " + type + ".run", true ); handleQueueMarkDefer( elem, type, "queue" ); } } }); jQuery.fn.extend({ queue: function( type, data ) { if ( typeof type !== "string" ) { data = type; type = "fx"; } if ( data === undefined ) { return jQuery.queue( this[0], type ); } return this.each(function() { var queue = jQuery.queue( this, type, data ); if ( type === "fx" && queue[0] !== "inprogress" ) { jQuery.dequeue( this, type ); } }); }, dequeue: function( type ) { return this.each(function() { jQuery.dequeue( this, type ); }); }, // Based off of the plugin by Clint Helfers, with permission. // http://blindsignals.com/index.php/2009/07/jquery-delay/ delay: function( time, type ) { time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; type = type || "fx"; return this.queue( type, function( next, hooks ) { var timeout = setTimeout( next, time ); hooks.stop = function() { clearTimeout( timeout ); }; }); }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, object ) { if ( typeof type !== "string" ) { object = type; type = undefined; } type = type || "fx"; var defer = jQuery.Deferred(), elements = this, i = elements.length, count = 1, deferDataKey = type + "defer", queueDataKey = type + "queue", markDataKey = type + "mark", tmp; function resolve() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } } while( i-- ) { if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { count++; tmp.add( resolve ); } } resolve(); return defer.promise(); } }); var rclass = /[\n\t\r]/g, rspace = /\s+/, rreturn = /\r/g, rtype = /^(?:button|input)$/i, rfocusable = /^(?:button|input|object|select|textarea)$/i, rclickable = /^a(?:rea)?$/i, rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, getSetAttribute = jQuery.support.getSetAttribute, nodeHook, boolHook, fixSpecified; jQuery.fn.extend({ attr: function( name, value ) { return jQuery.access( this, name, value, true, jQuery.attr ); }, removeAttr: function( name ) { return this.each(function() { jQuery.removeAttr( this, name ); }); }, prop: function( name, value ) { return jQuery.access( this, name, value, true, jQuery.prop ); }, removeProp: function( name ) { name = jQuery.propFix[ name ] || name; return this.each(function() { // try/catch handles cases where IE balks (such as removing a property on window) try { this[ name ] = undefined; delete this[ name ]; } catch( e ) {} }); }, addClass: function( value ) { var classNames, i, l, elem, setClass, c, cl; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { jQuery( this ).addClass( value.call(this, j, this.className) ); }); } if ( value && typeof value === "string" ) { classNames = value.split( rspace ); for ( i = 0, l = this.length; i < l; i++ ) { elem = this[ i ]; if ( elem.nodeType === 1 ) { if ( !elem.className && classNames.length === 1 ) { elem.className = value; } else { setClass = " " + elem.className + " "; for ( c = 0, cl = classNames.length; c < cl; c++ ) { if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { setClass += classNames[ c ] + " "; } } elem.className = jQuery.trim( setClass ); } } } } return this; }, removeClass: function( value ) { var classNames, i, l, elem, className, c, cl; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { jQuery( this ).removeClass( value.call(this, j, this.className) ); }); } if ( (value && typeof value === "string") || value === undefined ) { classNames = ( value || "" ).split( rspace ); for ( i = 0, l = this.length; i < l; i++ ) { elem = this[ i ]; if ( elem.nodeType === 1 && elem.className ) { if ( value ) { className = (" " + elem.className + " ").replace( rclass, " " ); for ( c = 0, cl = classNames.length; c < cl; c++ ) { className = className.replace(" " + classNames[ c ] + " ", " "); } elem.className = jQuery.trim( className ); } else { elem.className = ""; } } } } return this; }, toggleClass: function( value, stateVal ) { var type = typeof value, isBool = typeof stateVal === "boolean"; if ( jQuery.isFunction( value ) ) { return this.each(function( i ) { jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); }); } return this.each(function() { if ( type === "string" ) { // toggle individual class names var className, i = 0, self = jQuery( this ), state = stateVal, classNames = value.split( rspace ); while ( (className = classNames[ i++ ]) ) { // check each className given, space seperated list state = isBool ? state : !self.hasClass( className ); self[ state ? "addClass" : "removeClass" ]( className ); } } else if ( type === "undefined" || type === "boolean" ) { if ( this.className ) { // store className if set jQuery._data( this, "__className__", this.className ); } // toggle whole className this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; } }); }, hasClass: function( selector ) { var className = " " + selector + " ", i = 0, l = this.length; for ( ; i < l; i++ ) { if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { return true; } } return false; }, val: function( value ) { var hooks, ret, isFunction, elem = this[0]; if ( !arguments.length ) { if ( elem ) { hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { return ret; } ret = elem.value; return typeof ret === "string" ? // handle most common string cases ret.replace(rreturn, "") : // handle cases where value is null/undef or number ret == null ? "" : ret; } return; } isFunction = jQuery.isFunction( value ); return this.each(function( i ) { var self = jQuery(this), val; if ( this.nodeType !== 1 ) { return; } if ( isFunction ) { val = value.call( this, i, self.val() ); } else { val = value; } // Treat null/undefined as ""; convert numbers to string if ( val == null ) { val = ""; } else if ( typeof val === "number" ) { val += ""; } else if ( jQuery.isArray( val ) ) { val = jQuery.map(val, function ( value ) { return value == null ? "" : value + ""; }); } hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; // If set returns undefined, fall back to normal setting if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { this.value = val; } }); } }); jQuery.extend({ valHooks: { option: { get: function( elem ) { // attributes.value is undefined in Blackberry 4.7 but // uses .value. See #6932 var val = elem.attributes.value; return !val || val.specified ? elem.value : elem.text; } }, select: { get: function( elem ) { var value, i, max, option, index = elem.selectedIndex, values = [], options = elem.options, one = elem.type === "select-one"; // Nothing was selected if ( index < 0 ) { return null; } // Loop through all the selected options i = one ? index : 0; max = one ? index + 1 : options.length; for ( ; i < max; i++ ) { option = options[ i ]; // Don't return options that are disabled or in a disabled optgroup if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { // Get the specific value for the option value = jQuery( option ).val(); // We don't need an array for one selects if ( one ) { return value; } // Multi-Selects return an array values.push( value ); } } // Fixes Bug #2551 -- select.val() broken in IE after form.reset() if ( one && !values.length && options.length ) { return jQuery( options[ index ] ).val(); } return values; }, set: function( elem, value ) { var values = jQuery.makeArray( value ); jQuery(elem).find("option").each(function() { this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; }); if ( !values.length ) { elem.selectedIndex = -1; } return values; } } }, attrFn: { val: true, css: true, html: true, text: true, data: true, width: true, height: true, offset: true }, attr: function( elem, name, value, pass ) { var ret, hooks, notxml, nType = elem.nodeType; // don't get/set attributes on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } if ( pass && name in jQuery.attrFn ) { return jQuery( elem )[ name ]( value ); } // Fallback to prop when attributes are not supported if ( typeof elem.getAttribute === "undefined" ) { return jQuery.prop( elem, name, value ); } notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); // All attributes are lowercase // Grab necessary hook if one is defined if ( notxml ) { name = name.toLowerCase(); hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); } if ( value !== undefined ) { if ( value === null ) { jQuery.removeAttr( elem, name ); return; } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { return ret; } else { elem.setAttribute( name, "" + value ); return value; } } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { return ret; } else { ret = elem.getAttribute( name ); // Non-existent attributes return null, we normalize to undefined return ret === null ? undefined : ret; } }, removeAttr: function( elem, value ) { var propName, attrNames, name, l, i = 0; if ( value && elem.nodeType === 1 ) { attrNames = value.toLowerCase().split( rspace ); l = attrNames.length; for ( ; i < l; i++ ) { name = attrNames[ i ]; if ( name ) { propName = jQuery.propFix[ name ] || name; // See #9699 for explanation of this approach (setting first, then removal) jQuery.attr( elem, name, "" ); elem.removeAttribute( getSetAttribute ? name : propName ); // Set corresponding property to false for boolean attributes if ( rboolean.test( name ) && propName in elem ) { elem[ propName ] = false; } } } } }, attrHooks: { type: { set: function( elem, value ) { // We can't allow the type property to be changed (since it causes problems in IE) if ( rtype.test( elem.nodeName ) && elem.parentNode ) { jQuery.error( "type property can't be changed" ); } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { // Setting the type on a radio button after the value resets the value in IE6-9 // Reset value to it's default in case type is set after value // This is for element creation var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } }, // Use the value property for back compat // Use the nodeHook for button elements in IE6/7 (#1954) value: { get: function( elem, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.get( elem, name ); } return name in elem ? elem.value : null; }, set: function( elem, value, name ) { if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { return nodeHook.set( elem, value, name ); } // Does not return so that setAttribute is also used elem.value = value; } } }, propFix: { tabindex: "tabIndex", readonly: "readOnly", "for": "htmlFor", "class": "className", maxlength: "maxLength", cellspacing: "cellSpacing", cellpadding: "cellPadding", rowspan: "rowSpan", colspan: "colSpan", usemap: "useMap", frameborder: "frameBorder", contenteditable: "contentEditable" }, prop: function( elem, name, value ) { var ret, hooks, notxml, nType = elem.nodeType; // don't get/set properties on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); if ( notxml ) { // Fix name and attach hooks name = jQuery.propFix[ name ] || name; hooks = jQuery.propHooks[ name ]; } if ( value !== undefined ) { if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { return ret; } else { return ( elem[ name ] = value ); } } else { if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { return ret; } else { return elem[ name ]; } } }, propHooks: { tabIndex: { get: function( elem ) { // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ var attributeNode = elem.getAttributeNode("tabindex"); return attributeNode && attributeNode.specified ? parseInt( attributeNode.value, 10 ) : rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? 0 : undefined; } } } }); // Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; // Hook for boolean attributes boolHook = { get: function( elem, name ) { // Align boolean attributes with corresponding properties // Fall back to attribute presence where some booleans are not supported var attrNode, property = jQuery.prop( elem, name ); return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? name.toLowerCase() : undefined; }, set: function( elem, value, name ) { var propName; if ( value === false ) { // Remove boolean attributes when set to false jQuery.removeAttr( elem, name ); } else { // value is true since we know at this point it's type boolean and not false // Set boolean attributes to the same name and set the DOM property propName = jQuery.propFix[ name ] || name; if ( propName in elem ) { // Only set the IDL specifically if it already exists on the element elem[ propName ] = true; } elem.setAttribute( name, name.toLowerCase() ); } return name; } }; // IE6/7 do not support getting/setting some attributes with get/setAttribute if ( !getSetAttribute ) { fixSpecified = { name: true, id: true }; // Use this for any attribute in IE6/7 // This fixes almost every IE6/7 issue nodeHook = jQuery.valHooks.button = { get: function( elem, name ) { var ret; ret = elem.getAttributeNode( name ); return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? ret.nodeValue : undefined; }, set: function( elem, value, name ) { // Set the existing or create a new attribute node var ret = elem.getAttributeNode( name ); if ( !ret ) { ret = document.createAttribute( name ); elem.setAttributeNode( ret ); } return ( ret.nodeValue = value + "" ); } }; // Apply the nodeHook to tabindex jQuery.attrHooks.tabindex.set = nodeHook.set; // Set width and height to auto instead of 0 on empty string( Bug #8150 ) // This is for removals jQuery.each([ "width", "height" ], function( i, name ) { jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { set: function( elem, value ) { if ( value === "" ) { elem.setAttribute( name, "auto" ); return value; } } }); }); // Set contenteditable to false on removals(#10429) // Setting to empty string throws an error as an invalid value jQuery.attrHooks.contenteditable = { get: nodeHook.get, set: function( elem, value, name ) { if ( value === "" ) { value = "false"; } nodeHook.set( elem, value, name ); } }; } // Some attributes require a special call on IE if ( !jQuery.support.hrefNormalized ) { jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { get: function( elem ) { var ret = elem.getAttribute( name, 2 ); return ret === null ? undefined : ret; } }); }); } if ( !jQuery.support.style ) { jQuery.attrHooks.style = { get: function( elem ) { // Return undefined in the case of empty string // Normalize to lowercase since IE uppercases css property names return elem.style.cssText.toLowerCase() || undefined; }, set: function( elem, value ) { return ( elem.style.cssText = "" + value ); } }; } // Safari mis-reports the default selected property of an option // Accessing the parent's selectedIndex property fixes it if ( !jQuery.support.optSelected ) { jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { get: function( elem ) { var parent = elem.parentNode; if ( parent ) { parent.selectedIndex; // Make sure that it also works with optgroups, see #5701 if ( parent.parentNode ) { parent.parentNode.selectedIndex; } } return null; } }); } // IE6/7 call enctype encoding if ( !jQuery.support.enctype ) { jQuery.propFix.enctype = "encoding"; } // Radios and checkboxes getter/setter if ( !jQuery.support.checkOn ) { jQuery.each([ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = { get: function( elem ) { // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified return elem.getAttribute("value") === null ? "on" : elem.value; } }; }); } jQuery.each([ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { set: function( elem, value ) { if ( jQuery.isArray( value ) ) { return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); } } }); }); var rformElems = /^(?:textarea|input|select)$/i, rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, rhoverHack = /\bhover(\.\S+)?\b/, rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|contextmenu)|click/, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, quickParse = function( selector ) { var quick = rquickIs.exec( selector ); if ( quick ) { // 0 1 2 3 // [ _, tag, id, class ] quick[1] = ( quick[1] || "" ).toLowerCase(); quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); } return quick; }, quickIs = function( elem, m ) { var attrs = elem.attributes || {}; return ( (!m[1] || elem.nodeName.toLowerCase() === m[1]) && (!m[2] || (attrs.id || {}).value === m[2]) && (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) ); }, hoverHack = function( events ) { return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); }; /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { add: function( elem, types, handler, data, selector ) { var elemData, eventHandle, events, t, tns, type, namespaces, handleObj, handleObjIn, quick, handlers, special; // Don't attach events to noData or text/comment nodes (allow plain objects tho) if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { return; } // Caller can pass in an object of custom data in lieu of the handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; } // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; } // Init the element's event structure and main handler, if this is the first events = elemData.events; if ( !events ) { elemData.events = events = {}; } eventHandle = elemData.handle; if ( !eventHandle ) { elemData.handle = eventHandle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : undefined; }; // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events eventHandle.elem = elem; } // Handle multiple events separated by a space // jQuery(...).bind("mouseover mouseout", fn); types = jQuery.trim( hoverHack(types) ).split( " " ); for ( t = 0; t < types.length; t++ ) { tns = rtypenamespace.exec( types[t] ) || []; type = tns[1]; namespaces = ( tns[2] || "" ).split( "." ).sort(); // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers handleObj = jQuery.extend({ type: type, origType: tns[1], data: data, handler: handler, guid: handler.guid, selector: selector, quick: quickParse( selector ), namespace: namespaces.join(".") }, handleObjIn ); // Init the event handler queue if we're the first handlers = events[ type ]; if ( !handlers ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener/attachEvent if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { // Bind the global event handler to the element if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } else if ( elem.attachEvent ) { elem.attachEvent( "on" + type, eventHandle ); } } } if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } // Add to the element's handler list, delegates in front if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } // Keep track of which events have ever been used, for event optimization jQuery.event.global[ type ] = true; } // Nullify elem to prevent memory leaks in IE elem = null; }, global: {}, // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), t, tns, type, origType, namespaces, origCount, j, events, special, handle, eventType, handleObj; if ( !elemData || !(events = elemData.events) ) { return; } // Once for each type.namespace in types; type may be omitted types = jQuery.trim( hoverHack( types || "" ) ).split(" "); for ( t = 0; t < types.length; t++ ) { tns = rtypenamespace.exec( types[t] ) || []; type = origType = tns[1]; namespaces = tns[2]; // Unbind all events (on this namespace, if provided) for the element if ( !type ) { for ( type in events ) { jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } special = jQuery.event.special[ type ] || {}; type = ( selector? special.delegateType : special.bindType ) || type; eventType = events[ type ] || []; origCount = eventType.length; namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; // Remove matching events for ( j = 0; j < eventType.length; j++ ) { handleObj = eventType[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !namespaces || namespaces.test( handleObj.namespace ) ) && ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { eventType.splice( j--, 1 ); if ( handleObj.selector ) { eventType.delegateCount--; } if ( special.remove ) { special.remove.call( elem, handleObj ); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( eventType.length === 0 && origCount !== eventType.length ) { if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { jQuery.removeEvent( elem, type, elemData.handle ); } delete events[ type ]; } } // Remove the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { handle = elemData.handle; if ( handle ) { handle.elem = null; } // removeData also checks for emptiness and clears the expando if empty // so use it instead of delete jQuery.removeData( elem, [ "events", "handle" ], true ); } }, // Events that are safe to short-circuit if no handlers are attached. // Native DOM events should not be added, they may have inline handlers. customEvent: { "getData": true, "setData": true, "changeData": true }, trigger: function( event, data, elem, onlyHandlers ) { // Don't do events on text and comment nodes if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { return; } // Event object or event type var type = event.type || event, namespaces = [], cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; // focus/blur morphs to focusin/out; ensure we're not firing them right now if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } if ( type.indexOf( "!" ) >= 0 ) { // Exclusive events trigger only for the exact event (no namespaces) type = type.slice(0, -1); exclusive = true; } if ( type.indexOf( "." ) >= 0 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { // No jQuery handlers for this event type, and it can't have inline handlers return; } // Caller can pass in an Event, Object, or just an event type string event = typeof event === "object" ? // jQuery.Event object event[ jQuery.expando ] ? event : // Object literal new jQuery.Event( type, event ) : // Just the event type (string) new jQuery.Event( type ); event.type = type; event.isTrigger = true; event.exclusive = exclusive; event.namespace = namespaces.join( "." ); event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; // Handle a global trigger if ( !elem ) { // TODO: Stop taunting the data cache; remove global events and always attach to document cache = jQuery.cache; for ( i in cache ) { if ( cache[ i ].events && cache[ i ].events[ type ] ) { jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); } } return; } // Clean up the event in case it is being reused event.result = undefined; if ( !event.target ) { event.target = elem; } // Clone any incoming data and prepend the event, creating the handler arg list data = data != null ? jQuery.makeArray( data ) : []; data.unshift( event ); // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if ( special.trigger && special.trigger.apply( elem, data ) === false ) { return; } // Determine event propagation path in advance, per W3C events spec (#9951) // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) eventPath = [[ elem, special.bindType || type ]]; if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type; cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; old = null; for ( ; cur; cur = cur.parentNode ) { eventPath.push([ cur, bubbleType ]); old = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( old && old === elem.ownerDocument ) { eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); } } // Fire handlers on the event path for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { cur = eventPath[i][0]; event.type = eventPath[i][1]; handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } // Note that this is a bare JS function and not a jQuery handler handle = ontype && cur[ ontype ]; if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { event.preventDefault(); } } event.type = type; // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { // Call a native DOM method on the target with the same name name as the event. // Can't use an .isFunction() check here because IE6/7 fails that test. // Don't do default actions on window, that's where global variables be (#6170) // IE<9 dies on focus/blur to hidden element (#1486) if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method old = elem[ ontype ]; if ( old ) { elem[ ontype ] = null; } // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; elem[ type ](); jQuery.event.triggered = undefined; if ( old ) { elem[ ontype ] = old; } } } } return event.result; }, dispatch: function( event ) { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event || window.event ); var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), delegateCount = handlers.delegateCount, args = [].slice.call( arguments, 0 ), run_all = !event.exclusive && !event.namespace, handlerQueue = [], i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[0] = event; event.delegateTarget = this; // Determine handlers that should run if there are delegated events // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861) if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) { // Pregenerate a single jQuery object for reuse with .is() jqcur = jQuery(this); jqcur.context = this.ownerDocument || this; for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { selMatch = {}; matches = []; jqcur[0] = cur; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; sel = handleObj.selector; if ( selMatch[ sel ] === undefined ) { selMatch[ sel ] = ( handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) ); } if ( selMatch[ sel ] ) { matches.push( handleObj ); } } if ( matches.length ) { handlerQueue.push({ elem: cur, matches: matches }); } } } // Add the remaining (directly-bound) handlers if ( handlers.length > delegateCount ) { handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); } // Run delegates first; they may want to stop propagation beneath us for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { matched = handlerQueue[ i ]; event.currentTarget = matched.elem; for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { handleObj = matched.matches[ j ]; // Triggered event must either 1) be non-exclusive and have no namespace, or // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { event.data = handleObj.data; event.handleObj = handleObj; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply( matched.elem, args ); if ( ret !== undefined ) { event.result = ret; if ( ret === false ) { event.preventDefault(); event.stopPropagation(); } } } } } return event.result; }, // Includes some event props shared by KeyEvent and MouseEvent // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), fixHooks: {}, keyHooks: { props: "char charCode key keyCode".split(" "), filter: function( event, original ) { // Add which for key events if ( event.which == null ) { event.which = original.charCode != null ? original.charCode : original.keyCode; } return event; } }, mouseHooks: { props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), filter: function( event, original ) { var eventDoc, doc, body, button = original.button, fromElement = original.fromElement; // Calculate pageX/Y if missing and clientX/Y available if ( event.pageX == null && original.clientX != null ) { eventDoc = event.target.ownerDocument || document; doc = eventDoc.documentElement; body = eventDoc.body; event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); } // Add relatedTarget, if necessary if ( !event.relatedTarget && fromElement ) { event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; } // Add which for click: 1 === left; 2 === middle; 3 === right // Note: button is not normalized, so don't use it if ( !event.which && button !== undefined ) { event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); } return event; } }, fix: function( event ) { if ( event[ jQuery.expando ] ) { return event; } // Create a writable copy of the event object and normalize some properties var i, prop, originalEvent = event, fixHook = jQuery.event.fixHooks[ event.type ] || {}, copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; event = jQuery.Event( originalEvent ); for ( i = copy.length; i; ) { prop = copy[ --i ]; event[ prop ] = originalEvent[ prop ]; } // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) if ( !event.target ) { event.target = originalEvent.srcElement || document; } // Target should not be a text node (#504, Safari) if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; } // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) if ( event.metaKey === undefined ) { event.metaKey = event.ctrlKey; } return fixHook.filter? fixHook.filter( event, originalEvent ) : event; }, special: { ready: { // Make sure the ready event is setup setup: jQuery.bindReady }, load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true }, focus: { delegateType: "focusin" }, blur: { delegateType: "focusout" }, beforeunload: { setup: function( data, namespaces, eventHandle ) { // We only want to do this special case on windows if ( jQuery.isWindow( this ) ) { this.onbeforeunload = eventHandle; } }, teardown: function( namespaces, eventHandle ) { if ( this.onbeforeunload === eventHandle ) { this.onbeforeunload = null; } } } }, simulate: function( type, elem, event, bubble ) { // Piggyback on a donor event to simulate a different one. // Fake originalEvent to avoid donor's stopPropagation, but if the // simulated event prevents default then we do the same on the donor. var e = jQuery.extend( new jQuery.Event(), event, { type: type, isSimulated: true, originalEvent: {} } ); if ( bubble ) { jQuery.event.trigger( e, null, elem ); } else { jQuery.event.dispatch.call( elem, e ); } if ( e.isDefaultPrevented() ) { event.preventDefault(); } } }; // Some plugins are using, but it's undocumented/deprecated and will be removed. // The 1.7 special event interface should provide all the hooks needed now. jQuery.event.handle = jQuery.event.dispatch; jQuery.removeEvent = document.removeEventListener ? function( elem, type, handle ) { if ( elem.removeEventListener ) { elem.removeEventListener( type, handle, false ); } } : function( elem, type, handle ) { if ( elem.detachEvent ) { elem.detachEvent( "on" + type, handle ); } }; jQuery.Event = function( src, props ) { // Allow instantiation without the 'new' keyword if ( !(this instanceof jQuery.Event) ) { return new jQuery.Event( src, props ); } // Event object if ( src && src.type ) { this.originalEvent = src; this.type = src.type; // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; // Event type } else { this.type = src; } // Put explicitly provided properties onto the event object if ( props ) { jQuery.extend( this, props ); } // Create a timestamp if incoming event doesn't have one this.timeStamp = src && src.timeStamp || jQuery.now(); // Mark it as fixed this[ jQuery.expando ] = true; }; function returnFalse() { return false; } function returnTrue() { return true; } // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { preventDefault: function() { this.isDefaultPrevented = returnTrue; var e = this.originalEvent; if ( !e ) { return; } // if preventDefault exists run it on the original event if ( e.preventDefault ) { e.preventDefault(); // otherwise set the returnValue property of the original event to false (IE) } else { e.returnValue = false; } }, stopPropagation: function() { this.isPropagationStopped = returnTrue; var e = this.originalEvent; if ( !e ) { return; } // if stopPropagation exists run it on the original event if ( e.stopPropagation ) { e.stopPropagation(); } // otherwise set the cancelBubble property of the original event to true (IE) e.cancelBubble = true; }, stopImmediatePropagation: function() { this.isImmediatePropagationStopped = returnTrue; this.stopPropagation(); }, isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse }; // Create mouseenter/leave events using mouseover/out and event-time checks jQuery.each({ mouseenter: "mouseover", mouseleave: "mouseout" }, function( orig, fix ) { jQuery.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function( event ) { var target = this, related = event.relatedTarget, handleObj = event.handleObj, selector = handleObj.selector, ret; // For mousenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || (related !== target && !jQuery.contains( target, related )) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; }); // IE submit delegation if ( !jQuery.support.submitBubbles ) { jQuery.event.special.submit = { setup: function() { // Only need this for delegated form submit events if ( jQuery.nodeName( this, "form" ) ) { return false; } // Lazy-add a submit handler when a descendant form may potentially be submitted jQuery.event.add( this, "click._submit keypress._submit", function( e ) { // Node name check avoids a VML-related crash in IE (#9807) var elem = e.target, form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; if ( form && !form._submit_attached ) { jQuery.event.add( form, "submit._submit", function( event ) { // If form was submitted by the user, bubble the event up the tree if ( this.parentNode && !event.isTrigger ) { jQuery.event.simulate( "submit", this.parentNode, event, true ); } }); form._submit_attached = true; } }); // return undefined since we don't need an event listener }, teardown: function() { // Only need this for delegated form submit events if ( jQuery.nodeName( this, "form" ) ) { return false; } // Remove delegated handlers; cleanData eventually reaps submit handlers attached above jQuery.event.remove( this, "._submit" ); } }; } // IE change delegation and checkbox/radio fix if ( !jQuery.support.changeBubbles ) { jQuery.event.special.change = { setup: function() { if ( rformElems.test( this.nodeName ) ) { // IE doesn't fire change on a check/radio until blur; trigger it on click // after a propertychange. Eat the blur-change in special.change.handle. // This still fires onchange a second time for check/radio after blur. if ( this.type === "checkbox" || this.type === "radio" ) { jQuery.event.add( this, "propertychange._change", function( event ) { if ( event.originalEvent.propertyName === "checked" ) { this._just_changed = true; } }); jQuery.event.add( this, "click._change", function( event ) { if ( this._just_changed && !event.isTrigger ) { this._just_changed = false; jQuery.event.simulate( "change", this, event, true ); } }); } return false; } // Delegated event; lazy-add a change handler on descendant inputs jQuery.event.add( this, "beforeactivate._change", function( e ) { var elem = e.target; if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { jQuery.event.add( elem, "change._change", function( event ) { if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { jQuery.event.simulate( "change", this.parentNode, event, true ); } }); elem._change_attached = true; } }); }, handle: function( event ) { var elem = event.target; // Swallow native change events from checkbox/radio, we already triggered them above if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { return event.handleObj.handler.apply( this, arguments ); } }, teardown: function() { jQuery.event.remove( this, "._change" ); return rformElems.test( this.nodeName ); } }; } // Create "bubbling" focus and blur events if ( !jQuery.support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler while someone wants focusin/focusout var attaches = 0, handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); }; jQuery.event.special[ fix ] = { setup: function() { if ( attaches++ === 0 ) { document.addEventListener( orig, handler, true ); } }, teardown: function() { if ( --attaches === 0 ) { document.removeEventListener( orig, handler, true ); } } }; }); } jQuery.fn.extend({ on: function( types, selector, data, fn, /*INTERNAL*/ one ) { var origFn, type; // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // ( types-Object, data ) data = selector; selector = undefined; } for ( type in types ) { this.on( type, selector, data, types[ type ], one ); } return this; } if ( data == null && fn == null ) { // ( types, fn ) fn = selector; data = selector = undefined; } else if ( fn == null ) { if ( typeof selector === "string" ) { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { return this; } if ( one === 1 ) { origFn = fn; fn = function( event ) { // Can use an empty set, since event contains the info jQuery().off( event ); return origFn.apply( this, arguments ); }; // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } return this.each( function() { jQuery.event.add( this, types, fn, data, selector ); }); }, one: function( types, selector, data, fn ) { return this.on.call( this, types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event var handleObj = types.handleObj; jQuery( types.delegateTarget ).off( handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { // ( types-object [, selector] ) for ( var type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) { // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) { fn = returnFalse; } return this.each(function() { jQuery.event.remove( this, types, fn, selector ); }); }, bind: function( types, data, fn ) { return this.on( types, null, data, fn ); }, unbind: function( types, fn ) { return this.off( types, null, fn ); }, live: function( types, data, fn ) { jQuery( this.context ).on( types, this.selector, data, fn ); return this; }, die: function( types, fn ) { jQuery( this.context ).off( types, this.selector || "**", fn ); return this; }, delegate: function( selector, types, data, fn ) { return this.on( types, selector, data, fn ); }, undelegate: function( selector, types, fn ) { // ( namespace ) or ( selector, types [, fn] ) return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); }, trigger: function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); }); }, triggerHandler: function( type, data ) { if ( this[0] ) { return jQuery.event.trigger( type, data, this[0], true ); } }, toggle: function( fn ) { // Save reference to arguments for access in closure var args = arguments, guid = fn.guid || jQuery.guid++, i = 0, toggler = function( event ) { // Figure out which function to execute var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); // Make sure that clicks stop event.preventDefault(); // and execute the function return args[ lastToggle ].apply( this, arguments ) || false; }; // link all the functions, so any of them can unbind this click handler toggler.guid = guid; while ( i < args.length ) { args[ i++ ].guid = guid; } return this.click( toggler ); }, hover: function( fnOver, fnOut ) { return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); } }); jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { // Handle event binding jQuery.fn[ name ] = function( data, fn ) { if ( fn == null ) { fn = data; data = null; } return arguments.length > 0 ? this.on( name, null, data, fn ) : this.trigger( name ); }; if ( jQuery.attrFn ) { jQuery.attrFn[ name ] = true; } if ( rkeyEvent.test( name ) ) { jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; } if ( rmouseEvent.test( name ) ) { jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; } }); /*! * Sizzle CSS Selector Engine * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * More information: http://sizzlejs.com/ */ (function(){ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, expando = "sizcache" + (Math.random() + '').replace('.', ''), done = 0, toString = Object.prototype.toString, hasDuplicate = false, baseHasDuplicate = true, rBackslash = /\\/g, rReturn = /\r\n/g, rNonWord = /\W/; // Here we check if the JavaScript engine is using some sort of // optimization where it does not always call our comparision // function. If that is the case, discard the hasDuplicate value. // Thus far that includes Google Chrome. [0, 0].sort(function() { baseHasDuplicate = false; return 0; }); var Sizzle = function( selector, context, results, seed ) { results = results || []; context = context || document; var origContext = context; if ( context.nodeType !== 1 && context.nodeType !== 9 ) { return []; } if ( !selector || typeof selector !== "string" ) { return results; } var m, set, checkSet, extra, ret, cur, pop, i, prune = true, contextXML = Sizzle.isXML( context ), parts = [], soFar = selector; // Reset the position of the chunker regexp (start from head) do { chunker.exec( "" ); m = chunker.exec( soFar ); if ( m ) { soFar = m[3]; parts.push( m[1] ); if ( m[2] ) { extra = m[3]; break; } } } while ( m ); if ( parts.length > 1 && origPOS.exec( selector ) ) { if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { set = posProcess( parts[0] + parts[1], context, seed ); } else { set = Expr.relative[ parts[0] ] ? [ context ] : Sizzle( parts.shift(), context ); while ( parts.length ) { selector = parts.shift(); if ( Expr.relative[ selector ] ) { selector += parts.shift(); } set = posProcess( selector, set, seed ); } } } else { // Take a shortcut and set the context if the root selector is an ID // (but not if it'll be faster if the inner selector is an ID) if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { ret = Sizzle.find( parts.shift(), context, contextXML ); context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; } if ( context ) { ret = seed ? { expr: parts.pop(), set: makeArray(seed) } : Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; if ( parts.length > 0 ) { checkSet = makeArray( set ); } else { prune = false; } while ( parts.length ) { cur = parts.pop(); pop = cur; if ( !Expr.relative[ cur ] ) { cur = ""; } else { pop = parts.pop(); } if ( pop == null ) { pop = context; } Expr.relative[ cur ]( checkSet, pop, contextXML ); } } else { checkSet = parts = []; } } if ( !checkSet ) { checkSet = set; } if ( !checkSet ) { Sizzle.error( cur || selector ); } if ( toString.call(checkSet) === "[object Array]" ) { if ( !prune ) { results.push.apply( results, checkSet ); } else if ( context && context.nodeType === 1 ) { for ( i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { results.push( set[i] ); } } } else { for ( i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && checkSet[i].nodeType === 1 ) { results.push( set[i] ); } } } } else { makeArray( checkSet, results ); } if ( extra ) { Sizzle( extra, origContext, results, seed ); Sizzle.uniqueSort( results ); } return results; }; Sizzle.uniqueSort = function( results ) { if ( sortOrder ) { hasDuplicate = baseHasDuplicate; results.sort( sortOrder ); if ( hasDuplicate ) { for ( var i = 1; i < results.length; i++ ) { if ( results[i] === results[ i - 1 ] ) { results.splice( i--, 1 ); } } } } return results; }; Sizzle.matches = function( expr, set ) { return Sizzle( expr, null, null, set ); }; Sizzle.matchesSelector = function( node, expr ) { return Sizzle( expr, null, null, [node] ).length > 0; }; Sizzle.find = function( expr, context, isXML ) { var set, i, len, match, type, left; if ( !expr ) { return []; } for ( i = 0, len = Expr.order.length; i < len; i++ ) { type = Expr.order[i]; if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { left = match[1]; match.splice( 1, 1 ); if ( left.substr( left.length - 1 ) !== "\\" ) { match[1] = (match[1] || "").replace( rBackslash, "" ); set = Expr.find[ type ]( match, context, isXML ); if ( set != null ) { expr = expr.replace( Expr.match[ type ], "" ); break; } } } } if ( !set ) { set = typeof context.getElementsByTagName !== "undefined" ? context.getElementsByTagName( "*" ) : []; } return { set: set, expr: expr }; }; Sizzle.filter = function( expr, set, inplace, not ) { var match, anyFound, type, found, item, filter, left, i, pass, old = expr, result = [], curLoop = set, isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); while ( expr && set.length ) { for ( type in Expr.filter ) { if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { filter = Expr.filter[ type ]; left = match[1]; anyFound = false; match.splice(1,1); if ( left.substr( left.length - 1 ) === "\\" ) { continue; } if ( curLoop === result ) { result = []; } if ( Expr.preFilter[ type ] ) { match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); if ( !match ) { anyFound = found = true; } else if ( match === true ) { continue; } } if ( match ) { for ( i = 0; (item = curLoop[i]) != null; i++ ) { if ( item ) { found = filter( item, match, i, curLoop ); pass = not ^ found; if ( inplace && found != null ) { if ( pass ) { anyFound = true; } else { curLoop[i] = false; } } else if ( pass ) { result.push( item ); anyFound = true; } } } } if ( found !== undefined ) { if ( !inplace ) { curLoop = result; } expr = expr.replace( Expr.match[ type ], "" ); if ( !anyFound ) { return []; } break; } } } // Improper expression if ( expr === old ) { if ( anyFound == null ) { Sizzle.error( expr ); } else { break; } } old = expr; } return curLoop; }; Sizzle.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; /** * Utility function for retreiving the text value of an array of DOM nodes * @param {Array|Element} elem */ var getText = Sizzle.getText = function( elem ) { var i, node, nodeType = elem.nodeType, ret = ""; if ( nodeType ) { if ( nodeType === 1 || nodeType === 9 ) { // Use textContent || innerText for elements if ( typeof elem.textContent === 'string' ) { return elem.textContent; } else if ( typeof elem.innerText === 'string' ) { // Replace IE's carriage returns return elem.innerText.replace( rReturn, '' ); } else { // Traverse it's children for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } } else { // If no nodeType, this is expected to be an array for ( i = 0; (node = elem[i]); i++ ) { // Do not traverse comment nodes if ( node.nodeType !== 8 ) { ret += getText( node ); } } } return ret; }; var Expr = Sizzle.selectors = { order: [ "ID", "NAME", "TAG" ], match: { ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ }, leftMatch: {}, attrMap: { "class": "className", "for": "htmlFor" }, attrHandle: { href: function( elem ) { return elem.getAttribute( "href" ); }, type: function( elem ) { return elem.getAttribute( "type" ); } }, relative: { "+": function(checkSet, part){ var isPartStr = typeof part === "string", isTag = isPartStr && !rNonWord.test( part ), isPartStrNotTag = isPartStr && !isTag; if ( isTag ) { part = part.toLowerCase(); } for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { if ( (elem = checkSet[i]) ) { while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? elem || false : elem === part; } } if ( isPartStrNotTag ) { Sizzle.filter( part, checkSet, true ); } }, ">": function( checkSet, part ) { var elem, isPartStr = typeof part === "string", i = 0, l = checkSet.length; if ( isPartStr && !rNonWord.test( part ) ) { part = part.toLowerCase(); for ( ; i < l; i++ ) { elem = checkSet[i]; if ( elem ) { var parent = elem.parentNode; checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; } } } else { for ( ; i < l; i++ ) { elem = checkSet[i]; if ( elem ) { checkSet[i] = isPartStr ? elem.parentNode : elem.parentNode === part; } } if ( isPartStr ) { Sizzle.filter( part, checkSet, true ); } } }, "": function(checkSet, part, isXML){ var nodeCheck, doneName = done++, checkFn = dirCheck; if ( typeof part === "string" && !rNonWord.test( part ) ) { part = part.toLowerCase(); nodeCheck = part; checkFn = dirNodeCheck; } checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); }, "~": function( checkSet, part, isXML ) { var nodeCheck, doneName = done++, checkFn = dirCheck; if ( typeof part === "string" && !rNonWord.test( part ) ) { part = part.toLowerCase(); nodeCheck = part; checkFn = dirNodeCheck; } checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); } }, find: { ID: function( match, context, isXML ) { if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 return m && m.parentNode ? [m] : []; } }, NAME: function( match, context ) { if ( typeof context.getElementsByName !== "undefined" ) { var ret = [], results = context.getElementsByName( match[1] ); for ( var i = 0, l = results.length; i < l; i++ ) { if ( results[i].getAttribute("name") === match[1] ) { ret.push( results[i] ); } } return ret.length === 0 ? null : ret; } }, TAG: function( match, context ) { if ( typeof context.getElementsByTagName !== "undefined" ) { return context.getElementsByTagName( match[1] ); } } }, preFilter: { CLASS: function( match, curLoop, inplace, result, not, isXML ) { match = " " + match[1].replace( rBackslash, "" ) + " "; if ( isXML ) { return match; } for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { if ( elem ) { if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { if ( !inplace ) { result.push( elem ); } } else if ( inplace ) { curLoop[i] = false; } } } return false; }, ID: function( match ) { return match[1].replace( rBackslash, "" ); }, TAG: function( match, curLoop ) { return match[1].replace( rBackslash, "" ).toLowerCase(); }, CHILD: function( match ) { if ( match[1] === "nth" ) { if ( !match[2] ) { Sizzle.error( match[0] ); } match[2] = match[2].replace(/^\+|\s*/g, ''); // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); // calculate the numbers (first)n+(last) including if they are negative match[2] = (test[1] + (test[2] || 1)) - 0; match[3] = test[3] - 0; } else if ( match[2] ) { Sizzle.error( match[0] ); } // TODO: Move to normal caching system match[0] = done++; return match; }, ATTR: function( match, curLoop, inplace, result, not, isXML ) { var name = match[1] = match[1].replace( rBackslash, "" ); if ( !isXML && Expr.attrMap[name] ) { match[1] = Expr.attrMap[name]; } // Handle if an un-quoted value was used match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); if ( match[2] === "~=" ) { match[4] = " " + match[4] + " "; } return match; }, PSEUDO: function( match, curLoop, inplace, result, not ) { if ( match[1] === "not" ) { // If we're dealing with a complex expression, or a simple one if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { match[3] = Sizzle(match[3], null, null, curLoop); } else { var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); if ( !inplace ) { result.push.apply( result, ret ); } return false; } } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { return true; } return match; }, POS: function( match ) { match.unshift( true ); return match; } }, filters: { enabled: function( elem ) { return elem.disabled === false && elem.type !== "hidden"; }, disabled: function( elem ) { return elem.disabled === true; }, checked: function( elem ) { return elem.checked === true; }, selected: function( elem ) { // Accessing this property makes selected-by-default // options in Safari work properly if ( elem.parentNode ) { elem.parentNode.selectedIndex; } return elem.selected === true; }, parent: function( elem ) { return !!elem.firstChild; }, empty: function( elem ) { return !elem.firstChild; }, has: function( elem, i, match ) { return !!Sizzle( match[3], elem ).length; }, header: function( elem ) { return (/h\d/i).test( elem.nodeName ); }, text: function( elem ) { var attr = elem.getAttribute( "type" ), type = elem.type; // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) // use getAttribute instead to test this case return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); }, radio: function( elem ) { return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; }, checkbox: function( elem ) { return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; }, file: function( elem ) { return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; }, password: function( elem ) { return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; }, submit: function( elem ) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && "submit" === elem.type; }, image: function( elem ) { return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; }, reset: function( elem ) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && "reset" === elem.type; }, button: function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && "button" === elem.type || name === "button"; }, input: function( elem ) { return (/input|select|textarea|button/i).test( elem.nodeName ); }, focus: function( elem ) { return elem === elem.ownerDocument.activeElement; } }, setFilters: { first: function( elem, i ) { return i === 0; }, last: function( elem, i, match, array ) { return i === array.length - 1; }, even: function( elem, i ) { return i % 2 === 0; }, odd: function( elem, i ) { return i % 2 === 1; }, lt: function( elem, i, match ) { return i < match[3] - 0; }, gt: function( elem, i, match ) { return i > match[3] - 0; }, nth: function( elem, i, match ) { return match[3] - 0 === i; }, eq: function( elem, i, match ) { return match[3] - 0 === i; } }, filter: { PSEUDO: function( elem, match, i, array ) { var name = match[1], filter = Expr.filters[ name ]; if ( filter ) { return filter( elem, i, match, array ); } else if ( name === "contains" ) { return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; } else if ( name === "not" ) { var not = match[3]; for ( var j = 0, l = not.length; j < l; j++ ) { if ( not[j] === elem ) { return false; } } return true; } else { Sizzle.error( name ); } }, CHILD: function( elem, match ) { var first, last, doneName, parent, cache, count, diff, type = match[1], node = elem; switch ( type ) { case "only": case "first": while ( (node = node.previousSibling) ) { if ( node.nodeType === 1 ) { return false; } } if ( type === "first" ) { return true; } node = elem; case "last": while ( (node = node.nextSibling) ) { if ( node.nodeType === 1 ) { return false; } } return true; case "nth": first = match[2]; last = match[3]; if ( first === 1 && last === 0 ) { return true; } doneName = match[0]; parent = elem.parentNode; if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { count = 0; for ( node = parent.firstChild; node; node = node.nextSibling ) { if ( node.nodeType === 1 ) { node.nodeIndex = ++count; } } parent[ expando ] = doneName; } diff = elem.nodeIndex - last; if ( first === 0 ) { return diff === 0; } else { return ( diff % first === 0 && diff / first >= 0 ); } } }, ID: function( elem, match ) { return elem.nodeType === 1 && elem.getAttribute("id") === match; }, TAG: function( elem, match ) { return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; }, CLASS: function( elem, match ) { return (" " + (elem.className || elem.getAttribute("class")) + " ") .indexOf( match ) > -1; }, ATTR: function( elem, match ) { var name = match[1], result = Sizzle.attr ? Sizzle.attr( elem, name ) : Expr.attrHandle[ name ] ? Expr.attrHandle[ name ]( elem ) : elem[ name ] != null ? elem[ name ] : elem.getAttribute( name ), value = result + "", type = match[2], check = match[4]; return result == null ? type === "!=" : !type && Sizzle.attr ? result != null : type === "=" ? value === check : type === "*=" ? value.indexOf(check) >= 0 : type === "~=" ? (" " + value + " ").indexOf(check) >= 0 : !check ? value && result !== false : type === "!=" ? value !== check : type === "^=" ? value.indexOf(check) === 0 : type === "$=" ? value.substr(value.length - check.length) === check : type === "|=" ? value === check || value.substr(0, check.length + 1) === check + "-" : false; }, POS: function( elem, match, i, array ) { var name = match[2], filter = Expr.setFilters[ name ]; if ( filter ) { return filter( elem, i, match, array ); } } } }; var origPOS = Expr.match.POS, fescape = function(all, num){ return "\\" + (num - 0 + 1); }; for ( var type in Expr.match ) { Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); } var makeArray = function( array, results ) { array = Array.prototype.slice.call( array, 0 ); if ( results ) { results.push.apply( results, array ); return results; } return array; }; // Perform a simple check to determine if the browser is capable of // converting a NodeList to an array using builtin methods. // Also verifies that the returned array holds DOM nodes // (which is not the case in the Blackberry browser) try { Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; // Provide a fallback method if it does not work } catch( e ) { makeArray = function( array, results ) { var i = 0, ret = results || []; if ( toString.call(array) === "[object Array]" ) { Array.prototype.push.apply( ret, array ); } else { if ( typeof array.length === "number" ) { for ( var l = array.length; i < l; i++ ) { ret.push( array[i] ); } } else { for ( ; array[i]; i++ ) { ret.push( array[i] ); } } } return ret; }; } var sortOrder, siblingCheck; if ( document.documentElement.compareDocumentPosition ) { sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; return 0; } if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { return a.compareDocumentPosition ? -1 : 1; } return a.compareDocumentPosition(b) & 4 ? -1 : 1; }; } else { sortOrder = function( a, b ) { // The nodes are identical, we can exit early if ( a === b ) { hasDuplicate = true; return 0; // Fallback to using sourceIndex (in IE) if it's available on both nodes } else if ( a.sourceIndex && b.sourceIndex ) { return a.sourceIndex - b.sourceIndex; } var al, bl, ap = [], bp = [], aup = a.parentNode, bup = b.parentNode, cur = aup; // If the nodes are siblings (or identical) we can do a quick check if ( aup === bup ) { return siblingCheck( a, b ); // If no parents were found then the nodes are disconnected } else if ( !aup ) { return -1; } else if ( !bup ) { return 1; } // Otherwise they're somewhere else in the tree so we need // to build up a full list of the parentNodes for comparison while ( cur ) { ap.unshift( cur ); cur = cur.parentNode; } cur = bup; while ( cur ) { bp.unshift( cur ); cur = cur.parentNode; } al = ap.length; bl = bp.length; // Start walking down the tree looking for a discrepancy for ( var i = 0; i < al && i < bl; i++ ) { if ( ap[i] !== bp[i] ) { return siblingCheck( ap[i], bp[i] ); } } // We ended someplace up the tree so do a sibling check return i === al ? siblingCheck( a, bp[i], -1 ) : siblingCheck( ap[i], b, 1 ); }; siblingCheck = function( a, b, ret ) { if ( a === b ) { return ret; } var cur = a.nextSibling; while ( cur ) { if ( cur === b ) { return -1; } cur = cur.nextSibling; } return 1; }; } // Check to see if the browser returns elements by name when // querying by getElementById (and provide a workaround) (function(){ // We're going to inject a fake input element with a specified name var form = document.createElement("div"), id = "script" + (new Date()).getTime(), root = document.documentElement; form.innerHTML = ""; // Inject it into the root element, check its status, and remove it quickly root.insertBefore( form, root.firstChild ); // The workaround has to do additional checks after a getElementById // Which slows things down for other browsers (hence the branching) if ( document.getElementById( id ) ) { Expr.find.ID = function( match, context, isXML ) { if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; } }; Expr.filter.ID = function( elem, match ) { var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return elem.nodeType === 1 && node && node.nodeValue === match; }; } root.removeChild( form ); // release memory in IE root = form = null; })(); (function(){ // Check to see if the browser returns only elements // when doing getElementsByTagName("*") // Create a fake element var div = document.createElement("div"); div.appendChild( document.createComment("") ); // Make sure no comments are found if ( div.getElementsByTagName("*").length > 0 ) { Expr.find.TAG = function( match, context ) { var results = context.getElementsByTagName( match[1] ); // Filter out possible comments if ( match[1] === "*" ) { var tmp = []; for ( var i = 0; results[i]; i++ ) { if ( results[i].nodeType === 1 ) { tmp.push( results[i] ); } } results = tmp; } return results; }; } // Check to see if an attribute returns normalized href attributes div.innerHTML = ""; if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && div.firstChild.getAttribute("href") !== "#" ) { Expr.attrHandle.href = function( elem ) { return elem.getAttribute( "href", 2 ); }; } // release memory in IE div = null; })(); if ( document.querySelectorAll ) { (function(){ var oldSizzle = Sizzle, div = document.createElement("div"), id = "__sizzle__"; div.innerHTML = "

"; // Safari can't handle uppercase or unicode characters when // in quirks mode. if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { return; } Sizzle = function( query, context, extra, seed ) { context = context || document; // Only use querySelectorAll on non-XML documents // (ID selectors don't work in non-HTML documents) if ( !seed && !Sizzle.isXML(context) ) { // See if we find a selector to speed up var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { // Speed-up: Sizzle("TAG") if ( match[1] ) { return makeArray( context.getElementsByTagName( query ), extra ); // Speed-up: Sizzle(".CLASS") } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { return makeArray( context.getElementsByClassName( match[2] ), extra ); } } if ( context.nodeType === 9 ) { // Speed-up: Sizzle("body") // The body element only exists once, optimize finding it if ( query === "body" && context.body ) { return makeArray( [ context.body ], extra ); // Speed-up: Sizzle("#ID") } else if ( match && match[3] ) { var elem = context.getElementById( match[3] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id === match[3] ) { return makeArray( [ elem ], extra ); } } else { return makeArray( [], extra ); } } try { return makeArray( context.querySelectorAll(query), extra ); } catch(qsaError) {} // qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { var oldContext = context, old = context.getAttribute( "id" ), nid = old || id, hasParent = context.parentNode, relativeHierarchySelector = /^\s*[+~]/.test( query ); if ( !old ) { context.setAttribute( "id", nid ); } else { nid = nid.replace( /'/g, "\\$&" ); } if ( relativeHierarchySelector && hasParent ) { context = context.parentNode; } try { if ( !relativeHierarchySelector || hasParent ) { return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); } } catch(pseudoError) { } finally { if ( !old ) { oldContext.removeAttribute( "id" ); } } } } return oldSizzle(query, context, extra, seed); }; for ( var prop in oldSizzle ) { Sizzle[ prop ] = oldSizzle[ prop ]; } // release memory in IE div = null; })(); } (function(){ var html = document.documentElement, matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; if ( matches ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9 fails this) var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), pseudoWorks = false; try { // This should fail with an exception // Gecko does not error, returns false instead matches.call( document.documentElement, "[test!='']:sizzle" ); } catch( pseudoError ) { pseudoWorks = true; } Sizzle.matchesSelector = function( node, expr ) { // Make sure that attribute selectors are quoted expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); if ( !Sizzle.isXML( node ) ) { try { if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { var ret = matches.call( node, expr ); // IE 9's matchesSelector returns false on disconnected nodes if ( ret || !disconnectedMatch || // As well, disconnected nodes are said to be in a document // fragment in IE 9, so check for that node.document && node.document.nodeType !== 11 ) { return ret; } } } catch(e) {} } return Sizzle(expr, null, null, [node]).length > 0; }; } })(); (function(){ var div = document.createElement("div"); div.innerHTML = "
"; // Opera can't find a second classname (in 9.6) // Also, make sure that getElementsByClassName actually exists if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { return; } // Safari caches class attributes, doesn't catch changes (in 3.2) div.lastChild.className = "e"; if ( div.getElementsByClassName("e").length === 1 ) { return; } Expr.order.splice(1, 0, "CLASS"); Expr.find.CLASS = function( match, context, isXML ) { if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { return context.getElementsByClassName(match[1]); } }; // release memory in IE div = null; })(); function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { var match = false; elem = elem[dir]; while ( elem ) { if ( elem[ expando ] === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 && !isXML ){ elem[ expando ] = doneName; elem.sizset = i; } if ( elem.nodeName.toLowerCase() === cur ) { match = elem; break; } elem = elem[dir]; } checkSet[i] = match; } } } function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { var match = false; elem = elem[dir]; while ( elem ) { if ( elem[ expando ] === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 ) { if ( !isXML ) { elem[ expando ] = doneName; elem.sizset = i; } if ( typeof cur !== "string" ) { if ( elem === cur ) { match = true; break; } } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { match = elem; break; } } elem = elem[dir]; } checkSet[i] = match; } } } if ( document.documentElement.contains ) { Sizzle.contains = function( a, b ) { return a !== b && (a.contains ? a.contains(b) : true); }; } else if ( document.documentElement.compareDocumentPosition ) { Sizzle.contains = function( a, b ) { return !!(a.compareDocumentPosition(b) & 16); }; } else { Sizzle.contains = function() { return false; }; } Sizzle.isXML = function( elem ) { // documentElement is verified for cases where it doesn't yet exist // (such as loading iframes in IE - #4833) var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; }; var posProcess = function( selector, context, seed ) { var match, tmpSet = [], later = "", root = context.nodeType ? [context] : context; // Position selectors must be done after the filter // And so must :not(positional) so we move all PSEUDOs to the end while ( (match = Expr.match.PSEUDO.exec( selector )) ) { later += match[0]; selector = selector.replace( Expr.match.PSEUDO, "" ); } selector = Expr.relative[selector] ? selector + "*" : selector; for ( var i = 0, l = root.length; i < l; i++ ) { Sizzle( selector, root[i], tmpSet, seed ); } return Sizzle.filter( later, tmpSet ); }; // EXPOSE // Override sizzle attribute retrieval Sizzle.attr = jQuery.attr; Sizzle.selectors.attrMap = {}; jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; jQuery.expr[":"] = jQuery.expr.filters; jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; })(); var runtil = /Until$/, rparentsprev = /^(?:parents|prevUntil|prevAll)/, // Note: This RegExp should be improved, or likely pulled from Sizzle rmultiselector = /,/, isSimple = /^.[^:#\[\.,]*$/, slice = Array.prototype.slice, POS = jQuery.expr.match.POS, // methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, next: true, prev: true }; jQuery.fn.extend({ find: function( selector ) { var self = this, i, l; if ( typeof selector !== "string" ) { return jQuery( selector ).filter(function() { for ( i = 0, l = self.length; i < l; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } }); } var ret = this.pushStack( "", "find", selector ), length, n, r; for ( i = 0, l = this.length; i < l; i++ ) { length = ret.length; jQuery.find( selector, this[i], ret ); if ( i > 0 ) { // Make sure that the results are unique for ( n = length; n < ret.length; n++ ) { for ( r = 0; r < length; r++ ) { if ( ret[r] === ret[n] ) { ret.splice(n--, 1); break; } } } } } return ret; }, has: function( target ) { var targets = jQuery( target ); return this.filter(function() { for ( var i = 0, l = targets.length; i < l; i++ ) { if ( jQuery.contains( this, targets[i] ) ) { return true; } } }); }, not: function( selector ) { return this.pushStack( winnow(this, selector, false), "not", selector); }, filter: function( selector ) { return this.pushStack( winnow(this, selector, true), "filter", selector ); }, is: function( selector ) { return !!selector && ( typeof selector === "string" ? // If this is a positional selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". POS.test( selector ) ? jQuery( selector, this.context ).index( this[0] ) >= 0 : jQuery.filter( selector, this ).length > 0 : this.filter( selector ).length > 0 ); }, closest: function( selectors, context ) { var ret = [], i, l, cur = this[0]; // Array (deprecated as of jQuery 1.7) if ( jQuery.isArray( selectors ) ) { var level = 1; while ( cur && cur.ownerDocument && cur !== context ) { for ( i = 0; i < selectors.length; i++ ) { if ( jQuery( cur ).is( selectors[ i ] ) ) { ret.push({ selector: selectors[ i ], elem: cur, level: level }); } } cur = cur.parentNode; level++; } return ret; } // String var pos = POS.test( selectors ) || typeof selectors !== "string" ? jQuery( selectors, context || this.context ) : 0; for ( i = 0, l = this.length; i < l; i++ ) { cur = this[i]; while ( cur ) { if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { ret.push( cur ); break; } else { cur = cur.parentNode; if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { break; } } } } ret = ret.length > 1 ? jQuery.unique( ret ) : ret; return this.pushStack( ret, "closest", selectors ); }, // Determine the position of an element within // the matched set of elements index: function( elem ) { // No argument, return index in parent if ( !elem ) { return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; } // index in selector if ( typeof elem === "string" ) { return jQuery.inArray( this[0], jQuery( elem ) ); } // Locate the position of the desired element return jQuery.inArray( // If it receives a jQuery object, the first element is used elem.jquery ? elem[0] : elem, this ); }, add: function( selector, context ) { var set = typeof selector === "string" ? jQuery( selector, context ) : jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), all = jQuery.merge( this.get(), set ); return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? all : jQuery.unique( all ) ); }, andSelf: function() { return this.add( this.prevObject ); } }); // A painfully simple check to see if an element is disconnected // from a document (should be improved, where feasible). function isDisconnected( node ) { return !node || !node.parentNode || node.parentNode.nodeType === 11; } jQuery.each({ parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { return jQuery.dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { return jQuery.dir( elem, "parentNode", until ); }, next: function( elem ) { return jQuery.nth( elem, 2, "nextSibling" ); }, prev: function( elem ) { return jQuery.nth( elem, 2, "previousSibling" ); }, nextAll: function( elem ) { return jQuery.dir( elem, "nextSibling" ); }, prevAll: function( elem ) { return jQuery.dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { return jQuery.dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { return jQuery.dir( elem, "previousSibling", until ); }, siblings: function( elem ) { return jQuery.sibling( elem.parentNode.firstChild, elem ); }, children: function( elem ) { return jQuery.sibling( elem.firstChild ); }, contents: function( elem ) { return jQuery.nodeName( elem, "iframe" ) ? elem.contentDocument || elem.contentWindow.document : jQuery.makeArray( elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { var ret = jQuery.map( this, fn, until ); if ( !runtil.test( name ) ) { selector = until; } if ( selector && typeof selector === "string" ) { ret = jQuery.filter( selector, ret ); } ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { ret = ret.reverse(); } return this.pushStack( ret, name, slice.call( arguments ).join(",") ); }; }); jQuery.extend({ filter: function( expr, elems, not ) { if ( not ) { expr = ":not(" + expr + ")"; } return elems.length === 1 ? jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : jQuery.find.matches(expr, elems); }, dir: function( elem, dir, until ) { var matched = [], cur = elem[ dir ]; while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { if ( cur.nodeType === 1 ) { matched.push( cur ); } cur = cur[dir]; } return matched; }, nth: function( cur, result, dir, elem ) { result = result || 1; var num = 0; for ( ; cur; cur = cur[dir] ) { if ( cur.nodeType === 1 && ++num === result ) { break; } } return cur; }, sibling: function( n, elem ) { var r = []; for ( ; n; n = n.nextSibling ) { if ( n.nodeType === 1 && n !== elem ) { r.push( n ); } } return r; } }); // Implement the identical functionality for filter and not function winnow( elements, qualifier, keep ) { // Can't pass null or undefined to indexOf in Firefox 4 // Set to 0 to skip string check qualifier = qualifier || 0; if ( jQuery.isFunction( qualifier ) ) { return jQuery.grep(elements, function( elem, i ) { var retVal = !!qualifier.call( elem, i, elem ); return retVal === keep; }); } else if ( qualifier.nodeType ) { return jQuery.grep(elements, function( elem, i ) { return ( elem === qualifier ) === keep; }); } else if ( typeof qualifier === "string" ) { var filtered = jQuery.grep(elements, function( elem ) { return elem.nodeType === 1; }); if ( isSimple.test( qualifier ) ) { return jQuery.filter(qualifier, filtered, !keep); } else { qualifier = jQuery.filter( qualifier, filtered ); } } return jQuery.grep(elements, function( elem, i ) { return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; }); } function createSafeFragment( document ) { var list = nodeNames.split( "|" ), safeFrag = document.createDocumentFragment(); if ( safeFrag.createElement ) { while ( list.length ) { safeFrag.createElement( list.pop() ); } } return safeFrag; } var nodeNames = "abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|" + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, rleadingWhitespace = /^\s+/, rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, rtagName = /<([\w:]+)/, rtbody = /", "" ], legend: [ 1, "
", "
" ], thead: [ 1, "", "
" ], tr: [ 2, "", "
" ], td: [ 3, "", "
" ], col: [ 2, "", "
" ], area: [ 1, "", "" ], _default: [ 0, "", "" ] }, safeFragment = createSafeFragment( document ); wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; // IE can't serialize and

Previous topic

pyFormex example scripts

Next topic

1. coords — A structured collection of 3D coordinates.

[FSF Associate Member]

Valid XHTML 1.0 Transitional

pyFormex reference manual

Abstract

This is the reference manual for pyFormex 0.8.6. It describes most of the classes and functions defined in the pyFormex modules. It was built automatically from the pyFormex sources and is therefore the ultimate reference document if you want to look up the precise arguments (and their meaning) of any class constructor or function in pyFormex. The Index and Module Index may be helpful in navigating through this document.

This reference manual describes the classes in functions defined in most of the pyFormex modules. It was built automatically from the docstrings in the pyFormex sources. The pyFormex modules are placed in three paths:

  • pyformex contains the core functionality, with most of the geometrical transformations, the pyFormex scripting language and utilities,
  • pyformex/gui contains all the modules that form the interactive graphical user interface,
  • pyformex/plugins contains extensions that are not considered to be essential parts of pyFormex. They usually provide additional functionality for specific applications.

Some of the modules are loaded automatically when pyFormex is started. Currently this is the case with the modules coords, formex, arraytools, script and, if the GUI is used, draw and colors. All the public definitions in these modules are available to pyFormex scripts without explicitly importing them. Also available is the complete numpy namespace, because it is imported by arraytools.

The definitions in the other modules can only be accessed using the normal Python import statements.

Autoloaded modules

The definitions in these modules are always available to your scripts, without the need to explicitely import them.

Other pyFormex core modules

Together with the autoloaded modules, the following modules located under the main pyformex path are considered to belong to the pyformex core functionality.

pyFormex GUI modules

These modules are located under pyformex/gui.

pyFormex plugins

Plugin modules extend the basic pyFormex functions to variety of specific applications. Apart from being located under the pyformex/plugins path, these modules are in no way different from other pyFormex modules.

pyFormex tools

The main pyformex path contains a number of modules that are not considered to be part of the pyFormex core, but are rather tools that were used in the implementation of other modules, but can also be useful elsewhere.

pyformex-0.8.6/pyformex/doc/html/config.html0000644000211500021150000004256711705104245020764 0ustar benebene00000000000000 Configuring pyFormex — pyFormex v0.8.6 documentation

Table Of Contents

Previous topic

pyFormex plugins

Next topic

pyFormex example scripts

[FSF Associate Member]

Valid XHTML 1.0 Transitional

Configuring pyFormex

Many aspects of pyFormex can be configured to better suit the user’s needs and likings. These can range from merely cosmetic changes to important extensions of the functionality. As is written in a scripting language and distributed as source, the user can change every single aspect of the program. And the GNU-GPL license under which the program is distributed guarantees that you have access to the source and are allowed to change it.

Most users however will only want to change minor aspects of the program, and would rather not have to delve into the source to do just that. Therefore we have gathered some items of that users might like to change, into separate files where thay can easily be found. Some of these items can even be set interactivley through the GUI menus.

Often users want to keep their settings between subsequent invocation of the program. To this end, the user preferences have to be stored on file when leaving the program and read back when starting the next time. While it might make sense to distinct between the user’s current settings in the program and his default preferences, the current configuration system of (still under development) does not allow such distinction yet. Still, since the topic is so important to the user and the configuration system in is already quite complex, we tought it was necessary to provide already some information on how to configure . Be aware though that important changes to this system will likely occur.

Configuration files

On startup, reads its configurable data from a number of files. Often there are not less than four configuration files, read in sequence. The settings in each file being read override the value read before. The different configuration files used serve different purposes. On a typical GNU/Linux installation, the following files will be read in sequence:

  • PYFORMEX-INSTALL-PATH/pyformexrc: this file should never be changed , neither by the user nor the administrator. It is there to guarantee that all settings get an adequate default value to allow to correctly start up.
  • /etc/pyformex: this file can be used by the system administrator to make system-wide changes to the installation. This could e.g. be used to give all users at a site access to a common set of scripts or extensions.
  • /.pyformexrc: this is where the user normally stores his own default settings.
  • CURRENT-DIR/.pyformex: if the current working directory from which is started contains a file named .pyformex, it will be read too. This makes it possible to keep different configurations in different directories, depending on the purpose. Thus, one directory might aim at the use of for operating on triangulated surfaces, while another might be intended for pre- and post- processing of Finite Element models.
  • Finally, the --config= command line option provides a way to specify another file with any name to be used as the last configuration file.

On exit,will store the changed settings on the last user configuration file that was read. The first two files mentioned above are system configuration files and will never be changed by the program. A user configuration file will be generated if none existed.

Warning

Currently, when pyFormex exits, it will just dump all the changed configuration (key,value) pairs on the last configuration file, together with the values it read from that file. pyFormex will not detect if any changes were made to that file between reading it and writing back. Therefore, the user should never edit the configuration files directly while pyFormex is still running. Always close the program first!

Syntax of the configuration files

All configuration files are plain text files where each non blank line is one of the following:

  • a comment line, starting with a ‘#’,
  • a section header, of the form ‘[section-name]’,
  • a valid Python instruction.

The configuration file is organized in sections. All lines preceding the first section name refer to the general (unnamed) section.

Any valid Python source line can be used. This allows for quite complex configuration instructions, even importing Python modules. Any line that binds a value to a variable will cause a corresponding configuration variable to be set. The user can edit the configuration files with any text editor, but should make sure the lines are legal Python. Any line can use the previously defined variables, even those defined in previously read files.

In the configuration files, the variable pyformexdir refers to the directory where was installed (and which is also reported by the pyformex --whereami command).

Configuration variables

Many configuration variables can be set interactively from the GUI, and the user may prefer to do it that way. Some variables however can not (yet) be set from th GUI. And real programmers may prefer to do it with an editor anyway. So here are some guidelines for setting some interesting variables. The user may take a look at the installed default configuration file for more examples.

General section

  • syspath = []: Value is a list of path names that will be appended to the Python’s sys.path variable on startup. This enables your scripts to import modules from other than default Python paths.
  • scriptdirs = [ ('Examples',examplesdir), ('MyScripts',myscriptsdir)]: a list of tuples (name,path). On startup, all these paths will be scanned for scripts and these will be added in the menu under an item named name.
  • autorun = '.pyformex.startup': name of a script that will be executed on startup, before any other script (specified on the command line or started from the GUI).
  • editor = 'kedit': sets the name of the editor that will be used for editing pyformex scripts.
  • viewer = 'firefox': sets the name of the html viewer to be used to display the html help screens.
  • browser = 'firefox': sets the name of the browser to be used to access the website.
  • uselib = False: do not use the acceleration library. The default (True) is to use it when it is available.

Section [gui]

  • splash = 'path-to-splash-image.png': full path name of the image to be used as splash image on startup.
  • modebar = True: adds a toolbar with the render mode buttons. Besides True or False, the value can also be one of ‘top’, ‘bottom’, ‘left’ or ‘right’, specifying the placement of the render mode toolbar at the specified window border. Any other value that evaluates True will make the buttons get included in the top toolbar.
  • viewbar = True: adds a toolbar with different view buttons. Possioble values as explained above for modebar.
  • timeoutbutton = True: include the timeout button in the toolbar. The timeout button, when depressed, will cause input widgets to time out after a prespecified delay time. This feature is still experimental.
  • plugins = ['surface_menu', 'formex_menu', 'tools_menu']: a list of plugins to load on startup. This is mainly used to load extra (non-default) menus in the GUI to provide extended functionality. The named plugins should be available in the ‘plugins’ subdirectory of the installation. To autoload user extensions from a different place, the autorun script can be used.
pyformex-0.8.6/pyformex/doc/html/tutorial.html0000644000211500021150000032021111705104257021346 0ustar benebene00000000000000 pyFormex tutorial — pyFormex v0.8.6 documentation

pyFormex tutorial

Abstract

This tutorial will guide you step by step through the most important concepts of the pyFormex scripting language and the pyFormex Graphical User Interface (GUI). It is intended for first time users, giving explicit details of what to do and what to expect as result.

The philosophy

pyFormex is a Python implementation of Formex algebra. Using pyFormex, it is very easy to generate large geometrical models of 3D structures by a sequence of mathematical transformations. It is especially suited for the automated design of spatial structures. But it can also be used for other tasks, like operating on 3D geometry obtained from other sources, or for finite element pre- and postprocessing, or just for creating some nice pictures.

By writing a simple script, a large and complex geometry can be created by copying, translating, rotating, or otherwise transforming geometrical entities. pyFormex will interpret the script and draw what you have created. This is clearly very different from the traditional (mostly interactive) way of creating a geometrical model, like is done in most CAD packages. There are some huge advantages in using pyFormex:

  • It is especially suited for the automated design of spatial frame structures. A dome, an arc, a hypar shell, ..., when constructed as a space frame, can be rather difficult and tedious to draw with a general CAD program; using scripted mathematical transformations however, it may become a trivial task.
  • Using a script makes it very easy to apply changes in the geometry: you simply modify the script and re-execute it. You can easily change the value of a geometrical parameter in any way you want: set it directly, interactively ask it from the user, calculate it from some formula, read it from a file, etcetera. Using CAD, you would have often have to completely redo your drawing work. The power of scripted geometry building is illustrated in figure Same script, different domes: all these domes were created with the same script, but with different values of some parameters.
Three scallop domes

Same script, different domes

  • At times there will be operations that are easier to perform through an interactive Graphical User Interface (GUI). The GUI gives access to many such functions. Especially occasional and untrained users will benefit from it. As everything else in pyFormex, the GUI is completely open and can be modified at will by the user’s application scripts, to provide an interface with either extended or restricted functionality.
  • pyformex scripts are written in the Python programming language. This implies that the scripts are also Python-based. It is a very easy language to learn, and if you are interested in reading more about it, there are good tutorials and beginner’s guides available on the Python website (http://www.python.org/doc). However, if you’re only using Python to write pyFormex scripts, the tutorial you’re reading right now should be enough.

Getting started

  • Start the pyFormex GUI by entering the command pyformex in a terminal. Depending on your instalation, there may also be a menu item in the application menu to start pyFormex, or even a quickstart button in the panel. Using the terminal however can still be useful, especially in the case of errors, because otherwise the GUI might suppress some of the error messages that normally are sent to the terminal.

  • Create a new pyFormex script using the File‣Create new script option, and enter a filename with extension .py. This will open up your favorite editor with a pyFormex script template like the one below.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #        *** pyformex ***
    ##
    ##  Copyright (C) 2011 John Doe (j.doe@somewhere.org) 
    ##  Distributed under the GNU General Public License version 3 or later.
    ##
    ##  This program is free software: you can redistribute it and/or modify
    ##  it under the terms of the GNU General Public License as published by
    ##  the Free Software Foundation, either version 3 of the License, or
    ##  (at your option) any later version.
    ##
    ##  This program is distributed in the hope that it will be useful,
    ##  but WITHOUT ANY WARRANTY; without even the implied warranty of
    ##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ##  GNU General Public License for more details.
    ##
    ##  You should have received a copy of the GNU General Public License
    ##  along with this program.  If not, see http://www.gnu.org/licenses/.
    ##
    
    """Script Template
    
    This is a template file to show the general layout of a pyFormex script.
    In the current version, a pyFormex script should obey the following rules:
    
    - file name extension is '.py'
    - first (comment) line contains 'pyformex'
    
    The script starts by preference with a docstring (like this),
    composed of a short first line, then a blank line and
    one or more lines explaining the intention of the script.
    """
    
    print "This is the pyFormex template script"
    
    # End
    

Note

If the editor does not open, you may need to configure the editor command: see Settings –> Commands.

Make sure you are using an editor that understands Python code. Most modern editors will give you syntax highlighting and help with indentation.

  • The template script shows the typical layout of a pyFormex script:

    • The script starts with a line #!/usr/bin/env pyformex, followed by a number of comments lines (all lines starting with a ‘#’). For the sake of this tutorial, you can just disregard the comments.
    • Then comes a multiline documentation string, contained between two """ delimiters. Read it: it repeats this instructions on how a legal pyFormex script should be structured.
    • Next are the pyFormex instructions: in this case there’s only one line.
    • The script ends with a comment line # End. We recommend you to do this also. It serves as a warning for inadvertent truncation of your file.
  • In the status bar at the bottom of the pyFormex GUI, you will now see the name of the script, together with a green dot. This tells you that the script has been recognized by the system as a pyFormex script, and is ready to run.

  • Execute the script by selecting the File ‣ Play menu option, or by just pushing the button-play button in the toolbar. In the message area just above the status bar, a line is printed announcing the start and end of execution. Any output created by the script during execution will be displayed in between this two lines. As expected, the template script just prints the text from line 16 of the script.

  • Now change the text of the string in line 33, but do not save your changes. Execute the script again, and notice that the printed text has not changed! This is because the editor is an external program to pyFormex, and the executed script is always the text as read from file, not necessarily equal to what is displayed in your editor.

    Save the script, run it again, and you will see the output has changed.

  • Next, change the text of the script to look like the one below, and save it as example1.py. Again, note that the editor and pyFormex are separate programs, and saving the script does not change the name of the current script in pyFormex.

    Selecting an existing script file for execution in pyFormex is done with the File ‣ Open option. Open the example1.py file you just saved and check that its name is indeed displayed in the status bar. You can now execute the script if you want, but it will not produce anything visible. We’ll learn you how to visualize geometry later on.

    1
    2
    3
    4
    5
    6
    7
    # $Id: example1.py 2146 2012-01-09 08:57:52Z bverheg $   *** pyformex ***
    
    """Example 1"""
    
    F = Formex([[[0.,0.],[1.,0.]],[[1.,1.],[0.,1.]]])
    
    # End
    
  • Exit pyFormex (using the File ‣ Exit) and then restart it. You should again see the example1.py displayed as the current script. On exit, pyFormex stores your last script name, and on restart it prepares to run it again. You can also easily select one the most recent scripts you used from the File ‣ History option. Select the oldest (bottom) one. Then close all your editor windows.

  • Open the example1.py again, either using File ‣ Open or File ‣ History. The script will not be loaded into your editor. That is becaused often you will just want to run the script, not change it. Use the File ‣ Edit option to load the current script into the editor.

Now that you know how to load, change and execute scripts in pyFormex, we’re all set for exploring its power. But first, let’s introduce you to some basic Python and NumPy concepts. If you are already familiar with them, you can just skip these sections.

Some basic Python concepts

pyFormex is written in the Python language, and Python is also the scripting language used by pyFormex. Since the whole intent of pyFormex is to generate geometrical structures from scripts, you will at least need to have some basic knowledge of Python before you can use it for your own projects.

The Python documentation website contains a variety of good documents to introduce you. If you are new to Python, but have already some programming experience, the Python tutorial may be a good starting point. Or else, you can take a look at one of the other beginners’ guides. Stick with the Python 2.x documentation for now. Though pyFormex might one day use Python 3.x, we are still far off that day, because all the underlying packages need to be converted to Python 3 first.

Do not be afraid of having to learn a new programming language. Python is known as own of the easiest languages to get started with: just a few basic concepts suffice to produce quite powerful scripts. Most developers and users of pyFormex have started without any knowledge of Python.

For the really impatient who do not want to go through the Python tutorial before diving into pyFormex, we have gathered hereafter some of the most important Python concepts, hopefully enabling you to continue with this tutorial.

Here is a small example Python script.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env python
"""Python intro

A short introduction to some aspects of the Python programming language
"""

for light in [ 'green','yellow','red','black',None]:
    if light == 'red':
        print 'stop'
    elif light == 'yellow':
        print 'brake'
    elif light == 'green':
        print 'drive'
    else:
        print 'THE LIGHT IS BROKEN!'



appreciation = { 0: 'not driving', 30:'slow', 60:'normal', 90:'dangerous', 120:'suicidal'}

for i in range(5):
    speed = 30*i
    print "%s. Driving at speed %s is %s" % (i,speed,appreciation[speed])


# End
  • A ‘#’ starts a comment: the ‘#’, and anything following it on the same line, is disregarded. A Python script typically starts with a comment line like line 1 of the above script.

  • Strings in Python can be delimited either by single quotes (‘), double quotes (”) or by triple double quotes (“””). The starting and ending delimiters have to be equal though. Strings in triple quotes can span several lines, like the string on lines 2-5.

  • Indentation is essential. Indentation is Python’s way of grouping statements. In small, sequential scripts, indentation is not needed and you should make sure that you start each new line in the first column. An if test or a for loop will however need indentation to mark the statement(s) inside the condition or loop. Thus, in the example, lines 8-15 are the block of statements that are executed repeatedly under the for loop construct in line 7. Notice that the condition and loop statements end with a ‘:’.

    You should make sure that statements belonging to the same block are indented consistently. We advice you not to use tabs for indenting. A good practice commonly followed by most Python programmers is to indent with 4 spaces.

    The indentation makes Python code easy to read for humans. Most modern editors will recognize Python code and help you with the indentation.

  • Variables in Python do not need to be declared before using them. In fact, Python has no variables, only typed objects. An assignment is just the binding of a name to an object. That binding can be changed at each moment by a new assignment to the same name.

  • Sequences of objects can be grouped in tuples or lists, and individual items of them are accessed by an index starting from 0.

  • Function definitions can use both positional arguments and keyword arguments, but the keyword arguments must follow the positional arguments. The order in which keyword arguments are specified is not important.

  • You can use names defined in other modules, but you need to import those first. This can be done by importing the whole module and then using a name relative to that module:

    import mymodule
    print mymodule.some_variable
    

    or you can import specific names from a module:

    from mymodule import some_variable
    print some_variable
    

    or you can import everything from a module (not recommended, because you can easily clutter your name space):

    from mymodule import *
    print some_variable
    

Some basic NumPy concepts

Warning

This section still needs to be written!

Numerical Python (or NumPy for short) is an extension to the Python language providing efficient operations on large (numerical) arrays. relies heavily on NumPy, and most likely you will need to use some NumPy functions in your scripts. As NumPy is still quite young, the available documentation is not so extensive yet. Still, the tentative NumPy tutorial http://www.scipy.org/Tentative_NumPy_Tutorial already provides the basics.

If you have ever used some other matrix language, you will find a lot of similar concepts in NumPy.

To do: Introduce the (for users) most important NumPy concepts.

pyFormex uses the NumPy ndarray as implementation of fast numerical arrays in Python.

The Formex data model

The most important geometrical object in pyFormex is the Formex class. A Formex (plural:Formices) can describe a variety of geometrical objects: points, lines, surfaces, volumes. The most simple geometrical object is the point, which in three dimensions is only determined by its coordinates (x,y,z), which are numbered (0,1,2) in pyFormex to be consistent with Python and NumPy indexing. Higher order geometrical objects are defined as a collection of points. The number of points of an object is called the plexitude of the object.

A Formex is a collection of geometrical objects of the same plexitude. The objects in the collection are called the elements of the Formex. A Formex whose elements have plexitude n is also called an n-plex Formex. Internally, the coordinates of the points are stored in a NumPy ndarray with three dimensions. The coordinates of a single point are stored along the last axis (2) of the Formex; all the points of an element are stored along the second axis (1); different elements are stored along the first axis (0) of the Formex. The figure The structure of a Formex schematizes the structure of a Formex.

The structure of a Formex

The structure of a Formex

Warning

The beginning user should be aware not to confuse the three axes of a Formex with the axes of the 3D space. Both are numbered 0..2. The three coordinate axes form the components of the last axis of a Formex.

For simplicity of the implemented algorithms, internally pyFormex only deals with 3D geometry. This means that the third axis of a Formex always has length 3. You can however import 2D geometry: all points will be given a third coordinate z=0.0. If you restrict your operations to transformations in the (x,y)-plane, it suffices to extract just the first two coordinates to get the transformed 2D geometry.

The Formex object F can be indexed just like a NumPy numerical array: F[i] returns the element with index i (counting from 0). For a Formex with plexitude n, the result will be an array with shape (n,3), containing all the points of the element. Further, F[i][j] will be a (3,)-shaped array containing the coordinates of point j of element i. Finally, F[i][j][k] is a single floating point value representing one coordinate of that point.

In the following sections of this tutorial, we will first learn you how to create simple geometry using the Formex data model and how to use the basic pyFormex interface functions. The real power of the Formex class will then be established starting from the section Transforming a Formex.

Creating a Formex

There are many, many ways to create Formex instances in your scripts. Most of the geometrical operations and transformations in pyFormex return geometry as a Formex. But how do you create a new geometric structure from simple coordinate data? Well, there are several ways to do that too, and we’ll introduce them one by one.

Direct input of structured coordinate data

The most straightforward way to create a Formex is by directly specifying the coordinates of the points of all its elements in a way compatible to creating a 3D ndarray:

F = Formex([[[0.,0.],[1.,0.]],[[1.,1.],[0.,1.]]])

The data form a nested list of three levels deep. Each innermost level list holds the coordinates of a single point. There are four of them: [0.,0.], [1.,0.], [1.,1.] and [0.,1.]. Remark that we left out the third (z) coordinate and it will be set equal to zero. Also, though the values are integer, we added a dot to force floating point values.

Warning

Python by default uses integer math on integer arguments! We advice you to always write the decimal point in values that initialize variables that can have floating point values, such as lengths, angles, thicknesses. Use integer values only to initialize variables that can only have an integer value, such as the number of elements.

The second list level groups the points into elements. In this case there are two elements, each containing two points. The outermost list level then is the Formex: it has plexitude 2 and contains 2 elements. But what geometrical entities does this represent? The plexitude alone does not specify what kind of geometric objects we are dealing about. A 2-plex element would presumably represent a straight line segment between two points in space, but it could just as well be used to represent a sphere (by its center and a point on the surface) or a plane (by a point in the plane and the direction of the normal).

By default, pyFormex will interprete the plexitude as follows:

Plexitude Geometrical interpretation
1 Points
2 Straight line segments
3 Triangles
4 or higher Polygons (possibly nonplanar)

We will see later how to override this default. For now, let’s draw Formices with the default. Go back to the example1.py script in your editor, containing the line above, and add the draw(F) instruction to make it look like:

F = Formex([[[0.,0.],[1.,0.]],[[1.,1.],[0.,1.]]])
draw(F)

Save the script and execute it in pyFormex. You will see the following picture appear in the canvas.

Two parallel lines

Two parallel lines

Now let’s remove the two central ‘]’ and ‘[‘ brackets in the first line:

F = Formex([[[0.,0.],[1.,0.],[1.,1.],[0.,1.]]])
draw(F,color=blue)

With the same data we have now created a 4-plex Formex with only one element. Execute the script again (do not forget to save it first) and you will see a square. Note that the draw command allows you to specify a color.

A square

A square.

But wait a minute! Does this represent a square surface, or just the four lines constituting the circumference of the square? Actually, it is a square surface, but since the pyFormex GUI by default displays in wireframe mode, unless you have changed it, you will only see the border of the square. You can make surfaces and solids get fully rendered by selecting the Viewport ‣ Render Mode ‣ Flat option or using the shortcut button-flat button in the toolbar. You will then see

A filled square

The square in smooth rendering.

pyFormex by default uses wireframe rendering, because in a fully rendered mode many details are obscured. Switch back to wireframe mode using the Viewport ‣ Render Mode ‣ Wireframe menu option or button-wireframe toolbar button.

Now suppose you want to define a Formex representing the four border lines of the square, and not the surface inside that border. Obviously, you need a 4 element 2-plex Formex, using data structured like this:

F = Formex([[[0.,0.],[0.,1.]], [[0.,1.],[1.,1.]], [[1.,1.],[1.,0.]], [[1.,0.],[0.,0.]]])
draw(F,color=blue,clear=True)

Try it, and you will see an image identical to the earlier figure A square.. But now this image represents four straight lines, while the same image formerly represented a square plane surface.

Warning

When modeling geometry, always be aware that what you think you see is not necessarily what it really is!

The clear=True option in the draw statement makes sure the screen is cleared before drawing. By default the pyFormex draw statement does not clear the screen but just adds to what was already drawn. You can make the clear=True option the default from the Viewport ‣ Drawing Options menu. Do this now before continuing.

Changing the rendering mode, the perspective and the viewpoint can often help you to find out what the image is really representing. But interrogating the Formex data itself is the definite way to make sure:

F = Formex([[[0.,0.],[1.,0.],[1.,1.],[0.,1.]]])
print F.shape()
F = Formex([[[0.,0.],[1.,0.]],[[1.,1.],[0.,1.]]])
print F.shape()

This will print the length of the three axes of the coordinate array. In the first case you get (1, 4, 3) (1 element of plexitude 4), while the second one gives (2, 2, 3) (2 elements of plexitude 2).

You can also print the whole Formex, using print F, giving you the coordinate data in a more readable fashion than the list input. The last example above will yield: {[0.0,0.0,0.0; 1.0,0.0,0.0], [1.0,1.0,0.0; 0.0,1.0,0.0]}. In the output, coordinates are separated by commas and points by semicolons. Elements are contained between brackets and the full Formex is placed inside braces.

Until now we have only dealt with plane structures, but 3D structures are as easy to create from the coordinate data. The following Formex represents a pyramid defined by four points (a tetrahedron):

F = Formex([[[0.,0.,0.],[1.,0.,0.],[0.,1.,0.],[0.,0.,1.]]],eltype='tet4')
draw(F)

Depending on your current rendering mode, this will produce an image like one of the following:

The tetrahedron in wireframe and smoothwire rendering

The tetrahedron in wireframe and smoothwire (transparent) rendering

The smoothwire mode can be set from the Viewport ‣ Render Mode ‣ Smoothwire option or the button-smoothwire button. The transparent mode can be toggled using the button-transparent button.

Hold down the left mouse button and move the mouse: the pyramid will rotate. In the same way, holding down the rigth button will zoomin and out. Holding down the middle button (possibly the mouse wheel, or the left and right button together) will move the pyramid over the canvas. Practice a bit with these mouse manipulations, until you get a feeling of what they do. All these mouse operations do not change the coordinates of the structure: they just change the way you’re looking at it. You can restore the default view with the Views ‣ Front menu or the button-front button.

The default installation of pyFormex provides seven default views: Front, Back, Left, Right, Top, Bottom and Iso. They can be set from the Views menu items or the corresponding view buttons in the toolbar. The default Front corresponds to the camera looking in the -z direction, with the x axis oriented to the right and the y axis upward.

We explicitely added the element type tet4 when creating the pyramid. Without it, pyFormex would have interpreted the 4-plex Formex as a quadrilateral (though in this case a non-planar one).

Using the pattern() or mpattern() functions

In the previous examples the Formices were created by directly specifying the coordinate data. That is fine for small structures, but quickly becomes cumbersome when the structures get larger. The pattern() and mpattern() functions reduce the amount of input needed to create a Formex from scratch.

Both functions create a series of points from a simple string. Each character of the string is interpreted either as a unit step in one of the coordinate directions, or as some other simple action. These functions are thus very valuable in creating geometry where the points lie on a regular grid.

The first point of the list is [0,0,0]. Each character from the input string is interpreted as a code specifying how to move to the next point. Currently defined are the following codes: 0 = goto origin [0,0,0], 1..8 move in the x,y plane, 9 remains at the same place. When looking at the plane with the x-axis to the right and the y-axis up, 1 = East, 2 = North, 3 = West, 4 = South, 5 = NE, 6 = NW, 7 = SW, 8 = SE. Adding 16 to the ordinal of the character causes an extra move of +1 in the z-direction. Adding 48 causes an extra move of -1. This means that ‘ABCDEFGHI’, resp. ‘abcdefghi’, correspond with ‘123456789’ with an extra z = +/- 1. This gives the following schema:

    z+=1             z unchanged            z -= 1

F    B    E          6    2    5         f    b    e
     |                    |                   |
     |                    |                   |
C----I----A          3----9----1         c----i----a
     |                    |                   |
     |                    |                   |
G    D    H          7    4    8         g    d    h

The special character ‘/’ can be put before any character to make the move without making a connection. The effect of any other character is undefined.

The pattern() function creates a straight line segment between each pair of subsequent points, and thus results in a 2-plex Formex. Here’s an example:

F = Formex(pattern('1234'))
draw(F)

It creates the same circumference of a unit square as above (see figure A square.), but is much simpler than the explicit specification of the coordinates we used before. Figure Images generated from the patterns ‘127’, ‘11722’ and ‘22584433553388’ shows some more examples.

Images generated from patterns

Images generated from the patterns ‘127’, ‘11722’ and ‘22584433553388’

The mpattern() function is more general than pattern() in that it allows the creation of Formices of any plexitude. Each subsequent point is added to the same element, until a ‘-‘ character or the end of the string is found. The following example creates a square:

F = Formex(mpattern('123'))
draw(F)

If it comes as a surprise that there are only 3 characters for a square, remember that the origin is always added as first point.

Some simple wireframe patterns are defined in simple.py and are ready for use. These pattern strings are stacked in a dictionary called ‘Pattern’. Items of this dictionary can be accessed like Pattern['cube']. They still need to be processed by the pattern() function to produce coordinates:

#!/usr/bin/env pyformex
from simple import Pattern
F = Formex(pattern(Pattern['cube']))
print F.shape()
draw(F,color=blue,view='iso')
A wireframe cube

A wireframe cube

The printed out shape of the Formex is (12,2,3), confirming that what we have created here is not a 3D solid cube, nor the planes bounding that cube, but merely twelve straight line segments forming the edges of a cube.

The view='iso' option in the draw statement rotates the camera so that it looks in the [-1,-1,-1] direction. This is one of the predefined viewing directions and can also be set from the Views menu or using the button-iso button.

While the pattern() and mpattern() functions can only generate points lying on a regular cartesian grid, pyFormex provides a wealth of transformation functions to move the points to other locations after they were created. Also, the Turtle plugin module provides a more general mechanism to create planar wireframe structures.

Reading coordinates from a file or a string

Sometimes you want to read the coordinates from a file, rather than specifying them directly in your script. This is especially handy if you want to import geometry from some other program that can not export data in a format that is understood by pyFormex. There usually is a way to write the bare coordinates to a file, and the pyFormex scripting language provides all the necessary tools to read them back.

As an example, create (in the same folder where you store your scripts) the text file square.txt with the following contents:

0, 0, 0,  0, 1, 0,  1, 1, 0,  1, 0, 0,
1, 1, 0,  2, 1, 0,  2, 2, 0,
1, 2, 0

Then create and execute the following script.

#!/usr/bin/env pyformex
chdir(__file__)
F = Formex.fromfile('square.txt',sep=',',nplex=4)
draw(F)

It will generate two squares, as shown in the figure Two squares with coordinates read from a file.

Two squares read from file

Two squares with coordinates read from a file

The chdir(__file__) statement sets your working directory to the directory where the script is located, so that the filename can be specified without adding the full pathname. The Formex.fromfile() call reads the coordinates (as specified, separated by ‘,’) from the file and groups them into elements of the specified plexitude (4). The grouping of coordinates on a line is irrelevant: all data could just as well be given on a single line, or with just one value per line. The separator character can be accompanied by extra whitespace. Use a space character if your data are only separated by whitespace.

There is a similar Formex.fromstring() method, which reads coordinates directly from a string in the script. If you have a lot of coordinates to specify, this may be far more easy than using the list formatting. The following script yields the same result as the above one:

#!/usr/bin/env pyformex
F = Formex.fromstring("""
0 0 0  0 1 0  1 1 0  1 0 0
1 1 0  2 1 0  2 2 0  1 2 0
""",nplex=4)
draw(F)

Here we used the default separator, which is a space.

Note

Make sure to use Formex.fromfile(), to distinguish it from Coords.fromfile() and numpy.fromfile().

Concatenation and lists of Formices

Multiple Formices can be concatenated to form one new Formex. There are many ways to do this, but the simplest is to use the ‘+’ or += operator. Notice the diffference: the + operator does not changing any of the arguments, but the += operator adds the second argument to the first, changing its definition:

F = Formex(pattern('1234'))
G = Formex(pattern('5'))
H = F + G
draw(H)

displays the same Formex as:

F += G
draw(F)

but in the latter case, the original definition of F is lost.

The += operator is one of the very few operations that change an existing Formex. Nearly all other operations return a resulting Formex without changing the original ones.

Because a Formex has a single plexitude and element type, concatenation is restricted to Formices of the same plexitude and with the same eltype. If you want to handle structures with elements of different plexitude as a single object, you have to group them in a list:

F = Formex(pattern('1234'))
G = Formex([0.5,0.5,0.])
H = [F,G]
draw(H,color=red)

This draws the circumference of a unit square (F: plexitude 2) and the center point of the square (G: plexitude 1), both in red.

A square and its center point

A square and its center point.

Formex property numbers

Apart from the coordinates of its points, a Formex object can also store a set of property numbers. This is a set of integers, one for every element of the Formex. The property numbers are stored in an attribute prop of the Formex. They can be set, changed or deleted, and be used for any purpose the user wants, e.g. to number the elements in a different order than their appearence in the coordinate array. Or they can be used as pointers into a large database that stores all kind of properties for that element. Just remember that a Formex either has no property numbers, or a complete set of numbers: one for every element.

Property numbers can play an important role in the modeling process, because they present some means of tracking how the resulting Formex was created. Indeed, each transformation of a Formex that preserves its structure, will also preserve the property numbers. Concatenation of Formices with property numbers will also concatenate the property numbers. If any of the concatenated Formices does not have property numbers, it will receive value 0 for all its elements. If all concatenated Formices are without properties, so will be the resulting Formex.

On transformations that change the structure of the Formex, such as replication, each element of the created Formex will get the property number of the Formex element it was generated from.

To add properties to a Formex, use the setProp() method. It ensures that the property array is generated with the correct type and shape. If needed, the supplied values are repeated to match the number of elements in the Formex. The following script creates four triangles, the first and third get property number 1, the second and fourth get property 3.:

F = Formex(mpattern('12-34-14-32'))
F.setProp([1,3])
print F.prop   # --> [1 3 1 3]

As a convenience, you can also specify the property numbers as a second argument to the Formex constructor. Once the properties have been created, you can safely change individual values by directly accessing the prop attribute:

F = Formex(mpattern('12-34-14-32'),[1,3])
F.prop[3] = 4
print(F.prop)   # --> [1 3 1 4]
draw(F)
drawNumbers(F)

When you draw a Formex with property numbers using the default draw options (i.e. no color specified), pyFormex will use the property numbers as indices in a color table, so different properties are shown in different colors. The default color table has eight colors: [black, red, green, blue, cyan, magenta, yellow, white] and will wrap around if a property value larger than 7 is used. You can however specify any other and larger colorset to be used for drawing the property colors. The following figure shows different renderings of the structure created by the above script. The drawNumbers() function draws the element numbers (starting from 0).

A Formex with properties drawn as colors

A Formex with property numbers drawn as colors. From left to right: wireframe, flat, flat (transparent), flatwire (transparent).

In flat rendering mode, the element numbers may be obscured by the faces. In such case, you can make the numbers visible by using the transparent mode, which can be toggled with the button-transparent button.

Adding properties to a Formex is often done with the sole purpose of drawing with multiple colors. But remember you are free to use the properties for any purpose you want. You can even save, change and restore them throughout the lifetime of a Formex object, thus you can attibrute multiple property sets to a Formex.

Getting information about a Formex

While the visual feedback on the canvas usually gives a good impression of the structure you created, at times the view will not provide enough information or not precise enough. Viewing a 3D geometry on a 2D screen can at times even be very misleading. The most reliable source for checking your geometry will always be the Formex data itself. We have already seen that you can print the coordinates of the Formex F just by printing the Formex itself: print(F). Likewise you can see the property numbers from a print(F.prop) instruction.

But once you start using large data structures, this information may become difficult to handle. You are usually better off with some generalized information about the Formex object. The Formex class provides a number of methods that return such information. The following table lists the most interesting ones.

Function Return value
F.nelems() The number of elements in the Formex
F.nplex() The plexitude of the Formex (the number of points in each element of the Formex)
F.bbox() The bounding box of the Formex
F.center() The center of the bbox of the Formex
F.sizes() The size of the bbox of the Formex

Saving images

Often you will want to save an image of the created geometry to a file, e.g. to include it in some document. This can readily be done from the File ‣ Save Image menu. You just have to fill in the file name and click the Save buttton. You can specify the file format by using the appropriate extension in the file name. The default and recommended format is png, but pyFormex can save in commonly used bitmap formats like jpg or gif as well. If you have installed gl2ps (see Install additional software), you can even save in a number of vector formats, such as eps or svg.

But you can also create the images from inside your script. Just import the image module and call the image.save() function:

import gui.image
image.save("my_image.png")

Often you will want to change some settings, like rendering mode or background color, to get a better looking picture. Since the main goal of pyFormex is to automate the creation and transformation of geometrical models, all these settings can be changed from inside your script as well. The following code was used to create the four images in figure A Formex with property numbers drawn as colors. From left to right: wireframe, flat, flat (transparent), flatwire (transparent). above.

import gui.image
chdir(__file__)
reset()
bgcolor(white)
linewidth(2)
canvasSize(200,300)
F = Formex(mpattern('12-34-14-32'),[1,3])
F.prop[3] = 4
clear()
draw(F)
drawNumbers(F)
wireframe()
image.save('props-000.png')
flat()
transparent(False)
image.save('props-001.png')
transparent(True)
image.save('props-002.png')
flatwire()
image.save('props-003.png')

The following table lists the interactive menu option and the correspondant programmable function to be used to change some of the most common rendering settings.

Purpose Function(s) Menu item
Background color bgcolor() Viewport ‣ Background Color
Line width linewidth() Viewport ‣ LineWidth
Canvas Size canvasSize() Viewport ‣ Canvas Size
Render Mode wireframe(), flat(), flatwire(), smooth(), smoothwire() Viewport ‣ Render Mode
Transparency transparent()  

Transforming a Formex

Until now, we’ve only created simple Formices. The strength of pyFormex however is the ease to generate large geometrical models by a sequence of mathematical transformations. After creating a initial Formex, you can transform it by creating copies, translations, rotations, projections,...

The Formex class has an wide range of powerful transformation methods available, and this is not the place to treat them all. The reference manual pyFormex reference manual describes them in detail.

We will illustrate the power of the Formex transformations by studying one of the examples included with pyFormex. The examples can be accessed from the Examples menu option.

Note

If you have installed multiple script directories, the examples may be found in a submenu Scripts ‣ Examples.

When a script is selected from this menu, it will be executed automatically. Select the Examples ‣ Level ‣ Beginner ‣ Helix example. You will see an image of a complex helical frame structure:

A helical frame structure

A helical frame structure (Helix example)

Yet the geometry of this complex structure was built from the very simple pyFormex script shown below (Use File ‣ Edit script to load it in your editor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# $Id: Helix.py 2146 2012-01-09 08:57:52Z bverheg $   *** pyformex ***
##
##  This file is part of pyFormex 0.8.6  (Mon Jan 16 21:15:46 CET 2012)
##  pyFormex is a tool for generating, manipulating and transforming 3D
##  geometrical models by sequences of mathematical operations.
##  Home page: http://pyformex.org
##  Project page:  http://savannah.nongnu.org/projects/pyformex/
##  Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) 
##  Distributed under the GNU General Public License version 3 or later.
##
##
##  This program is free software: you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation, either version 3 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program.  If not, see http://www.gnu.org/licenses/.
##
"""Helix example from pyFormex"""
m = 36 # number of cells along helix
n = 10 # number of cells along circular cross section
reset()
setDrawOptions({'clear':True})
F = Formex(pattern("l:164"),[1,2,3]); draw(F)
F = F.replic(m,1.,0); draw(F)
F = F.replic(n,1.,1); draw(F)
F = F.translate(2,1.); draw(F,view='iso')
F = F.cylindrical([2,1,0],[1.,360./n,1.]); draw(F)
F = F.replic(5,m*1.,2); draw(F)
F = F.rotate(-10.,0); draw(F)
F = F.translate(0,5.); draw(F)
F = F.cylindrical([0,2,1],[1.,360./m,1.]); draw(F)
draw(F,view='right')

The script shows all steps in the building of the helical structure. We will explain and illustrate them one by one. If you want to see the intermediate results in pyFormex during execution of the script, you can set a wait time between subsequent drawing operations with Settings ‣ Draw Wait Time. Or alternatively, you can start the script with the button-step button: pyFormex will then halt before each draw function and wait until you push the button-step again.

The script starts (lines 26-27) with setting the two parameters m and n. It is always a good idea to put constants in a variable. That makes it easy to change the values in a single place when you want to create another structure: your model has become a parametric model.

Lines 28 resets the drawing options to the defaults. It is not essential in this script but it is often a good idea to restore the defaults, in case they would have been changed by a script that was run previously. Setting the clear=True option in line 29 makes sure the subsequent drawing instructions will remove the previous step from the canvas.

In line 30 we create the basic geometrical entity for this structure: a triangle consisting of three lines, which we give the properties 1, 2 and 3, so that the three lines are shown in a different color:

F = Formex(pattern("l:164"),[1,2,3])
The basic Formex

The basic Formex

This basic Formex is copied m times with a translation step 1.0 (this is precisely the length of the horizontal edge of the triangle) in the 0 direction:

F = F.replic(m,1.,0)
Replicated in x-direction

Replicated in x-direction

Then, the new Formex is copied n times with the same step size in the direction 1.

F = F.replic(n,1.,1)
Replicated in y-direction

Replicated in y-direction

Now a copy of this last Formex is translated in direction ‘2’ with a translation step of ‘1’. This necessary for the transformation into a cylinder. The result of all previous steps is a rectangular pattern with the desired dimensions, in a plane z=1.

F = F.translate(2,1); drawit(F,'iso')

This pattern is rolled up into a cylinder around the 2-axis.

F = F.cylindrical([2,1,0],[1.,360./n,1.]); drawit(F,'iso')

This cylinder is copied 5 times in the 2-direction with a translation step of ‘m’ (the lenght of the cylinder).

F = F.replic(5,m,2); drawit(F,'iso')

The next step is to rotate this cylinder -10 degrees around the 0-axis. This will determine the pitch angle of the spiral.

F = F.rotate(-10,0); drawit(F,'iso')

This last Formex is now translated in direction ‘0’ with a translation step of ‘5’.

F = F.translate(0,5); drawit(F,'iso')

Finally, the Formex is rolled up, but around a different axis then before. Due to the pitch angle, a spiral is created. If the pitch angle would be 0 (no rotation of -10 degrees around the 0-axis), the resulting Formex would be a torus.

F = F.cylindrical([0,2,1],[1.,360./m,1.]); drawit(F,'iso')
drawit(F,'right')

Converting a Formex to a Mesh model

pyFormex contains other geometry models besides the Formex. The Mesh model e.g. is important in exporting the geometry to finite element (FE) programs. A Formex often contains many points with (nearly) the same coordinates. In a Finite Element model, these points have to be merged into a single node, to express the continuity of the material. The toMesh() method of a Formex performs exactly that. It returns a Mesh instance, which has two import array attributes ‘coords’ and ‘elems’:

  • coords is a float array with shape (ncoords,3), containing the coordinates of the merged points (nodes),
  • elems is an integer array with shape (F.nelems(),F.nplex()), describing each element by a list of node numbers. These can be used as indices in the coords array to find the coordinates of the node. The elements and their nodes are in the same order as in F.
from simple import *
F = Formex(pattern(Pattern['cube']))
draw(F)
M = F.toMesh()
print 'Coords',M.coords
print 'Elements',M.elems

The output of this script are the coordinates of the unique nodes of the Mesh, and the connectivity of the elements. The connectivity is an integer array with the same shape as the first two dimensions of the Formex: (F.nelems(),F.nplex()):

Nodes
[[ 0.  0.  0.]
 [ 1.  0.  0.]
 [ 0.  1.  0.]
 [ 1.  1.  0.]
 [ 0.  0.  1.]
 [ 1.  0.  1.]
 [ 0.  1.  1.]
 [ 1.  1.  1.]]
Elements
[[0 1]
 [1 3]
 [3 2]
 [2 0]
 [0 4]
 [1 5]
 [3 7]
 [2 6]
 [4 5]
 [5 7]
 [7 6]
 [6 4]]

The inverse operation of transforming a Mesh model back into a Formex is also quite simple: Formex(nodes[elems]) will indeed be identical to the original F (within the tolerance used in merging of the nodes).

>>> G = Formex(nodes[elems])
>>> print allclose(F.f,G.f)
True

The allclose funcion in the second line tests that all coordinates in both arrays are the same, within a small tolerance.

pyformex-0.8.6/pyformex/doc/html/imaging.html0000644000211500021150000001677611705104246021136 0ustar benebene00000000000000 Creating Images — pyFormex v0.8.6 documentation

Table Of Contents

Previous topic

The Canvas

Next topic

Using Projects

[FSF Associate Member]

Valid XHTML 1.0 Transitional

Creating Images

Warning

This document still needs to be written!

Abstract

This chapter explains how to create image files of the renderings you created in pyFormex.

Save a rendering as image

A picture tells a thousand words
pyformex-0.8.6/pyformex/doc/html/gui.html0000644000211500021150000005670311705104246020301 0ustar benebene00000000000000 The Graphical User Interface — pyFormex v0.8.6 documentation

The Graphical User Interface

While the GUI has become much more elaborate in recent versions, its intention will never be to provide a fully interactive environment to operate on geometrical data. The main purpose of pyFormex will always remain to provide a framework for easily creating scripts to operate on geometries. Automization of otherwise tedious tasks is our primary focus.

The GUI mainly serves the following purposes:

  • Display a structure in 3D. This includes changing the viewpoint, orientation and viewing distance. Thus you can interactively rotate, translate, zoom.
  • Save a view in one of the supported image formats. Most of the images in this manual and on the website were created that way.
  • Changing settings (though not everything can be changed through the GUI yet).
  • Running scripts, possibly starting other programs and display their results.
  • Interactively construct, select, change, import or export geometrical structures.

Unlike with most other geometrical modelers, in you usually design a geometrical model by writing a small script with the mathematical expressions needed to generate it. Any text editor will be suitable for this purpose. The main author of uses GNU Emacs, but this is just a personal preference. Any modern text editor will be fine, and the one you are accustomed with, will probably be the best choice. Since Python is the language used in scripts, a Python aware editor is highly preferable. It will highlight the syntax and help you with proper alignment (which is very important in Python). The default editors of KDE and Gnome and most other modern editors will certainly do well. A special purpose editor integrated into the GUI is on our TODO list, but it certainly is not our top priority, because general purpose editors are already adequate for our purposes.

Learning how to use is best done by studying and changing some of the examples. We suggest that you first take a look at the examples included in the GUI and select those that display geometrical structures and/or use features that look interesting to you. Then you can study the source code of those examples and see how the structures got built and how the features were obtained. Depending on your installation and configuration, the examples can be found under the Examples or Scripts main menu item. The examples may appear classified according to themes or keywords, which can help you in selecting appropriate examples.

Selecting an example from the menu will normally execute the script, possibly ask for some interactive input and display the resulting geometrical structure. To see the source of the script, choose the File ‣ Edit Script menu item.

Before starting to write your own scripts, you should probably get acquainted with the basic data structures and instructions of Python, NumPy and pyFormex. You can do this by reading the pyFormex tutorial.

Starting the GUI

You start the pyFormex GUI by entering the command pyformex in a terminal window. Depending on your installation, you may also have a panel or menu button on your desktop from which you can start the graphical interface by a simple mouse click. When the main window appears, it will look like the one shown in the figure The pyFormex main window. Your window manager will most likely have put some decorations around it, but these are very much OS and window manager dependent and are therefore not shown in the figure.

Finally, you can also start the GUI with the instruction
startGUI() from a pyFormex script executed in non-GUI mode.
pyFormex GUI

The pyFormex main window

Basic use of the GUI

As is still in its infancy, the GUI is subject to frequent changes and it would make no sense to cover here every single aspect of it. Rather we will describe the most important functions, so that users can quickly get used to working with . Also we will present some of the more obscure features that users may not expect but yet might be very useful.

The window (figure The pyFormex main window) comprises 5 parts. From top to bottom these are:

  1. the menu bar,
  2. the tool bar,
  3. the canvas (empty in the figure),
  4. the message board, and
  5. the status bar.

Many of these parts look and work in a rather familiar way. The menu bar gives access to most of the GUI features through a series of pull-down menus. The most import functions are described in following sections.

The toolbar contains a series of buttons that trigger actions when clicked upon. This provides an easier access to some frequently used functions, mainly for changing the viewing parameters.

The canvas is a drawing board where your scripts can show the created geometrical structures and provide them with full 3D view and manipulation functions. This is obviously the most important part of the GUI, and even the main reason for having a GUI at all. However, the contents of the canvas is often mainly created by calling drawing functions from a script. This part of the GUI is therefore treated in full detail in a separate chapter.

In the message board displays informative messages, requested results, possibly also errors and any text that your script writes out.

The status bar shows the current status of the GUI. For now this only contains the filename of the current script and an indicator if this file has been recognized as a script (happy face) or not (unhappy face).

Between the canvas and the message board is a splitter allowing resizing the parts of the window occupied by the canvas and message board. The mouse cursor changes to a vertical resizing symbol when you move over it. Just click on the splitter and move the mouse up or down to adjust the canvas/message board to your likings.

The main window can be resized in the usual ways.

The File menu

The Settings menu

Many aspects of the pyFormex GUI are configurable to suit better to the user’s likings. This customization can be made persistent by storing it in a configuration file. This is explained in Configuring pyFormex.

Many of the configuration variables however can be changed interactively from the GUI itself.

  • Settings ‣ Commands: Lets you change the external command name used for the editor, the HTML/text file viewer and the HTML browser. Each of these values should be an executable command accepting a file name as parameter.

The viewport menu

Mouse interactions on the canvas

A number of actions can be performed by interacting with the mouse on the canvas. The default initial bindings of the mouse buttons are shown in the following table.

Rotate, pan and zoom

You can use the mouse to dynamically rotate, pan and zoom the scene displayed on the canvas. These actions are bound to the left, middle and right mouse buttons by default. Pressing the corresponding mouse button starts the action; moving the mouse with depressed button continuously performs the actions, until the button is released. During picking operations, the mouse bindings are changed. You can however still start the interactive rotate, pan and zoom, by holding down the ALT key modifier when pressing the mouse button.

rotate
Press the left mouse button, and while holding it down, move the mouse ove the canvas: the scene will rotate. Rotating in 3D by a 2D translation of the mouse is a fairly complex operation:
  • Moving the mouse radially with respect to the center of the screen rotates

    around an axis lying in the screen and perpendicular to the direction of the movement.

  • Moving tangentially rotates around an axis perpendicular to the screen (the

    screen z-axis), but only if the mouse was not too close to the center of the screen when the button was pressed.

    Try it out on some examples to get a feeling of the workinhg of mouse rotation.

pan
Pressing the middle (or simultanuous left+right) mouse button and holding it down, will move the scene in the direction of the mouse movement. Because this is implemented as a movement of the camera in the opposite direction, the perspective of the scene may change during this operation.
zoom
Interactive zooming is performed by pressing the right mouse button and move the mouse while keeping the button depressed. The type of zoom action depends on the direction of the movement:
  • horizontal movement zooms by camera lens angle,

  • vertical movement zooms by changing camera distance.

    The first mode keeps the perspective, the second changes it. Moving right and upzooms in, left and down zooms out. Moving diagonally from upper left to lower right more or less keeps the image size, while changing the perspective.

Interactive selection

During picking operations, the mouse button functionality is changed. Click and drag the left mouse button to create a rectangular selection region on the canvas. Depending on the modifier key that was used when pressing the button, the selected items will be:

NONE
set as the current selection;
SHIFT
added to the currentselection;
CTRL
removed from the current selection.

Clicking the right mouse button finishes the interactive selection mode.

During selection mode, using the mouse buttons in combination with the ALT modifier key will still activate the default mouse functions (rotate/pan/zoom).

Customizing the GUI

Some parts of the GUI can easily be customized by the user. The appearance (widget style and fonts) can be changed from the preferences menu. Custom menus can be added by executing a script. Both are very simple tasks even for beginning users. They are explained shortly hereafter.

Experienced users with a sufficient knowledge of Python and GUI building with Qt can of course use all their skills to tune every single aspect of the GUI according to their wishes. If you send us your modifications, we might even include them in the official distribution.

Changing the appearance of the GUI

Adding your scripts in a menu

By default, pyFormex adds all the example scripts that come with the distribution in a single menu accessible from the menubar. The scripts in this menu are executed by selecting them from the menu. This is easier than opening the file and then executing it.

You can customize this scripts menu and add your own scripts directories to it. Just add a line like the following to the main section of your .pyformexrc configuration file: — scriptdirs = [(‘Examples’, None), (‘My Scripts’, ‘/home/me/myscripts’), (‘More’, ‘/home/me/morescripts’)]

Each tuple in this list consists of a string to be used as menu title and the absolute path of a directory with your scripts. From each such directory all the files that are recognized as scripts and do no start with a ‘.’ or ‘_’, will be included in the menu. If your scriptdirs setting has only one item, the menu item will be created directly in the menubar. If there are multiple items, a top menu named ‘Scripts’ will be created with submenus for each entry.

Notice the special entry for the examples supplied with the distribution. You do not specify the directory where the examples are: you would probably not even know the correct path, and it could change when a new version of is installed. As long as you keep its name to ‘Examples’ (in any case: ‘examples’ would work as well) and the path set to None (unquoted!), will itself try to detect the path to the installed examples.

Adding custom menus

When you start using for serious work, you will probably run into complex scripts built from simpler subtasks that are not necessarily always executed in the same order. While the scripting language offers enough functions to ask the user which parts of the script should be executed, in some cases it might be better to extend the GUI with custom menus to execute some parts of your script.

For this purpose, the gui.widgets module of provides a Menu widget class. Its use is illustrated in the example Stl.py.

pyformex-0.8.6/pyformex/doc/html/meta.html0000644000211500021150000001710311705104246020432 0ustar benebene00000000000000 About the pyFormex documentation — pyFormex v0.8.6 documentation

Table Of Contents

Previous topic

GNU GENERAL PUBLIC LICENSE

[FSF Associate Member]

Valid XHTML 1.0 Transitional

About the pyFormex documentation

Abstract

This document contains some meta information about the pyFormex documentation. You will learn nothing here about pyFormex. But if you are interested in knowing how the documentation is created and maintained, and who is responsible for this work, you will find some answers here.

The people who did it

Most of the manual was written by Benedict Verhegghe, also the main author of pyFormex. There are contributions from Tim Neels, Matthieu De Beule and Peter Mortier.

How we did it

The documentation is written in reStructuredText and maintained with Sphinx.

pyformex-0.8.6/pyformex/doc/html/objects.inv0000644000211500021150000006515611705104257021002 0ustar benebene00000000000000# Sphinx inventory version 2 # Project: pyFormex # Version: 0.8.6 # The remainder of this file is compressed using zlib. xڭ[H6^ٌYcv2O+c[^$SRRU:DCA&Z d0J&<.;ƒ$""}۳Mm ٛ޴kM>=ա?jkMn}܈N 1ߏ^Ʃj[wpN=Mm]%f̵sa/^w4j;ݷQ׃?< #gh>mclCWpU5^KE.#zklyj۪Qkة=NxҫO pz]M?TZo*WR'רuE?ݛ??\5xV,O:S''S54L+vr}U [ 2/CyyշStм_/CݪZ]?͓X=T=busܫ+44g1y?RnWMۏU=G~t&GsFlӯ7m5zK <zz>{ }u8TJ ZR|ڣ0Wz-pݽgz,3<P=3G9P깃Vn 14i-rWFY?cԈg8s|W;yەz/'{RpC%@?T> ̼ri싔R|[T^p*UK>M0J'Џ&LI8Ly#Fe̦|X UEB]ge҂."w/W~'e <+>^Ќ5|5;9ǗٛeR6nJcgm%0@K-q3re^$º X‡'35HT_ %ţGhe=t>gݘ禆 (gՃcyg޷K#FtX5;|#= 33j묓3{)|WlVWp6xu󖜾Scgzr54Gm'z4bpjـgϟhZDzf?_Gy?(@I۬փgfԻCJU_9>$Wc.PYp t}K~f7p7O!R OןUgfo͑Z~;=˅q?wm_Ո?Ƽ eW}ގ:ܚR7By͕7>V!TNQ?z/_n?\俷Z$rF:c?:L ]C ]$&l0~~ D)opF ٫-zyFx䩏}Vvq/-yxPY#?<$A#/)c/zZw&2Kvu}.^ ƽ:Dtz}zSF_c+}L=<YOpS/ޮҭtTZz&h!sAٝ;=i?wY g[nmphI_'|۝jQ voOV/M0=~|F7ySFI95Z4sF!dq²:@q0y[rLb0#*Q=0sݮGE4,TVh.i^ Q*hY3ξ >]SOE/  #C}SǮ֐ɹ60L UZWRl^W<v;Ć"ÑFaJn`lvSGG_ 13Kz\2ځ>=APFdR6tj4;Z`:O m[Mտ)9pz7>Zg۹X!LYfnfh}? bӨ5 Of gi~!ȯMՁi*8UeBGuWI;DOJ)d xn 9q Α$q_A޽ ## /!(N]Hn,`Gia5R*u6Fw 846IMϙUo0ʙzA,GJe[ ĺРaGۿ)m -;D $Oո't&9ǣgǚ?s/M b㑂cR&r)$3g揺?19q }4*Le- `Mf!D}/ 1~ϐX6N$ybz!4ulp%P9Ř1P8hԥ2ʯ}/l8+" kgQ3+ n J]H2aOB s~h}uП^}OXOO~5,ƀ f<D5̟&w^xcPЉ@Bt}-Lp=/XY58vҋn-{/3 6 {D7ݘ.Iyg!- ek:n?O*k&RPuc (APC\%\UD$Dzmbued2_QSp/ye=Kz9S7.ʣȯ^xzl+sVV9՚&_zagl~nl3xgq8:CJ"*9K VؘHXJ_iD::lfyKt0/^kxFsJA/zJzƁ5C0w=7U7lxCrj Pf$wb%|2[{ ׯJZ 2WO(/7e:)IV~>ޮkF1B kl隊D֩KDUy"rmƚ?j'[Xsbw R,c Hsp[fTOt8aVցjbpTMFdPCuBT=ģJ=*i7(uTLʩn2PSr| FVqi񕮨@H4'zLh5x om VrwnUK6Ӱiv)H4y8˜{QaEnhZ8[s=D_g~VCgǺ M u<Ϣ|GXmu~fs0#J hrmTY{/Dv"M;KMg`WͰ9bTYmT%;2DZZW﫦r8g汇׿;@4vRy  /!)҃-Iϔ%t*5̌b__ѪL,_MF:Z7 TpYŠ#ٰ{cKG q7Ϻu[5bM.~E,oC0gy A$Z_.G/ ~Ĥj\-:!5䦝^϶hk1{dSH(5hB_%Dʮj3)!{Y;a02 w0wܘǦCL TX>V_Y3{a1*f $"`eW=Qz|3*x̟7 N;̤#k.;;E6<[h='aB(yD._7*+ Nm4CTYSYRCs$pOT&+GH2v&)名$(9 Uo ɺ'Z=5>}G ʭW1eu|P'XD#B.֥%$bnbvWHwJRg9'?BoσwꓒsL4?v>h<nf]̚VOgUn=4%_0Y`O]Bu^)5GRfPANKz[Aּ8[5tQawW^8a6c:9hȞaͣrO.p]8Y{ŹJ?Cp+{z\ǮMC\/9[gQ|uhMSc#9&%~P|׷j6p I 1j<vf#̈xgp[Pyzg3@Wz6@蔨{4bз}^mR٘Vq0RcЁl*,^$k R#j1jg=y^ͮcfB띝1W~hKx?U :/<8OJ\x9?.x|n<3Tf:mU\,'\Ŝ1'|&&ڼncFPOqiM+j_yer^`҄ۍ/-,99@09-^+\;+%5ȣ g@eǥ!g~^@ھʝeP#W(>je?r .Z[#UvZF'}C-"Xv}>0ki0nQlҊAA{DVU( ge5*X.^^T~QGOV\dWv~V9t%,K\V;y~J]Y:۬#lVQ)mQֳ0Tl:6*jݤ1^`DVb1󛗭. s ?FOdQ>yc8D}܈_r*+nDEB*km6[rJRA?lQewJA@b kqV#YW'zm~x19Z.mףh QD /PWHO5M;jSGoN+hީN) yvc|#~1rQhN6l͵b%U@ijKp 8*G@եZ0W:O_S5g&Wq)RdIobb&֞z }=xR 8ut FH'ů7pMsy0 7h_x R@HduxZAdTFq,gBV|_T`+ k{ǠJg10j/!v^&RKq ~Pbl] Vk kF.sS .@ G,`! d"ׯҦڴf\wbI/ sc( Y}p Ke~`&K sB]F5y1rYQ~wJ.ٯ<)>x\A*xb)n$mBX9.tǪÜ\"gΡ`綯jb4[- nTYe4fʹ\ cV}b6^夶}i@`kJ~+h_"QgBdVj[QrAv7?sd.aQ/W|ϥ"bg}t5+Z0A<;vϞJy) NʠP٩QL Y )t'oʄC:+#[ 8$ : G|/cƘ[nL*pPt=K4DSMٛVgF-':,Q7mJ%!=֬#/Q\0CNLxQѲJM*Xidb* GMNJɁ+X2/F%oaD  TD\\?6j+/l/b) KTV@ةn ~ʪ`^tpRPuxeu_MJט/V|5EQzM RNzYYejHlZn-1B?P9c4#l@m^#rО*njRaҐETp7<&2U_ l7mA h\U<.t% 3]:D,._Eb8lV$RUp0:x<.pc8PI\d1dLZK1\mI`!-\$fR%f*XF'Ԅi|X|5(d n⟊r-Sv 03ñ3l`̀dK&%Fުyvx&S&g غj~i#ak02:~x]zDI Y̜ǕK%Eda8@xCb|:B-o?t_ :F+1;i^=@\e@ft۟oxR;`0%_a$7vTh 3#(]GL/n8|9Af23Mtk fA~ FB.3]`Q OQS.%!H )!R/RM&3 ͘*Gf)p%kzo¹$L.F}C!H-ȟ0&px'݁Ow/j Vl$DZgfH}~ԮHW? zd1Rt3SɢP T)ʲ$[V.Em۸Dj1b$*ʡryڍɳ8]fPa'G$ipX3l##.{graX4sLibmKN^vi®DqXO|>gW ٕ~:̕ĹutG(fX 6EgΣCb=(EjS6j5cBjC (|cڑ |rb) pY80'6LCQLXOr~ܛa`_0Kʘ ^m"q$NSq6fe6t+ARE,lAIQ792ev< WWķVUʢHN"Q*2qšvW5Gd6~NIR G28F HQn'o9UaE|&U{XZ2 F=OJyCӂ1., Q&It*'[-WK<ȏEX$`ͯ§:=s/@l bjݓðE4ȘgPe#g9cPMZ&v=Ipüa?KŢjWV@8:zl9Ô6]IhdG;ﷅ,VqcoyK;ӒAh[2wd3 4lt. cÌEzL4_7J j,aE ; W"վc$ NPdNxAd as"TdSN~0g4d)~ʛ '0Cacw38"z'/íoQF,!dURydn͐cvh4Z*`4f9P˥pX뽼Qdܠ KZhbS=86PMEVt ;R¦2aŲ>48i4 U m;ߥϕge"қb36\a&!Tq$ףl0Z7JNgM"¨[0>t>I>fDF TcZfa}Vl<|v7YJ- HΊ药 dp9aԺYV 3 GN/p'7*AJ mJrx#Xތy!g /_Bk퐃KoZCr)d'z@t2K[TI:VΖK{7u c2!rLoƩBtL"-P[lZxM={M CI, {zDLؐ툇BjI_ ]!L:79H" Ue LBO7%yZ&`AK$+M_1+đFoNd$JKJ/去TL(?Xvdu$ܲO,{J^S[DdV @^Ww$nզdPZJ&4hoB9Uc&gICՔOAtUaT`ڢ/K\x쁍$*z]>RG֦ lEdE9BR)ϪH zm⒍I,EV=)q~&K(~䏾_bqd+偝EZKoQd L@N'4a3M? a4E, q:o;Y ,l" V qbi} a.t:٧̕VeN|ѣЀ'RQv xqm/Ms#HKjo3qB\/fNY>;0;[Q@iX9>9R%'%l - y@^"bFڙ@`~ldJԣBLdxLh:]R-KV2Bq;BhҧLlJUq?&TNE ^pL4 &Tܬ$` 沒E5h`W;YFMduSPE,<$/O ?F'C@wI|кlOSY/1PC饭0 aްE b=覨N)k[M|*XSRO¡m,9RlP,.3Yӭe5uZF)6CSdU,`;Ž ntr.$cd\3;+DAw`-Ƕ'INZ|o|ai8֢")QMe{~}Q {Oml&AԖMНh# i~0 AzщBn$$fByFDX-+NH|$Ӕwu/zY3ns lg0P`$Z=u3Ne{Ǥ Wrur1<5Po3wXًH+b$ްiV'~'GFom$ mǔ+fL *JE6!a"w8Sl=~Kxib@Hԍyk0N)(ROKJpZx7FM2k$ċ@S3MZWڀXhBd6XZTjEč.$FS3gƮLG/_s8ς ճ%qIzG_)! @s@nCU2=x/oX ]St/aw[O vHgZț^ PL( |Pא܏TӇֆh@9i=>]*S܉K]gd|=ŴRֽQ+mx?7J2Dey@sI U#* ?TA6-vIٰn$Bܹo%\Rň3qs(t_}6BMw[ngjB rRͦXçUO;Ӷ$&*{WB̗nm2ͨy [#et Zɢbx4X>i|}?gL$"ya!M{1i;v" ъ嫧؜׃! ·N :lJY!lpy&cQŔ=J_t納2oRseiH:),NM V_o20 H{돁:Tӌëm/s$c2s"剼W9[.=@UuB6(c2w`#E7/ءxa,̞Y>M%F9覢|2=[tWY.X {%*A D!1Ӽr vBnoЖɚ2VS6q Niu\zƓ|',[;[aڶMjҳZ.+cT43.?R<(K.hUQ%Ƒv!u,Nj*b}ox1N<<%DpwH3[ԍ3r 0!WoEAٌٸD"%1fZ!8r~lf*=Ѽ-g!PE҃7{%-(i!V@F}gi (Cs&;:Dyޑo4k;e/`oń8ܺs3u3p;o-)..I_0eCm (됉NP_L %Q#2/975/ k xF<4 aOpe4xHo- o]31UQ굆yb{ΧLLb4HFa_0Nޙ&9I4'oHd.V&)_XLxck~%~yVT-JYTD`Z~rX4HR`e b *[)TE8j3/ff$e)OD!6`OXFP12-G)q5VI~6[2IߢG4']@,FxR$ byG)U<\<uO]Dϔ0wd`&ܸ07Iap8>02ĐyMa ףe*EU.XHt+Z|[i*gU])Q}ElK⋅ -bk Xr f%j-ZSL't.Y4W %!TqCyPJ~㾠k49T>!>6mH>*QYn&u ![~L!`{T~jX0[5Jd52V"AӉU+#Y:*Q$}x 9(T.[CK!ث1&mSgv]ٍ TYnjAdn#EY@6[_X&VY NQ+1]W03yoUI4f8 1 "XDd`; f.R𥥰yjHMԱjHfěDFH )"9pɺ{id%D,PR> h (,Ƌb?DIe$MB[K|jnGvLт&[*XBɯԃGeZBp3aj?$v -n/6!c㶝tU>_M36aYxKՔI݌5Ҕ*7ZW2EDvۦeKf N`$%( 328пe0:X;`X."T_΋tb0ᴍ(aqy{IPP`UCĻ c/&f_'kM9 "*W0K2~S GW\!Mx-r}='߲a)A+d ys&,`x&@Y-4R@(^vC3zgjJ6aܶпzz#t:Ժ0} gBpIiT1U7E(0c0SB{fkHFq4( #ЋQr4N:O%,zZeqfuq̢?23|! q%xMsZݣ39`,-muac%k5k 7(z,+A7f8 uZz^C#8J:W>0+_;/Fw2L 5WSr.aN4;3MB ց8"܊!̞lrZBV#Θ1 xmf뮒V0)ofGM+1H^ { iIp< ~`[6yfCNqMi_pJe]2Xefko#3g Q yrXP;<@~Lڪ:9=WKZ:gJx (8qk}Aݧ#LD0qlX Bڬ,!y ?3`yMWi{>*6s)EudY?Xq.7A&f*arYJHqLMa )ǸNh#[, iؾy5أ+|ATG1X9&f  yODəc47[Kh7tun}X.YҾIb,p.NVZx/A2aB[~Vܗ,:x Q1RXg̘~`4uB|}Z-Nќf!Z!&(̀h9l,/~H.aS!ѲdI<Í99c0Gs_!m,k,.<ۂny7wXjK1ۮ4Xªes\xmR^{!.Vǩ J&97vZIBOX6J×ŘO vcR'\X#13oNwBܴX&9"f `.B*bS3TN:tm /pdǪ.o0yۇo` w.!*NBTO.6iyv;YW_YZMσxHߏa֦$Q]X* k=k?"I9w2Ђ|I8F{.^?YL@Ag!7UŬT )>o JEW<)!g0V<dBpvrc2C,\)[mt~-bFzS-"D[j(m;FKiV˥%5×4QAa tD-&+M I^O2-QPE-*U$7@R/~Ԗy9sx`z~.8 1ɛf]8d|MJ;&ј|)\M=A e PvΧy̪͍An1ᄎ׈jJyP6cVc.:Os{eart3uE݃xeѺȤuKJ1\ oV;x '+z DXIy!Nf΅.j)U>r!̀GLw!bB4a"j[1JyU0rρ%Ǟ ?S4$&' ɤJG"HbSXwlƼ4y.mu{o?V'׬*NE3tH uwrk2/M/*PX5nfZ[sNGz7xRx?R%b-fxA88FPǽ;i\mzDm3c%=ICb]]O;? xW1KIzAsP]DI7Ki6?M,߅3p0"iSNA{PXhz |fÙxBo6~!|Nr.\DI9#%T]4cQg=%jJHwOAkW^~-x8U\[1Cs%e}yabUT)F08*O@YwD?yrQ\Yˌ⋳~˛u37fش_h hfX\Ϳ?uQQ|L dЬx/0?T]ƭa Sf|[j0nY+&koɬjǔG}F>e2*S"K"@4{Xs3,]҃DsZ3܎똥% i,GfL^0ِ7رytoWaQ-fbIIuH1Vp(]GT7:煊`R0Qf%-_pn[N0!Nscuk؉i`@`I-2&&Pz=NmUDlRM -kD:a= =i.h{s D|mmZn 뇶>;DQ Н)5Z5hWT4,.Օ%bW&OgZB8f =d=e+O{%=?{}04/Pl q,y~\8317`JmnTXתA++a#(1'![X;D6y1HEOw@ܢaCE3I)O&HSl2]ׯ6G:)Ư|1:cYdf7R3=u~iĝVriOb]bhOϯ*P \ n q9I Qy_}IC*zN1Wf669t8i ˜:o?~j@mfa.Nmp,c*oԀL &T,Xc`YektrRn;sEWrmt 71 G<$[; xGA(l;,>cH8gg7jR(NҜer,2t2Snob"vߚJ C*)/l7ٲ wK6R-"=nR)*Cfczrgy5e">[2HH|X$!awXI 0Ik>,wl4IڸbN |P(+ǩ-~>qiDp"pL/yOc^ż>݋|wc3fҭvC<02{A"̣ PY__nVŁoC3gx獵F3ce6Tq2el$t51Dt@9JY?"xB(8οK *So]XhGbUX]E .oB [Ct$uń,lgo`И0]BWm4פ cO()W2DD=_ڬ~sC I5NHbėE`xjfO7̧( @JA=` <6,b^a(j_Ȕdqb}O}XJx6z-y?|+<<%ՃpIɢv2OԆV窙֦_ gf + ='TNvx`y; eQR <+V:Z9կlLWmM%Na{^M7VyOҏ&Tf">οz'fVMĊ}~8cid#@wio9BG'ijqlp) t9eJG?ƭ T[,J%0¿(T^Lb"o#>ix{F fDj]gKљ루:~wH; <+"0H1EMdIN*l?.X{ ;L+0+Oҵ'tCգhTisc#juw2s]r;G31IiSASD$T妨գ(`O5I]IzW5͂?hp&W6k1I,:"Ǽ~Y3G :0TCm 6s87MqX"<\CTr??n~ u!uvGƄ7{+Q4n설㶆6^?XW-tؖTP1V+H&M#=_MA Ǯ>}Ёj98n #+r-Gt(n,@[t"^fAH_ MϿl]63=L5rΆI>%c *3tum”VCZIh%lrQh@E,5l ~'s<TIlb.EFwn\X;FƒOXM62ĔwP:+ k%h0?ý#VL'#98Q1L4vw헲=*?F9\=O,KCF7(WPr'TΝy[Ƿ~xߤGnWxyjPR6)U%R8ރBcFfڛCL*ɳ\yB]?0+B1ô *;!?&"SC3YfHW \/J<ˠ`GLjnu<T- /RFv1aUPxX%x|X`CbqY ?BܙIpn(GM(?oɩj}SjhӍ▻^y4H._(7%AgDvա{^X z,* G$r Qjű#"ac{@LhlXt;O;I xph2g֧K_eXp%/KLQlv+DdwL%N~Sxr&EVHr,\A׺`X){M(tuY0/|)S4hY B^.gqPYőHCjk(I ƸL ۪5qLEq_Rqp4Jeų߭3ars:䬓06 h`LܴuU[ul1)YVkUu1^K)0!7_D@fMHpIJܢΓ>kfr-x0F(P'Z5]QmR,FVWXCR\dIsDC%0يi&O.8,k|=XfՓ3OP!?[NۂqiQnR6-WFUI'?CGΖhX"F {ߜ.(cEeb1с m/,38lq gt7cF;pˉ.!25P7'#L6,E+|(j1}3caC 8ƈ{&g,b5^;?Ǐ +`A2ԠU3g;m#lˑюp|[#*JW`.~+Tg_ZfU|)"$`# %&~keQmI8EۖO*8̱Q 4X0=>79kܔE%Az'PKlǪ f2/''U5muz0>wy"\| d{ϼk 6g#VjI2;_MCM41QeD" ~!/6] Qhcۑ!Ċ#b\"Rx깾es(oe =RgDaǴ"/XԊޔh.,LGm Rg: ӾUC'TQnldH< ꂲht.xY{Bn"5LN"5Xe&1t!>Ul3^, 8p**u1fi8@Px$n!jfucv%r`dxWXmT0on vIG"Oz,uDTbt*=Gq ):(A'E_O:%{yW#RP4pb-+%n;ϯEԌ?u8*ЦVq%֕)b"h=gly$(bP &Yp''ВuCFb~ExU0Xؔ>W5!^GM" TʍNbYLD"w}g(#MEt <dhXszq8' (z6)65)žAq"lxT/W͠|1DMS,Yt^4Ӧ C6dK (z_Tӈ~σ@2'vv4Ze3;Jaf4zmVH׵uԌDS5j {8,XdždžB Oa-فO]ͧ"OddB"@B5r3NCAaZQ ͜%WjTT)Pt ') [xPj,I{qM%9CGHGjKLW\cn"AXbF➊YC\r~LCTJEܝfMX 1S܀ eMZF.~Te({6KjhZ1?V1o8aiuԊQjGZK "SOE==,$SR'^䩛%Z8T KϠO⻶G},LSekwM)TiCʒQ19)PȮ΍Yj/iךrn1 zpBĭ6ql1A'|7haľZշڦm0({ڍP.a9 - c|qE,GD,tuf;FX 1ڡjK>2Ah!s7%8{-cIv]fj< K}y&sdtP5x}K\4 L `} )`&s^>ٍQ,JIQL8-R-9M~X敼an.18HzϋO1ӤN4L J:Ry(Yd=ټw9B9LlB7Xo(OOr1[X]ΒOe39>we}BՊ(j/%^Eād<`uF)6֣8$;yoMÍH\o,s|L' _43m舲6Q50J<]Op{*,C"G>w=Z*,΅hCG_XEpMX}I1ʨ!+h+cng!85_BsY !E.Uj 4}/7j>*r`- -ZOL6/)EW<Ҙ?z!aQ ˘#&C ƛen -TG@H|$VEiPI!P#S:a؊ÀWJYS״j#e2pO=nm#OQ:w5Plˤw^ItqU]3'mԚ7p1f2(V,p(򓂱iX.Ŕ^*" <Y'wϥ\Kmf$d kY~JpUanZ1m)]b˦=zCIIl;̚kX' DL~蛺pSK(r.d9i':eU,=d{.$ Bm]$fڍb7-Z CX; , {lL}?=HdXЋԓ2m =wYz^e> nГpyformex-0.8.6/pyformex/doc/html/searchindex.js0000644000211500021150000051000411705104257021451 0ustar benebene00000000000000Search.setIndex({objects:{"":{turtle:[77,0,1,""],simple:[72,0,1,""],tetgen:[33,0,1,""],image:[74,0,1,""],mesh_ext:[46,0,1,""],addViewport:[82,2,1,""],colors:[28,0,1,""],flavia:[57,0,1,""],"export":[5,0,1,""],isopar:[43,0,1,""],fe_post:[30,0,1,""],dxf:[2,0,1,""],trisurface:[6,0,1,""],nurbs:[35,0,1,""],tools:[45,0,1,""],imagearray:[36,0,1,""],canvas:[19,0,1,""],lima:[60,0,1,""],layout:[82,2,1,""],postproc:[25,0,1,""],script:[4,0,1,""],menu:[70,0,1,""],linkViewport:[82,2,1,""],flatkeydb:[22,0,1,""],inertia:[62,0,1,""],removeViewport:[82,2,1,""],camera:[24,0,1,""],actors:[31,0,1,""],toolbar:[80,0,1,""],marks:[15,0,1,""],units:[55,0,1,""],fe_abq:[12,0,1,""],config:[23,0,1,""],utils:[53,0,1,""],decors:[1,0,1,""],draw:[32,0,1,""],elements:[38,0,1,""],arraytools:[9,0,1,""],sendmail:[13,0,1,""],odict:[47,0,1,""],connectivity:[66,0,1,""],objects:[18,0,1,""],collection:[63,0,1,""],scriptMenu:[37,0,1,""],formex:[73,0,1,""],mydict:[0,0,1,""],properties:[14,0,1,""],viewport:[68,0,1,""],geomtools:[40,0,1,""],datareader:[44,0,1,""],imageViewer:[3,0,1,""],colorscale:[7,0,1,""],geometry:[61,0,1,""],imagecolor:[59,0,1,""],curve:[79,0,1,""],timer:[76,0,1,""],widgets:[29,0,1,""],project:[20,0,1,""],section2d:[48,0,1,""],mesh:[26,0,1,""],coords:[39,0,1,""],plot2d:[52,0,1,""],olist:[41,0,1,""],gluttext:[71,0,1,""],fe:[50,0,1,""]},"actors.BboxActor":{setLineStipple:[31,1,1,""],drawGL:[31,1,1,""],setLineWidth:[31,1,1,""],setColor:[31,1,1,""]},"curve.Arc3":{spherical:[79,1,1,""],approx:[79,1,1,""],projectOnCylinder:[79,1,1,""],toSpherical:[79,1,1,""],bump:[79,1,1,""],setProp:[79,1,1,""],toFormex:[79,1,1,""],replace:[79,1,1,""],bump1:[79,1,1,""],bump2:[79,1,1,""],transformCS:[79,1,1,""],swapAxes:[79,1,1,""],endPoints:[79,1,1,""],rot:[79,1,1,""],directionsAt:[79,1,1,""],scale:[79,1,1,""],mapd:[79,1,1,""],projectOnSphere:[79,1,1,""],superSpherical:[79,1,1,""],affine:[79,1,1,""],subPoints:[79,1,1,""],write:[79,1,1,""],projectOnPlane:[79,1,1,""],translate:[79,1,1,""],shear:[79,1,1,""],map:[79,1,1,""],centered:[79,1,1,""],sub_points_2:[79,1,1,""],reflect:[79,1,1,""],cylindrical:[79,1,1,""],copy:[79,1,1,""],sub_directions:[79,1,1,""],rollAxes:[79,1,1,""],sub_directions_2:[79,1,1,""],rotate:[79,1,1,""],trl:[79,1,1,""],pointsAt:[79,1,1,""],flare:[79,1,1,""],hyperCylindrical:[79,1,1,""],resized:[79,1,1,""],isopar:[79,1,1,""],addNoise:[79,1,1,""],length:[79,1,1,""],map1:[79,1,1,""],toCylindrical:[79,1,1,""],position:[79,1,1,""],egg:[79,1,1,""],align:[79,1,1,""]},"objects.DrawableObjects":{printval:[18,1,1,""],set:[18,1,1,""],forget:[18,1,1,""],object_type:[18,1,1,""],setProp:[18,1,1,""],undoChanges:[18,1,1,""],readFromFile:[18,1,1,""],drawAnnotation:[18,1,1,""],printbbox:[18,1,1,""],removeAnnotation:[18,1,1,""],check:[18,1,1,""],append:[18,1,1,""],writeToFile:[18,1,1,""],changeValues:[18,1,1,""],hasAnnotation:[18,1,1,""],odict:[18,1,1,""],delProp:[18,1,1,""],toggleAnnotation:[18,1,1,""],ask:[18,1,1,""],ask1:[18,1,1,""],remember:[18,1,1,""],clear:[18,1,1,""],drawChanges:[18,1,1,""],keep:[18,1,1,""],listAll:[18,1,1,""]},"viewport.CursorShapeHandler":{setCursorShape:[68,1,1,""],setCursorShapeFromFunc:[68,1,1,""]},"marks.AxesMark":{pickGL:[15,1,1,""],setLineStipple:[15,1,1,""],setColor:[15,1,1,""],setLineWidth:[15,1,1,""]},"flatkeydb.FlatDB":{newRecord:[22,1,1,""],splitKeyValue:[22,1,1,""],readFile:[22,1,1,""],insert:[22,1,1,""],checkKeys:[22,1,1,""],key_error_handler:[22,1,1,""],parse:[22,1,1,""],writeFile:[22,1,1,""],checkRecord:[22,1,1,""],append:[22,1,1,""],parseLine:[22,1,1,""],match:[22,1,1,""],record_error_handler:[22,1,1,""]},"project.Project":{load:[20,1,1,""],convert:[20,1,1,""],uncompress:[20,1,1,""],header_data:[20,1,1,""],readHeader:[20,1,1,""],save:[20,1,1,""],"delete":[20,1,1,""]},colors:{colorName:[28,2,1,""],GLColor:[28,2,1,""],GREY:[28,2,1,""],RGBcolor:[28,2,1,""],closestColorName:[28,2,1,""],RGBA:[28,2,1,""],WEBcolor:[28,2,1,""]},"widgets.SaveImageDialog":{getFilename:[29,1,1,""]},"coords.CoordinateSystem":{spherical:[39,1,1,""],origin:[39,1,1,""],set:[39,1,1,""],bsphere:[39,1,1,""],toSpherical:[39,1,1,""],bump:[39,1,1,""],closestToPoint:[39,1,1,""],replace:[39,1,1,""],bump1:[39,1,1,""],bump2:[39,1,1,""],transformCS:[39,1,1,""],swapAxes:[39,1,1,""],superSpherical:[39,1,1,""],rot:[39,1,1,""],append:[39,1,1,""],directionalWidth:[39,1,1,""],scale:[39,1,1,""],mapd:[39,1,1,""],projectOnSphere:[39,1,1,""],projectOnSurface:[39,1,1,""],rep:[39,1,1,""],affine:[39,1,1,""],projectOnPlane:[39,1,1,""],map1:[39,1,1,""],replicate:[39,1,1,""],split:[39,1,1,""],distanceFromLine:[39,1,1,""],test:[39,1,1,""],centroid:[39,1,1,""],fromfile:[39,4,1,""],ncoords:[39,1,1,""],directionalExtremes:[39,1,1,""],shear:[39,1,1,""],concatenate:[39,4,1,""],map:[39,1,1,""],distanceFromPoint:[39,1,1,""],cylindrical:[39,1,1,""],fprint:[39,1,1,""],centered:[39,1,1,""],center:[39,1,1,""],interpolate:[39,1,1,""],reflect:[39,1,1,""],y:[39,1,1,""],dsize:[39,1,1,""],fuse:[39,1,1,""],axes:[39,1,1,""],x:[39,1,1,""],average:[39,1,1,""],inertia:[39,1,1,""],rollAxes:[39,1,1,""],distanceFromPlane:[39,1,1,""],pshape:[39,1,1,""],rotate:[39,1,1,""],translate:[39,1,1,""],trl:[39,1,1,""],directionalSize:[39,1,1,""],sizes:[39,1,1,""],flare:[39,1,1,""],align:[39,1,1,""],projectOnCylinder:[39,1,1,""],isopar:[39,1,1,""],points:[39,1,1,""],toCylindrical:[39,1,1,""],match:[39,1,1,""],fromstring:[39,4,1,""],npoints:[39,1,1,""],position:[39,1,1,""],z:[39,1,1,""],egg:[39,1,1,""],addNoise:[39,1,1,""],bbox:[39,1,1,""]},"properties.Database":{get:[14,1,1,""],readDatabase:[14,1,1,""],setdefault:[14,1,1,""],update:[14,1,1,""]},"widgets.InputBool":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},menu:{resetGUI:[70,2,1,""],ActionList:[70,3,1,""],Menu:[70,3,1,""],DAction:[70,3,1,""],createMenuData:[70,2,1,""],BaseMenu:[70,3,1,""],resetWarnings:[70,2,1,""],MenuBar:[70,3,1,""]},"actors.Actor":{drawGL:[31,1,1,""],setLineStipple:[31,1,1,""],bbox:[31,1,1,""],setLineWidth:[31,1,1,""],setColor:[31,1,1,""]},"widgets.ProjectSelection":{getFilename:[29,1,1,""]},marks:{AxesMark:[15,3,1,""],Mark:[15,3,1,""],TextMark:[15,3,1,""],MarkList:[15,3,1,""]},"actors.PlaneActor":{setLineStipple:[31,1,1,""],setColor:[31,1,1,""],setLineWidth:[31,1,1,""],drawGL:[31,1,1,""]},"dxf.DxfExporter":{layer:[2,1,1,""],section:[2,1,1,""],write:[2,1,1,""],entities:[2,1,1,""],close:[2,1,1,""],line:[2,1,1,""],endSection:[2,1,1,""],out:[2,1,1,""]},"properties.MaterialDB":{setdefault:[14,1,1,""],readDatabase:[14,1,1,""],update:[14,1,1,""],get:[14,1,1,""]},"widgets.InputInteger":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""],show:[29,1,1,""]},"nurbs.Coords4":{normalize:[35,1,1,""],z:[35,1,1,""],toCoords:[35,1,1,""],actor:[35,1,1,""],npoints:[35,1,1,""],bbox:[35,1,1,""],w:[35,1,1,""],deNormalize:[35,1,1,""],y:[35,1,1,""],x:[35,1,1,""],ncoords:[35,1,1,""]},"coords.BoundVectors":{spherical:[39,1,1,""],set:[39,1,1,""],bsphere:[39,1,1,""],toSpherical:[39,1,1,""],bump:[39,1,1,""],sizes:[39,1,1,""],closestToPoint:[39,1,1,""],replace:[39,1,1,""],directionalSize:[39,1,1,""],bump2:[39,1,1,""],transformCS:[39,1,1,""],swapAxes:[39,1,1,""],superSpherical:[39,1,1,""],append:[39,1,1,""],directionalWidth:[39,1,1,""],scale:[39,1,1,""],heads:[39,1,1,""],mapd:[39,1,1,""],projectOnSphere:[39,1,1,""],projectOnSurface:[39,1,1,""],rep:[39,1,1,""],affine:[39,1,1,""],fuse:[39,1,1,""],projectOnPlane:[39,1,1,""],points:[39,1,1,""],distanceFromPlane:[39,1,1,""],split:[39,1,1,""],rot:[39,1,1,""],distanceFromLine:[39,1,1,""],test:[39,1,1,""],replicate:[39,1,1,""],fromfile:[39,4,1,""],translate:[39,1,1,""],rotate:[39,1,1,""],shear:[39,1,1,""],concatenate:[39,4,1,""],map:[39,1,1,""],distanceFromPoint:[39,1,1,""],y:[39,1,1,""],fprint:[39,1,1,""],centered:[39,1,1,""],interpolate:[39,1,1,""],reflect:[39,1,1,""],average:[39,1,1,""],dsize:[39,1,1,""],cylindrical:[39,1,1,""],bbox:[39,1,1,""],x:[39,1,1,""],inertia:[39,1,1,""],rollAxes:[39,1,1,""],center:[39,1,1,""],pshape:[39,1,1,""],ncoords:[39,1,1,""],trl:[39,1,1,""],origins:[39,1,1,""],directionalExtremes:[39,1,1,""],flare:[39,1,1,""],align:[39,1,1,""],vectors:[39,1,1,""],projectOnCylinder:[39,1,1,""],isopar:[39,1,1,""],bump1:[39,1,1,""],map1:[39,1,1,""],toCylindrical:[39,1,1,""],match:[39,1,1,""],fromstring:[39,4,1,""],npoints:[39,1,1,""],position:[39,1,1,""],centroid:[39,1,1,""],z:[39,1,1,""],egg:[39,1,1,""],addNoise:[39,1,1,""]},"menu.Menu":{index:[70,1,1,""],nextitem:[70,1,1,""],item:[70,1,1,""],actionList:[70,1,1,""],create_insert_action:[70,1,1,""],insert_menu:[70,1,1,""],insert_action:[70,1,1,""],removeItem:[70,1,1,""],action:[70,1,1,""],insert_sep:[70,1,1,""],insertItems:[70,1,1,""]},odict:{ODict:[47,3,1,""],KeyedList:[47,3,1,""]},"connectivity.Connectivity":{resolve:[66,1,1,""],removeDegenerate:[66,1,1,""],nelems:[66,1,1,""],listNonDegenerate:[66,1,1,""],adjacency:[66,1,1,""],selectNodes:[66,1,1,""],insertLevel:[66,1,1,""],listUnique:[66,1,1,""],inverse:[66,1,1,""],combine:[66,1,1,""],listDuplicate:[66,1,1,""],testDegenerate:[66,1,1,""],report:[66,1,1,""],nplex:[66,1,1,""],testDuplicate:[66,1,1,""],reduceDegenerate:[66,1,1,""],removeDuplicate:[66,1,1,""],listDegenerate:[66,1,1,""],reorder:[66,1,1,""]},"scriptMenu.ScriptMenu":{loadFiles:[37,1,1,""],getFiles:[37,1,1,""],run:[37,1,1,""],runAllRandom:[37,1,1,""],fileName:[37,1,1,""],reload:[37,1,1,""],add:[37,1,1,""],runAllNext:[37,1,1,""],runScript:[37,1,1,""],runAll:[37,1,1,""],runRandom:[37,1,1,""],filterFiles:[37,1,1,""],runAllFiles:[37,1,1,""],classify:[37,1,1,""],runNext:[37,1,1,""]},"odict.ODict":{sort:[47,1,1,""],keys:[47,1,1,""],items:[47,1,1,""],update:[47,1,1,""],pos:[47,1,1,""],values:[47,1,1,""]},scriptMenu:{createScriptMenu:[37,2,1,""],scriptKeywords:[37,2,1,""],extractKeyword:[37,2,1,""],ScriptMenu:[37,3,1,""],sortSets:[37,2,1,""]},mydict:{raiseKeyError:[0,2,1,""],cascade:[0,2,1,""],CDict:[0,3,1,""],Dict:[0,3,1,""],returnNone:[0,2,1,""]},"formex.Formex":{spherical:[73,1,1,""],cclip:[73,1,1,""],shape:[73,1,1,""],nnodes:[73,1,1,""],transformCS:[73,1,1,""],ros:[73,1,1,""],rot:[73,1,1,""],rosette:[73,1,1,""],projectOnSphere:[73,1,1,""],affine:[73,1,1,""],elbbox:[73,1,1,""],nelems:[73,1,1,""],shear:[73,1,1,""],read:[73,4,1,""],circulize1:[73,1,1,""],splitProp:[73,1,1,""],unique:[73,1,1,""],rotate:[73,1,1,""],trl:[73,1,1,""],projectOnCylinder:[73,1,1,""],remove:[73,1,1,""],translatem:[73,1,1,""],toCylindrical:[73,1,1,""],hyperCylindrical:[73,1,1,""],view:[73,1,1,""],resized:[73,1,1,""],bump:[73,1,1,""],bump1:[73,1,1,""],bump2:[73,1,1,""],coord:[73,1,1,""],mirror:[73,1,1,""],append:[73,1,1,""],asFormexWithProp:[73,1,1,""],scale:[73,1,1,""],removeDuplicate:[73,1,1,""],rep:[73,1,1,""],cutWithPlane:[73,1,1,""],fromfile:[73,4,1,""],cselect:[73,1,1,""],concatenate:[73,4,1,""],asArray:[73,1,1,""],toSurface:[73,1,1,""],point2str:[73,4,1,""],cylindrical:[73,1,1,""],circulize:[73,1,1,""],element2str:[73,4,1,""],reverse:[73,1,1,""],flare:[73,1,1,""],selectNodes:[73,1,1,""],npoints:[73,1,1,""],addNoise:[73,1,1,""],ndim:[73,1,1,""],divide:[73,1,1,""],toSpherical:[73,1,1,""],projectOnPlane:[73,1,1,""],replace:[73,1,1,""],extrude:[73,1,1,""],select:[73,1,1,""],replicate:[73,1,1,""],translate:[73,1,1,""],centered:[73,1,1,""],asFormex:[73,1,1,""],centroids:[73,1,1,""],copy:[73,1,1,""],rollAxes:[73,1,1,""],info:[73,1,1,""],replic:[73,1,1,""],align:[73,1,1,""],isopar:[73,1,1,""],setPrintFunction:[73,4,1,""],point:[73,1,1,""],maxProp:[73,1,1,""],clip:[73,1,1,""],setProp:[73,1,1,""],swapAxes:[73,1,1,""],superSpherical:[73,1,1,""],mapd:[73,1,1,""],write:[73,1,1,""],points:[73,1,1,""],split:[73,1,1,""],intersectionWithPlane:[73,1,1,""],test:[73,1,1,""],withProp:[73,1,1,""],shrink:[73,1,1,""],replic2:[73,1,1,""],map:[73,1,1,""],toMesh:[73,1,1,""],propSet:[73,1,1,""],whereProp:[73,1,1,""],reflect:[73,1,1,""],fuse:[73,1,1,""],nplex:[73,1,1,""],getProp:[73,1,1,""],fromstring:[73,4,1,""],vertices:[73,1,1,""],element:[73,1,1,""],map1:[73,1,1,""],position:[73,1,1,""],egg:[73,1,1,""]},"coords.Coords":{spherical:[39,1,1,""],set:[39,1,1,""],ncoords:[39,1,1,""],bsphere:[39,1,1,""],toSpherical:[39,1,1,""],bump:[39,1,1,""],rot:[39,1,1,""],replace:[39,1,1,""],bump1:[39,1,1,""],bump2:[39,1,1,""],transformCS:[39,1,1,""],swapAxes:[39,1,1,""],distanceFromPlane:[39,1,1,""],superSpherical:[39,1,1,""],append:[39,1,1,""],directionalWidth:[39,1,1,""],scale:[39,1,1,""],mapd:[39,1,1,""],projectOnSphere:[39,1,1,""],projectOnSurface:[39,1,1,""],rep:[39,1,1,""],affine:[39,1,1,""],projectOnPlane:[39,1,1,""],map1:[39,1,1,""],centroid:[39,1,1,""],split:[39,1,1,""],distanceFromLine:[39,1,1,""],test:[39,1,1,""],replicate:[39,1,1,""],fromfile:[39,4,1,""],translate:[39,1,1,""],directionalExtremes:[39,1,1,""],npoints:[39,1,1,""],match:[39,1,1,""],concatenate:[39,4,1,""],map:[39,1,1,""],distanceFromPoint:[39,1,1,""],cylindrical:[39,1,1,""],fprint:[39,1,1,""],centered:[39,1,1,""],interpolate:[39,1,1,""],reflect:[39,1,1,""],average:[39,1,1,""],dsize:[39,1,1,""],fuse:[39,1,1,""],closestToPoint:[39,1,1,""],x:[39,1,1,""],inertia:[39,1,1,""],rollAxes:[39,1,1,""],center:[39,1,1,""],pshape:[39,1,1,""],rotate:[39,1,1,""],trl:[39,1,1,""],directionalSize:[39,1,1,""],sizes:[39,1,1,""],fromstring:[39,4,1,""],align:[39,1,1,""],projectOnCylinder:[39,1,1,""],isopar:[39,1,1,""],points:[39,1,1,""],toCylindrical:[39,1,1,""],shear:[39,1,1,""],flare:[39,1,1,""],y:[39,1,1,""],position:[39,1,1,""],z:[39,1,1,""],egg:[39,1,1,""],addNoise:[39,1,1,""],bbox:[39,1,1,""]},"marks.Mark":{pickGL:[15,1,1,""],setLineStipple:[15,1,1,""],drawGL:[15,1,1,""],setLineWidth:[15,1,1,""],setColor:[15,1,1,""]},"widgets.ArrayModel":{flags:[29,1,1,""]},"widgets.InputList":{setNone:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],setAll:[29,1,1,""],setSelected:[29,1,1,""],setChecked:[29,1,1,""],value:[29,1,1,""],text:[29,1,1,""]},"widgets.InputLabel":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},"curve.Arc":{spherical:[79,1,1,""],approx:[79,1,1,""],resized:[79,1,1,""],toSpherical:[79,1,1,""],bump:[79,1,1,""],setProp:[79,1,1,""],toFormex:[79,1,1,""],replace:[79,1,1,""],bump1:[79,1,1,""],bump2:[79,1,1,""],transformCS:[79,1,1,""],swapAxes:[79,1,1,""],endPoints:[79,1,1,""],rot:[79,1,1,""],directionsAt:[79,1,1,""],scale:[79,1,1,""],mapd:[79,1,1,""],projectOnSphere:[79,1,1,""],superSpherical:[79,1,1,""],affine:[79,1,1,""],subPoints:[79,1,1,""],write:[79,1,1,""],projectOnPlane:[79,1,1,""],translate:[79,1,1,""],shear:[79,1,1,""],map:[79,1,1,""],centered:[79,1,1,""],sub_points_2:[79,1,1,""],reflect:[79,1,1,""],cylindrical:[79,1,1,""],copy:[79,1,1,""],sub_directions:[79,1,1,""],rollAxes:[79,1,1,""],sub_directions_2:[79,1,1,""],rotate:[79,1,1,""],trl:[79,1,1,""],pointsAt:[79,1,1,""],flare:[79,1,1,""],hyperCylindrical:[79,1,1,""],projectOnCylinder:[79,1,1,""],isopar:[79,1,1,""],addNoise:[79,1,1,""],length:[79,1,1,""],map1:[79,1,1,""],toCylindrical:[79,1,1,""],position:[79,1,1,""],egg:[79,1,1,""],align:[79,1,1,""]},"canvas.Canvas":{setFgColor:[19,1,1,""],zoom:[19,1,1,""],addActor:[19,1,1,""],do_lighting:[19,1,1,""],zoomCentered:[19,1,1,""],addHighlight:[19,1,1,""],setCamera:[19,1,1,""],setLineWidth:[19,1,1,""],removeAnnotations:[19,1,1,""],createBackground:[19,1,1,""],setSlColor:[19,1,1,""],removeHighlights:[19,1,1,""],setMaterial:[19,1,1,""],saveBuffer:[19,1,1,""],zoomAll:[19,1,1,""],showBuffer:[19,1,1,""],setBbox:[19,1,1,""],draw_focus_rectangle:[19,1,1,""],setBgColor:[19,1,1,""],has_lighting:[19,1,1,""],project:[19,1,1,""],removeDecorations:[19,1,1,""],removeAnnotation:[19,1,1,""],resetDefaults:[19,1,1,""],setPointSize:[19,1,1,""],unProject:[19,1,1,""],setLineStipple:[19,1,1,""],glinit:[19,1,1,""],redrawAll:[19,1,1,""],setTriade:[19,1,1,""],setDefaults:[19,1,1,""],addAnnotation:[19,1,1,""],begin_2D_drawing:[19,1,1,""],resetLighting:[19,1,1,""],end_2D_drawing:[19,1,1,""],addDecoration:[19,1,1,""],removeActors:[19,1,1,""],glupdate:[19,1,1,""],removeHighlight:[19,1,1,""],clear:[19,1,1,""],draw_cursor:[19,1,1,""],zoomRectangle:[19,1,1,""],remove:[19,1,1,""],removeAll:[19,1,1,""],removeDecoration:[19,1,1,""],removeActor:[19,1,1,""],setAmbient:[19,1,1,""],setRenderMode:[19,1,1,""],display:[19,1,1,""]},coords:{origin:[39,2,1,""],pattern:[39,2,1,""],CoordinateSystem:[39,3,1,""],xpattern:[39,2,1,""],sweepCoords:[39,2,1,""],Coords:[39,3,1,""],bbox:[39,2,1,""],BoundVectors:[39,3,1,""]},"colorscale.ColorScale":{color:[7,1,1,""],scale:[7,1,1,""]},"widgets.InputItem":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},"decors.LineDrawing":{pickGL:[1,1,1,""],setLineStipple:[1,1,1,""],setColor:[1,1,1,""],setLineWidth:[1,1,1,""]},turtle:{reset:[77,2,1,""],play:[77,2,1,""],cosd:[77,2,1,""],pop:[77,2,1,""],an:[77,2,1,""],go:[77,2,1,""],mv:[77,2,1,""],fd:[77,2,1,""],st:[77,2,1,""],push:[77,2,1,""],sind:[77,2,1,""],ro:[77,2,1,""]},"camera.Camera":{setArea:[24,1,1,""],loadProjection:[24,1,1,""],lock:[24,1,1,""],move:[24,1,1,""],transform:[24,1,1,""],project:[24,1,1,""],tilt:[24,1,1,""],getRot:[24,1,1,""],toWorld:[24,1,1,""],setLens:[24,1,1,""],dolly:[24,1,1,""],setAngles:[24,1,1,""],pan:[24,1,1,""],setTracking:[24,1,1,""],saveModelView:[24,1,1,""],unProject:[24,1,1,""],setCenter:[24,1,1,""],setModelView:[24,1,1,""],getCenter:[24,1,1,""],getDist:[24,1,1,""],setRotation:[24,1,1,""],report:[24,1,1,""],resetArea:[24,1,1,""],loadCurrentRotation:[24,1,1,""],rotate:[24,1,1,""],setClip:[24,1,1,""],setDist:[24,1,1,""],transArea:[24,1,1,""],setPerspective:[24,1,1,""],loadModelView:[24,1,1,""],zoomArea:[24,1,1,""]},"widgets.ListSelection":{add_input:[29,1,1,""],setValue:[29,1,1,""],add_tab:[29,1,1,""],updateData:[29,1,1,""],acceptData:[29,1,1,""],show:[29,1,1,""],getResults:[29,1,1,""],value:[29,1,1,""],add_items:[29,1,1,""],add_group:[29,1,1,""],getResult:[29,1,1,""],timeout:[29,1,1,""],timedOut:[29,1,1,""]},"curve.BezierSpline":{spherical:[79,1,1,""],approx:[79,1,1,""],projectOnCylinder:[79,1,1,""],pointsOn:[79,1,1,""],toSpherical:[79,1,1,""],projectOnPlane:[79,1,1,""],length_intgrnd:[79,1,1,""],setProp:[79,1,1,""],toFormex:[79,1,1,""],sub_curvature:[79,1,1,""],sub_directions_2:[79,1,1,""],align:[79,1,1,""],bump2:[79,1,1,""],transformCS:[79,1,1,""],swapAxes:[79,1,1,""],endPoints:[79,1,1,""],superSpherical:[79,1,1,""],directionsAt:[79,1,1,""],scale:[79,1,1,""],toMesh:[79,1,1,""],mapd:[79,1,1,""],lengths:[79,1,1,""],rot:[79,1,1,""],affine:[79,1,1,""],pointsOff:[79,1,1,""],subPoints:[79,1,1,""],write:[79,1,1,""],bump:[79,1,1,""],translate:[79,1,1,""],shear:[79,1,1,""],map:[79,1,1,""],extend:[79,1,1,""],approx_by_subdivision:[79,1,1,""],centered:[79,1,1,""],sub_points_2:[79,1,1,""],reflect:[79,1,1,""],replace:[79,1,1,""],cylindrical:[79,1,1,""],part:[79,1,1,""],copy:[79,1,1,""],sub_directions:[79,1,1,""],rollAxes:[79,1,1,""],reverse:[79,1,1,""],sub_points:[79,1,1,""],bump1:[79,1,1,""],rotate:[79,1,1,""],trl:[79,1,1,""],pointsAt:[79,1,1,""],flare:[79,1,1,""],hyperCylindrical:[79,1,1,""],resized:[79,1,1,""],isopar:[79,1,1,""],projectOnSphere:[79,1,1,""],length:[79,1,1,""],map1:[79,1,1,""],toCylindrical:[79,1,1,""],position:[79,1,1,""],egg:[79,1,1,""],addNoise:[79,1,1,""]},"decors.Decoration":{pickGL:[1,1,1,""],setLineStipple:[1,1,1,""],drawGL:[1,1,1,""],setLineWidth:[1,1,1,""],setColor:[1,1,1,""]},"widgets.InputPush":{setValue:[29,1,1,""],name:[29,1,1,""],text:[29,1,1,""],setText:[29,1,1,""],value:[29,1,1,""],setIcon:[29,1,1,""]},tetgen:{nextFilename:[33,2,1,""],readTetgen:[33,2,1,""],readNodesBlock:[33,2,1,""],readElemsBlock:[33,2,1,""],writeNodes:[33,2,1,""],getInts:[33,2,1,""],runTetgen:[33,2,1,""],addElem:[33,2,1,""],readNeigh:[33,2,1,""],readSurface:[33,2,1,""],stripLine:[33,2,1,""],readPolyFile:[33,2,1,""],readEleFile:[33,2,1,""],writeSurface:[33,2,1,""],readFacesBlock:[33,2,1,""],readSmeshFacetsBlock:[33,2,1,""],readSmeshFile:[33,2,1,""],writeSmesh:[33,2,1,""],readFaceFile:[33,2,1,""],readNodeFile:[33,2,1,""],skipComments:[33,2,1,""]},postproc:{frameScale:[25,2,1,""]},mesh_ext:{alt_report:[46,2,1,""],partitionByAngle:[46,2,1,""],partitionByNodeFront:[46,2,1,""],area:[46,2,1,""],largestByConnection:[46,2,1,""],splitByConnection:[46,2,1,""],rings:[46,2,1,""],correctNegativeVolumes:[46,2,1,""],nodeFront:[46,2,1,""],partitionByConnection:[46,2,1,""],report:[46,2,1,""],initialize:[46,2,1,""],scaledJacobian:[46,2,1,""],areas:[46,2,1,""]},"nurbs.NurbsSurface":{bbox:[35,1,1,""],pointsAt:[35,1,1,""],actor:[35,1,1,""]},"actors.AxesActor":{setLineStipple:[31,1,1,""],drawGL:[31,1,1,""],setLineWidth:[31,1,1,""],setColor:[31,1,1,""]},"properties.PropertyDB":{getProp:[14,1,1,""],setdefault:[14,1,1,""],Prop:[14,1,1,""],setMaterialDB:[14,1,1,""],setSectionDB:[14,1,1,""],update:[14,1,1,""],delProp:[14,1,1,""],get:[14,1,1,""],nodeProp:[14,1,1,""],elemProp:[14,1,1,""]},"widgets.TableModel":{flags:[29,1,1,""]},"curve.PolyLine":{spherical:[79,1,1,""],approx:[79,1,1,""],resized:[79,1,1,""],avgDirections:[79,1,1,""],bump:[79,1,1,""],copy:[79,1,1,""],setProp:[79,1,1,""],toFormex:[79,1,1,""],replace:[79,1,1,""],sub_directions_2:[79,1,1,""],align:[79,1,1,""],bump2:[79,1,1,""],transformCS:[79,1,1,""],swapAxes:[79,1,1,""],close:[79,1,1,""],superSpherical:[79,1,1,""],endPoints:[79,1,1,""],rot:[79,1,1,""],directionsAt:[79,1,1,""],append:[79,1,1,""],affine:[79,1,1,""],scale:[79,1,1,""],sub_points:[79,1,1,""],mapd:[79,1,1,""],lengths:[79,1,1,""],open:[79,1,1,""],toSpherical:[79,1,1,""],subPoints:[79,1,1,""],sub_directions:[79,1,1,""],write:[79,1,1,""],projectOnPlane:[79,1,1,""],cutWithPlane:[79,1,1,""],map1:[79,1,1,""],split:[79,1,1,""],translate:[79,1,1,""],shear:[79,1,1,""],map:[79,1,1,""],toMesh:[79,1,1,""],centered:[79,1,1,""],atLength:[79,1,1,""],reflect:[79,1,1,""],cylindrical:[79,1,1,""],directions:[79,1,1,""],sub_points_2:[79,1,1,""],sub_points2:[79,1,1,""],rollAxes:[79,1,1,""],trl:[79,1,1,""],bump1:[79,1,1,""],rotate:[79,1,1,""],reverse:[79,1,1,""],pointsAt:[79,1,1,""],flare:[79,1,1,""],hyperCylindrical:[79,1,1,""],vectors:[79,1,1,""],isopar:[79,1,1,""],projectOnSphere:[79,1,1,""],length:[79,1,1,""],projectOnCylinder:[79,1,1,""],toCylindrical:[79,1,1,""],position:[79,1,1,""],egg:[79,1,1,""],addNoise:[79,1,1,""]},"decors.Triade":{pickGL:[1,1,1,""],setLineStipple:[1,1,1,""],drawGL:[1,1,1,""],setLineWidth:[1,1,1,""],setColor:[1,1,1,""]},trisurface:{find_nodes:[6,2,1,""],find_first_nodes:[6,2,1,""],degenerate:[6,2,1,""],read_off:[6,2,1,""],Rectangle:[6,2,1,""],Cube:[6,2,1,""],write_smesh:[6,2,1,""],read_stl:[6,2,1,""],off_to_tet:[6,2,1,""],read_stla:[6,2,1,""],read_error:[6,2,1,""],curvature:[6,2,1,""],read_ascii_large:[6,2,1,""],fillBorder:[6,2,1,""],read_gts:[6,2,1,""],TriSurface:[6,3,1,""],read_gambit_neutral:[6,2,1,""],find_triangles:[6,2,1,""],surface_volume:[6,2,1,""],remove_triangles:[6,2,1,""],stlConvert:[6,2,1,""],write_stla:[6,2,1,""],find_row:[6,2,1,""],Sphere:[6,2,1,""]},nurbs:{frenet:[35,2,1,""],Coords4:[35,3,1,""],globalInterpolationCurve:[35,2,1,""],knotVector:[35,2,1,""],pointsOnBezierCurve:[35,2,1,""],curveToNurbs:[35,2,1,""],uniformParamValues:[35,2,1,""],NurbsSurface:[35,3,1,""],deCasteljou:[35,2,1,""],toCoords4:[35,2,1,""],NurbsCurve:[35,3,1,""]},"widgets.InputRadio":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},"marks.TextMark":{pickGL:[15,1,1,""],setLineStipple:[15,1,1,""],setColor:[15,1,1,""],setLineWidth:[15,1,1,""]},canvas:{Canvas:[19,3,1,""],glLineStipple:[19,2,1,""],gl_pickbuffer:[19,2,1,""],glSmooth:[19,2,1,""],glFlat:[19,2,1,""],CanvasSettings:[19,3,1,""],glEnable:[19,2,1,""],onOff:[19,2,1,""]},lima:{lima:[60,2,1,""],Lima:[60,3,1,""]},"viewport.CanvasMouseHandler":{getMouseFunc:[68,1,1,""]},"utils.NameSequence":{peek:[53,1,1,""],glob:[53,1,1,""],next:[53,1,1,""]},gluttext:{glutFontHeight:[71,2,1,""],glutBitmapLength:[71,2,1,""],glutFont:[71,2,1,""],glutDrawText:[71,2,1,""],glutRenderText:[71,2,1,""],glutSelectFont:[71,2,1,""]},"menu.BaseMenu":{index:[70,1,1,""],nextitem:[70,1,1,""],item:[70,1,1,""],actionList:[70,1,1,""],create_insert_action:[70,1,1,""],insert_menu:[70,1,1,""],insert_action:[70,1,1,""],removeItem:[70,1,1,""],action:[70,1,1,""],insert_sep:[70,1,1,""],insertItems:[70,1,1,""]},decors:{ColorLegend:[1,3,1,""],drawGrid:[1,2,1,""],LineDrawing:[1,3,1,""],drawLine:[1,2,1,""],Text:[1,5,1,""],Mark:[1,3,1,""],Decoration:[1,3,1,""],GlutText:[1,3,1,""],Grid:[1,3,1,""],Triade:[1,3,1,""],drawRect:[1,2,1,""],drawDot:[1,2,1,""],Line:[1,3,1,""],drawRectangle:[1,2,1,""],Rectangle:[1,3,1,""]},sendmail:{message:[13,2,1,""],sendmail:[13,2,1,""]},"widgets.InputFSlider":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""],show:[29,1,1,""]},"decors.Rectangle":{pickGL:[1,1,1,""],setLineStipple:[1,1,1,""],setColor:[1,1,1,""],setLineWidth:[1,1,1,""]},"curve.NaturalSpline":{spherical:[79,1,1,""],approx:[79,1,1,""],resized:[79,1,1,""],toSpherical:[79,1,1,""],projectOnPlane:[79,1,1,""],setProp:[79,1,1,""],toFormex:[79,1,1,""],replace:[79,1,1,""],bump1:[79,1,1,""],bump2:[79,1,1,""],transformCS:[79,1,1,""],swapAxes:[79,1,1,""],endPoints:[79,1,1,""],superSpherical:[79,1,1,""],directionsAt:[79,1,1,""],scale:[79,1,1,""],mapd:[79,1,1,""],projectOnSphere:[79,1,1,""],rot:[79,1,1,""],affine:[79,1,1,""],subPoints:[79,1,1,""],write:[79,1,1,""],bump:[79,1,1,""],translate:[79,1,1,""],shear:[79,1,1,""],map:[79,1,1,""],centered:[79,1,1,""],sub_points_2:[79,1,1,""],reflect:[79,1,1,""],cylindrical:[79,1,1,""],copy:[79,1,1,""],sub_directions:[79,1,1,""],rollAxes:[79,1,1,""],sub_directions_2:[79,1,1,""],rotate:[79,1,1,""],trl:[79,1,1,""],pointsAt:[79,1,1,""],flare:[79,1,1,""],align:[79,1,1,""],projectOnCylinder:[79,1,1,""],isopar:[79,1,1,""],addNoise:[79,1,1,""],length:[79,1,1,""],map1:[79,1,1,""],toCylindrical:[79,1,1,""],position:[79,1,1,""],egg:[79,1,1,""],hyperCylindrical:[79,1,1,""]},"isopar.Isopar":{transform:[43,1,1,""]},"curve.Spiral":{spherical:[79,1,1,""],approx:[79,1,1,""],resized:[79,1,1,""],toSpherical:[79,1,1,""],projectOnPlane:[79,1,1,""],setProp:[79,1,1,""],toFormex:[79,1,1,""],replace:[79,1,1,""],bump1:[79,1,1,""],bump2:[79,1,1,""],transformCS:[79,1,1,""],swapAxes:[79,1,1,""],endPoints:[79,1,1,""],superSpherical:[79,1,1,""],directionsAt:[79,1,1,""],scale:[79,1,1,""],mapd:[79,1,1,""],projectOnSphere:[79,1,1,""],rot:[79,1,1,""],affine:[79,1,1,""],subPoints:[79,1,1,""],write:[79,1,1,""],bump:[79,1,1,""],translate:[79,1,1,""],hyperCylindrical:[79,1,1,""],shear:[79,1,1,""],map:[79,1,1,""],sub_points:[79,1,1,""],centered:[79,1,1,""],sub_points_2:[79,1,1,""],reflect:[79,1,1,""],cylindrical:[79,1,1,""],copy:[79,1,1,""],sub_directions:[79,1,1,""],rollAxes:[79,1,1,""],sub_directions_2:[79,1,1,""],rotate:[79,1,1,""],trl:[79,1,1,""],pointsAt:[79,1,1,""],flare:[79,1,1,""],align:[79,1,1,""],projectOnCylinder:[79,1,1,""],isopar:[79,1,1,""],length:[79,1,1,""],map1:[79,1,1,""],toCylindrical:[79,1,1,""],position:[79,1,1,""],egg:[79,1,1,""],addNoise:[79,1,1,""]},objects:{draw_bbox:[18,2,1,""],draw_node_numbers:[18,2,1,""],draw_object_name:[18,2,1,""],Objects:[18,3,1,""],draw_nodes:[18,2,1,""],DrawableObjects:[18,3,1,""],draw_free_edges:[18,2,1,""],draw_elem_numbers:[18,2,1,""]},"curve.Line":{spherical:[79,1,1,""],approx:[79,1,1,""],projectOnCylinder:[79,1,1,""],toSpherical:[79,1,1,""],bump:[79,1,1,""],copy:[79,1,1,""],setProp:[79,1,1,""],toFormex:[79,1,1,""],replace:[79,1,1,""],sub_directions_2:[79,1,1,""],align:[79,1,1,""],bump2:[79,1,1,""],avgDirections:[79,1,1,""],transformCS:[79,1,1,""],swapAxes:[79,1,1,""],close:[79,1,1,""],superSpherical:[79,1,1,""],endPoints:[79,1,1,""],open:[79,1,1,""],directionsAt:[79,1,1,""],append:[79,1,1,""],resized:[79,1,1,""],toMesh:[79,1,1,""],mapd:[79,1,1,""],lengths:[79,1,1,""],rot:[79,1,1,""],affine:[79,1,1,""],subPoints:[79,1,1,""],sub_directions:[79,1,1,""],write:[79,1,1,""],projectOnPlane:[79,1,1,""],cutWithPlane:[79,1,1,""],split:[79,1,1,""],translate:[79,1,1,""],shear:[79,1,1,""],map:[79,1,1,""],sub_points:[79,1,1,""],centered:[79,1,1,""],sub_points_2:[79,1,1,""],reflect:[79,1,1,""],cylindrical:[79,1,1,""],scale:[79,1,1,""],directions:[79,1,1,""],atLength:[79,1,1,""],sub_points2:[79,1,1,""],rollAxes:[79,1,1,""],reverse:[79,1,1,""],bump1:[79,1,1,""],rotate:[79,1,1,""],trl:[79,1,1,""],pointsAt:[79,1,1,""],flare:[79,1,1,""],hyperCylindrical:[79,1,1,""],vectors:[79,1,1,""],isopar:[79,1,1,""],projectOnSphere:[79,1,1,""],length:[79,1,1,""],map1:[79,1,1,""],toCylindrical:[79,1,1,""],position:[79,1,1,""],egg:[79,1,1,""],addNoise:[79,1,1,""]},"marks.MarkList":{pickGL:[15,1,1,""],drawpick:[15,1,1,""],setLineStipple:[15,1,1,""],setColor:[15,1,1,""],setLineWidth:[15,1,1,""]},"geometry.Geometry":{spherical:[61,1,1,""],projectOnCylinder:[61,1,1,""],toSpherical:[61,1,1,""],bump:[61,1,1,""],replace:[61,1,1,""],bump1:[61,1,1,""],bump2:[61,1,1,""],transformCS:[61,1,1,""],swapAxes:[61,1,1,""],rot:[61,1,1,""],scale:[61,1,1,""],mapd:[61,1,1,""],projectOnSphere:[61,1,1,""],superSpherical:[61,1,1,""],affine:[61,1,1,""],write:[61,1,1,""],projectOnPlane:[61,1,1,""],translate:[61,1,1,""],shear:[61,1,1,""],map:[61,1,1,""],centered:[61,1,1,""],reflect:[61,1,1,""],cylindrical:[61,1,1,""],copy:[61,1,1,""],align:[61,1,1,""],rollAxes:[61,1,1,""],rotate:[61,1,1,""],trl:[61,1,1,""],flare:[61,1,1,""],hyperCylindrical:[61,1,1,""],resized:[61,1,1,""],isopar:[61,1,1,""],map1:[61,1,1,""],toCylindrical:[61,1,1,""],position:[61,1,1,""],egg:[61,1,1,""],addNoise:[61,1,1,""]},inertia:{inertia:[62,2,1,""],center:[62,2,1,""],centroids:[62,2,1,""],principal:[62,2,1,""]},toolbar:{updatePerspectiveButton:[80,2,1,""],addButton:[80,2,1,""],addActionButtons:[80,2,1,""],updateTransparencyButton:[80,2,1,""],updateNormalsButton:[80,2,1,""],updateLightButton:[80,2,1,""],addCameraButtons:[80,2,1,""],toggleButton:[80,2,1,""],timeout:[80,2,1,""],removeButton:[80,2,1,""],addTimeoutButton:[80,2,1,""],updateButton:[80,2,1,""]},datareader:{splitFloat:[44,2,1,""],readData:[44,2,1,""]},"properties.ElemSection":{setdefault:[14,1,1,""],get:[14,1,1,""],addSection:[14,1,1,""],update:[14,1,1,""],addMaterial:[14,1,1,""],computeSection:[14,1,1,""]},geometry:{Geometry:[61,3,1,""]},"widgets.InputDialog":{add_input:[29,1,1,""],add_items:[29,1,1,""],timeout:[29,1,1,""],show:[29,1,1,""],acceptData:[29,1,1,""],timedOut:[29,1,1,""],getResults:[29,1,1,""],getResult:[29,1,1,""],updateData:[29,1,1,""],add_group:[29,1,1,""],add_tab:[29,1,1,""]},"nurbs.NurbsCurve":{derivatives:[35,1,1,""],pointsAt:[35,1,1,""],knotPoints:[35,1,1,""],actor:[35,1,1,""],bbox:[35,1,1,""],decompose:[35,1,1,""],insertKnots:[35,1,1,""]},timer:{Timer:[76,3,1,""]},olist:{concatenate:[41,2,1,""],difference:[41,2,1,""],symdifference:[41,2,1,""],union:[41,2,1,""],collectOnLength:[41,2,1,""],intersection:[41,2,1,""],roll:[41,2,1,""],select:[41,2,1,""],flatten:[41,2,1,""]},collection:{Collection:[63,3,1,""]},"canvas.CanvasSettings":{reset:[19,1,1,""],setdefault:[19,1,1,""],get:[19,1,1,""],update:[19,1,1,""],checkDict:[19,4,1,""],setMode:[19,1,1,""],setDefault:[19,1,1,""]},"widgets.ImageView":{showImage:[29,1,1,""]},simple:{sector:[72,2,1,""],cylinder:[72,2,1,""],triangle:[72,2,1,""],polygon:[72,2,1,""],point:[72,2,1,""],cuboid:[72,2,1,""],connectCurves:[72,2,1,""],regularGrid:[72,2,1,""],sphere2:[72,2,1,""],sphere3:[72,2,1,""],shape:[72,2,1,""],rectangle:[72,2,1,""],line:[72,2,1,""],circle:[72,2,1,""],rect:[72,2,1,""],quadraticCurve:[72,2,1,""]},image:{save_main_window:[74,2,1,""],imageFormats:[74,2,1,""],save_window:[74,2,1,""],createMovie:[74,2,1,""],initialize:[74,2,1,""],imageFormatFromExt:[74,2,1,""],saveIcon:[74,2,1,""],saveNext:[74,2,1,""],autoSaveOn:[74,2,1,""],saveMovie:[74,2,1,""],save_canvas:[74,2,1,""],checkImageFormat:[74,2,1,""],save_rect:[74,2,1,""],save:[74,2,1,""],saveImage:[74,2,1,""]},"properties.EdgeLoad":{update:[14,1,1,""],setdefault:[14,1,1,""],get:[14,1,1,""]},"actors.SphereActor":{setLineStipple:[31,1,1,""],drawGL:[31,1,1,""],setLineWidth:[31,1,1,""],setColor:[31,1,1,""]},"config.Config":{setdefault:[23,1,1,""],get:[23,1,1,""],read:[23,1,1,""],update:[23,1,1,""],write:[23,1,1,""],keys:[23,1,1,""]},"trisurface.TriSurface":{spherical:[6,1,1,""],toFormex:[6,1,1,""],nonManifoldEdges:[6,1,1,""],cclip:[6,1,1,""],nEdgeAdjacent:[6,1,1,""],shape:[6,1,1,""],transformCS:[6,1,1,""],partitionByConnection:[6,1,1,""],removeDuplicate:[6,1,1,""],addMeanNodes:[6,1,1,""],compact:[6,1,1,""],edgeAngles:[6,1,1,""],walkEdgeFront:[6,1,1,""],affine:[6,1,1,""],coarsen:[6,1,1,""],getEdges:[6,1,1,""],shear:[6,1,1,""],matchFaces:[6,1,1,""],renumberElems:[6,1,1,""],read:[6,4,1,""],isManifold:[6,1,1,""],getElemEdges:[6,1,1,""],splitProp:[6,1,1,""],report:[6,1,1,""],nNodeConnected:[6,1,1,""],areas:[6,1,1,""],extrude:[6,1,1,""],rotate:[6,1,1,""],removeDegenerate:[6,1,1,""],trl:[6,1,1,""],projectOnCylinder:[6,1,1,""],avgNodes:[6,1,1,""],nodeConnections:[6,1,1,""],smallestAltitude:[6,1,1,""],edgeAdjacency:[6,1,1,""],getNodes:[6,1,1,""],toCylindrical:[6,1,1,""],correctNegativeVolumes:[6,1,1,""],nfaces:[6,1,1,""],addNodes:[6,1,1,""],getElems:[6,1,1,""],align:[6,1,1,""],partitionByAngle:[6,1,1,""],resized:[6,1,1,""],map:[6,1,1,""],bump:[6,1,1,""],bump1:[6,1,1,""],bump2:[6,1,1,""],borderEdgeNrs:[6,1,1,""],getBorder:[6,1,1,""],append:[6,1,1,""],scale:[6,1,1,""],stats:[6,1,1,""],nNodeAdjacent:[6,1,1,""],rot:[6,1,1,""],edgeConnections:[6,1,1,""],rings:[6,1,1,""],withProp:[6,1,1,""],nodeFront:[6,1,1,""],cutWithPlane:[6,1,1,""],cselect:[6,1,1,""],propSet:[6,1,1,""],concatenate:[6,4,1,""],getFaces:[6,1,1,""],toSurface:[6,1,1,""],getFreeEdgesMesh:[6,1,1,""],fillBorder:[6,1,1,""],matchCentroids:[6,1,1,""],offset:[6,1,1,""],connectedElements:[6,1,1,""],inertia:[6,1,1,""],longestEdge:[6,1,1,""],scaledJacobian:[6,1,1,""],matchCoords:[6,1,1,""],reverse:[6,1,1,""],getPoints:[6,1,1,""],flare:[6,1,1,""],getCells:[6,1,1,""],selectNodes:[6,1,1,""],projectOnSphere:[6,1,1,""],cylindrical:[6,1,1,""],splitRandom:[6,1,1,""],getBorderMesh:[6,1,1,""],aspectRatio:[6,1,1,""],withoutProp:[6,1,1,""],addNoise:[6,1,1,""],toSpherical:[6,1,1,""],projectOnPlane:[6,1,1,""],nodeAdjacency:[6,1,1,""],sweep:[6,1,1,""],replace:[6,1,1,""],getFreeEntities:[6,1,1,""],"boolean":[6,1,1,""],edgeCosAngles:[6,1,1,""],nEdgeConnected:[6,1,1,""],select:[6,1,1,""],setType:[6,1,1,""],smoothLowPass:[6,1,1,""],area:[6,1,1,""],getCoords:[6,1,1,""],shortestEdge:[6,1,1,""],reduceDegenerate:[6,1,1,""],translate:[6,1,1,""],fixNormals:[6,1,1,""],meanNodes:[6,1,1,""],isClosedManifold:[6,1,1,""],avgVertexNormals:[6,1,1,""],centered:[6,1,1,""],convert:[6,1,1,""],centroids:[6,1,1,""],volume:[6,1,1,""],renumber:[6,1,1,""],copy:[6,1,1,""],rollAxes:[6,1,1,""],setElems:[6,1,1,""],splitDegenerate:[6,1,1,""],nedges:[6,1,1,""],hyperCylindrical:[6,1,1,""],smooth:[6,1,1,""],clipAtPlane:[6,1,1,""],isopar:[6,1,1,""],borderEdges:[6,1,1,""],distanceOfPoints:[6,1,1,""],checkBorder:[6,1,1,""],connect:[6,1,1,""],maxProp:[6,1,1,""],clip:[6,1,1,""],surfaceType:[6,1,1,""],setProp:[6,1,1,""],getLowerEntities:[6,1,1,""],borderNodeNrs:[6,1,1,""],refine:[6,1,1,""],swapAxes:[6,1,1,""],superSpherical:[6,1,1,""],check:[6,1,1,""],smoothLaplaceHC:[6,1,1,""],checkSelfIntersectionsWithTetgen:[6,1,1,""],mapd:[6,1,1,""],border:[6,1,1,""],write:[6,1,1,""],split:[6,1,1,""],intersectionWithPlane:[6,1,1,""],test:[6,1,1,""],partitionByEdgeFront:[6,1,1,""],facetArea:[6,1,1,""],edgeFront:[6,1,1,""],largestByConnection:[6,1,1,""],setEdgesAndFaces:[6,1,1,""],splitByConnection:[6,1,1,""],convertRandom:[6,1,1,""],areaNormals:[6,1,1,""],reflect:[6,1,1,""],slice:[6,1,1,""],curvature:[6,1,1,""],revolve:[6,1,1,""],setCoords:[6,1,1,""],getFreeEntitiesMesh:[6,1,1,""],getProp:[6,1,1,""],partitionByNodeFront:[6,1,1,""],vertices:[6,1,1,""],fuse:[6,1,1,""],map1:[6,1,1,""],volumes:[6,1,1,""],growSelection:[6,1,1,""],position:[6,1,1,""],egg:[6,1,1,""]},tools:{getObjectItems:[45,2,1,""],getPartition:[45,2,1,""],partitionCollection:[45,2,1,""],getCollection:[45,2,1,""],exportObjects:[45,2,1,""],growCollection:[45,2,1,""]},"decors.GlutText":{pickGL:[1,1,1,""],setLineStipple:[1,1,1,""],drawGL:[1,1,1,""],setLineWidth:[1,1,1,""],setColor:[1,1,1,""]},properties:{FindListItem:[14,2,1,""],SectionDB:[14,3,1,""],RemoveListItem:[14,2,1,""],Database:[14,3,1,""],CoordSystem:[14,3,1,""],MaterialDB:[14,3,1,""],checkString:[14,2,1,""],ElemLoad:[14,3,1,""],checkIdValue:[14,2,1,""],Amplitude:[14,3,1,""],ElemSection:[14,3,1,""],EdgeLoad:[14,3,1,""],checkArrayOrIdValue:[14,2,1,""],PropertyDB:[14,3,1,""]},"actors.GeomActor":{setColor:[31,1,1,""],setLineStipple:[31,1,1,""],vertices:[31,1,1,""],pickGL:[31,1,1,""],setAlpha:[31,1,1,""],setLineWidth:[31,1,1,""],setBkColor:[31,1,1,""],select:[31,1,1,""],drawGL:[31,1,1,""]},script:{rename:[4,2,1,""],named:[4,2,1,""],export2:[4,2,1,""],executeScript:[4,2,1,""],startGui:[4,2,1,""],requireRevision:[4,2,1,""],writable:[4,2,1,""],"export":[4,2,1,""],setPrefs:[4,2,1,""],forget:[4,2,1,""],getcfg:[4,2,1,""],quit:[4,2,1,""],stopatbreakpt:[4,2,1,""],isWritable:[4,2,1,""],system:[4,2,1,""],exit:[4,2,1,""],processArgs:[4,2,1,""],forgetAll:[4,2,1,""],grepSource:[4,2,1,""],play:[4,2,1,""],playFile:[4,2,1,""],writeGeomFile:[4,2,1,""],playScript:[4,2,1,""],ask:[4,2,1,""],breakpt:[4,2,1,""],checkRevision:[4,2,1,""],chdir:[4,2,1,""],ack:[4,2,1,""],readGeomFile:[4,2,1,""],printall:[4,2,1,""],Globals:[4,2,1,""],error:[4,2,1,""],runtime:[4,2,1,""],listAll:[4,2,1,""]},"collection.Collection":{set:[63,1,1,""],get:[63,1,1,""],keys:[63,1,1,""],items:[63,1,1,""],remove:[63,1,1,""],add:[63,1,1,""],has_key:[63,1,1,""]},"fe_abq.Interaction":{setdefault:[12,1,1,""],update:[12,1,1,""],get:[12,1,1,""]},"widgets.MessageBox":{getResult:[29,1,1,""],addCheck:[29,1,1,""]},camera:{tand:[24,2,1,""],Camera:[24,3,1,""],ViewAngles:[24,3,1,""]},"properties.SectionDB":{update:[14,1,1,""],get:[14,1,1,""],setdefault:[14,1,1,""],readDatabase:[14,1,1,""]},widgets:{groupInputItem:[29,2,1,""],InputFSlider:[29,3,1,""],InputTab:[29,3,1,""],ProjectSelection:[29,3,1,""],InputList:[29,3,1,""],InputForm:[29,3,1,""],addTimeOut:[29,2,1,""],maxSize:[29,2,1,""],SaveImageDialog:[29,3,1,""],InputBool:[29,3,1,""],MessageBox:[29,3,1,""],defaultItemType:[29,2,1,""],InputInteger:[29,3,1,""],InputDialog:[29,3,1,""],ArrayModel:[29,3,1,""],WarningBox:[29,3,1,""],ListSelection:[29,3,1,""],TableModel:[29,3,1,""],InputPoint:[29,3,1,""],InputCombo:[29,3,1,""],updateText:[29,2,1,""],InputItem:[29,3,1,""],InputInfo:[29,3,1,""],tabInputItem:[29,2,1,""],TableDialog:[29,3,1,""],compatInputItem:[29,2,1,""],getColor:[29,2,1,""],InputButton:[29,3,1,""],InputString:[29,3,1,""],FileSelection:[29,3,1,""],simpleInputItem:[29,2,1,""],InputFloat:[29,3,1,""],InputFont:[29,3,1,""],InputWidget:[29,3,1,""],GenericDialog:[29,3,1,""],selectFont:[29,2,1,""],ImageView:[29,3,1,""],TextBox:[29,3,1,""],InputIVector:[29,3,1,""],inputAny:[29,2,1,""],ButtonBox:[29,3,1,""],CoordsBox:[29,3,1,""],InputPush:[29,3,1,""],convertInputItem:[29,2,1,""],InputGroup:[29,3,1,""],InputLabel:[29,3,1,""],InputSlider:[29,3,1,""],Tabs:[29,3,1,""],InputColor:[29,3,1,""],updateDialogItems:[29,2,1,""],InputText:[29,3,1,""],Table:[29,3,1,""],dialogButtons:[29,2,1,""],InputRadio:[29,3,1,""]},"viewport.QtCanvas":{getMouseFunc:[68,1,1,""],pick_actors:[68,1,1,""],pick_numbers:[68,1,1,""],do_lighting:[68,1,1,""],showBuffer:[68,1,1,""],saveBuffer:[68,1,1,""],mouse_pick:[68,1,1,""],mouse_draw_line:[68,1,1,""],setCursorShape:[68,1,1,""],cancel_draw:[68,1,1,""],setDefaults:[68,1,1,""],addAnnotation:[68,1,1,""],resetLighting:[68,1,1,""],pick_elements:[68,1,1,""],glupdate:[68,1,1,""],removeHighlight:[68,1,1,""],draw_cursor:[68,1,1,""],zoom:[68,1,1,""],remove:[68,1,1,""],removeAll:[68,1,1,""],drawLinesInter:[68,1,1,""],draw_state_rect:[68,1,1,""],wait_selection:[68,1,1,""],setRenderMode:[68,1,1,""],edit_drawing:[68,1,1,""],mouseReleaseEvent:[68,1,1,""],setFgColor:[68,1,1,""],setPickable:[68,1,1,""],zoomCentered:[68,1,1,""],dynarot:[68,1,1,""],setPointSize:[68,1,1,""],setMaterial:[68,1,1,""],removeAnnotation:[68,1,1,""],wait_drawing:[68,1,1,""],cancel_drawing:[68,1,1,""],resetDefaults:[68,1,1,""],setLineStipple:[68,1,1,""],mouseMoveEvent:[68,1,1,""],accept_draw:[68,1,1,""],end_2D_drawing:[68,1,1,""],addDecoration:[68,1,1,""],finish_selection:[68,1,1,""],dynapan:[68,1,1,""],removeDecoration:[68,1,1,""],resetOptions:[68,1,1,""],pick:[68,1,1,""],removeActor:[68,1,1,""],cancel_selection:[68,1,1,""],pick_parts:[68,1,1,""],wheel_zoom:[68,1,1,""],setCursorShapeFromFunc:[68,1,1,""],glinit:[68,1,1,""],finish_drawing:[68,1,1,""],pick_points:[68,1,1,""],zoomAll:[68,1,1,""],emit_done:[68,1,1,""],start_selection:[68,1,1,""],setBbox:[68,1,1,""],has_lighting:[68,1,1,""],removeDecorations:[68,1,1,""],mouse_draw:[68,1,1,""],removeHighlights:[68,1,1,""],begin_2D_drawing:[68,1,1,""],addActor:[68,1,1,""],draw_focus_rectangle:[68,1,1,""],accept_drawing:[68,1,1,""],zoomRectangle:[68,1,1,""],project:[68,1,1,""],setTriade:[68,1,1,""],setAmbient:[68,1,1,""],display:[68,1,1,""],draw_state_line:[68,1,1,""],mousePressEvent:[68,1,1,""],start_draw:[68,1,1,""],pickNumbers:[68,1,1,""],dynazoom:[68,1,1,""],setCamera:[68,1,1,""],finish_draw:[68,1,1,""],setLineWidth:[68,1,1,""],removeAnnotations:[68,1,1,""],createBackground:[68,1,1,""],emit_cancel:[68,1,1,""],pick_edges:[68,1,1,""],start_drawing:[68,1,1,""],addHighlight:[68,1,1,""],idraw:[68,1,1,""],mouse_rectangle_zoom:[68,1,1,""],setBgColor:[68,1,1,""],unProject:[68,1,1,""],redrawAll:[68,1,1,""],setOptions:[68,1,1,""],wheelEvent:[68,1,1,""],removeActors:[68,1,1,""],accept_selection:[68,1,1,""],clear:[68,1,1,""],setSlColor:[68,1,1,""]},dxf:{readDXF:[2,2,1,""],DxfExporter:[2,3,1,""],exportDXF:[2,2,1,""],importDXF:[2,2,1,""],convertDXF:[2,2,1,""]},"properties.ElemLoad":{get:[14,1,1,""],update:[14,1,1,""],setdefault:[14,1,1,""]},"widgets.InputIVector":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},"widgets.CoordsBox":{setValues:[29,1,1,""],getValues:[29,1,1,""]},formex:{cut2AtPlane:[73,2,1,""],pointsAt:[73,2,1,""],intersectionLinesWithPlane:[73,2,1,""],cut3AtPlane:[73,2,1,""],lpattern:[73,2,1,""],interpolate:[73,2,1,""],connect:[73,2,1,""],Formex:[73,3,1,""],cutElements3AtPlane:[73,2,1,""]},"objects.Objects":{readFromFile:[18,1,1,""],set:[18,1,1,""],ask1:[18,1,1,""],remember:[18,1,1,""],append:[18,1,1,""],clear:[18,1,1,""],object_type:[18,1,1,""],undoChanges:[18,1,1,""],odict:[18,1,1,""],writeToFile:[18,1,1,""],printval:[18,1,1,""],changeValues:[18,1,1,""],ask:[18,1,1,""],printbbox:[18,1,1,""],keep:[18,1,1,""],check:[18,1,1,""],listAll:[18,1,1,""],forget:[18,1,1,""]},viewport:{MultiCanvas:[68,3,1,""],CanvasMouseHandler:[68,3,1,""],dotpr:[68,2,1,""],QtCanvas:[68,3,1,""],projection:[68,2,1,""],CursorShapeHandler:[68,3,1,""],OpenGLFormat:[68,2,1,""],setOpenGLFormat:[68,2,1,""],length:[68,2,1,""],FramedGridLayout:[68,3,1,""],NewMultiCanvas:[68,3,1,""]},"widgets.InputCombo":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},"widgets.InputSlider":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""],show:[29,1,1,""]},"elements.Element":{getEntities:[38,1,1,""]},colorscale:{ColorLegend:[7,3,1,""],ColorScale:[7,3,1,""]},imagecolor:{image2glcolor:[59,2,1,""]},"actors.TranslatedActor":{setLineStipple:[31,1,1,""],setColor:[31,1,1,""],setLineWidth:[31,1,1,""]},isopar:{evaluate:[43,2,1,""],Isopar:[43,3,1,""]},project:{Project:[20,3,1,""],pickle_load:[20,2,1,""],find_global:[20,2,1,""]},"mesh.Mesh":{spherical:[26,1,1,""],toFormex:[26,1,1,""],addMeanNodes:[26,1,1,""],cclip:[26,1,1,""],nEdgeAdjacent:[26,1,1,""],transformCS:[26,1,1,""],removeDuplicate:[26,1,1,""],compact:[26,1,1,""],projectOnSphere:[26,1,1,""],affine:[26,1,1,""],getEdges:[26,1,1,""],shear:[26,1,1,""],matchFaces:[26,1,1,""],sweep:[26,1,1,""],splitProp:[26,1,1,""],report:[26,1,1,""],nNodeConnected:[26,1,1,""],extrude:[26,1,1,""],rotate:[26,1,1,""],removeDegenerate:[26,1,1,""],trl:[26,1,1,""],resized:[26,1,1,""],avgNodes:[26,1,1,""],nodeConnections:[26,1,1,""],renumberElems:[26,1,1,""],edgeAdjacency:[26,1,1,""],toCylindrical:[26,1,1,""],addNodes:[26,1,1,""],getElems:[26,1,1,""],hyperCylindrical:[26,1,1,""],projectOnCylinder:[26,1,1,""],getCoords:[26,1,1,""],bump:[26,1,1,""],bump1:[26,1,1,""],bump2:[26,1,1,""],connect:[26,1,1,""],scale:[26,1,1,""],nNodeAdjacent:[26,1,1,""],rot:[26,1,1,""],cselect:[26,1,1,""],concatenate:[26,4,1,""],getFaces:[26,1,1,""],toSurface:[26,1,1,""],getFreeEdgesMesh:[26,1,1,""],cylindrical:[26,1,1,""],matchCentroids:[26,1,1,""],matchCoords:[26,1,1,""],reverse:[26,1,1,""],getPoints:[26,1,1,""],flare:[26,1,1,""],getCells:[26,1,1,""],selectNodes:[26,1,1,""],splitRandom:[26,1,1,""],getBorderMesh:[26,1,1,""],withoutProp:[26,1,1,""],addNoise:[26,1,1,""],toSpherical:[26,1,1,""],projectOnPlane:[26,1,1,""],nodeAdjacency:[26,1,1,""],getElemEdges:[26,1,1,""],replace:[26,1,1,""],getFreeEntities:[26,1,1,""],getNodes:[26,1,1,""],nEdgeConnected:[26,1,1,""],select:[26,1,1,""],setType:[26,1,1,""],reduceDegenerate:[26,1,1,""],translate:[26,1,1,""],meanNodes:[26,1,1,""],centered:[26,1,1,""],convert:[26,1,1,""],centroids:[26,1,1,""],volume:[26,1,1,""],renumber:[26,1,1,""],copy:[26,1,1,""],rollAxes:[26,1,1,""],splitDegenerate:[26,1,1,""],align:[26,1,1,""],smooth:[26,1,1,""],clipAtPlane:[26,1,1,""],isopar:[26,1,1,""],getBorder:[26,1,1,""],maxProp:[26,1,1,""],clip:[26,1,1,""],setProp:[26,1,1,""],getLowerEntities:[26,1,1,""],edgeConnections:[26,1,1,""],swapAxes:[26,1,1,""],superSpherical:[26,1,1,""],mapd:[26,1,1,""],write:[26,1,1,""],test:[26,1,1,""],withProp:[26,1,1,""],map:[26,1,1,""],propSet:[26,1,1,""],convertRandom:[26,1,1,""],reflect:[26,1,1,""],fuse:[26,1,1,""],revolve:[26,1,1,""],getFreeEntitiesMesh:[26,1,1,""],getProp:[26,1,1,""],nedges:[26,1,1,""],map1:[26,1,1,""],volumes:[26,1,1,""],position:[26,1,1,""],egg:[26,1,1,""]},flavia:{readResults:[57,2,1,""],readFlavia:[57,2,1,""],readResult:[57,2,1,""],createFeResult:[57,2,1,""],readMesh:[57,2,1,""],readCoords:[57,2,1,""],readElems:[57,2,1,""]},plot2d:{showHistogram:[52,2,1,""],createHistogram:[52,2,1,""]},"fe_abq.AbqData":{write:[12,1,1,""]},"mydict.Dict":{update:[0,1,1,""],setdefault:[0,1,1,""],get:[0,1,1,""]},"fe_abq.Result":{setdefault:[12,1,1,""],update:[12,1,1,""],get:[12,1,1,""]},"actors.CoordPlaneActor":{setLineStipple:[31,1,1,""],setColor:[31,1,1,""],setLineWidth:[31,1,1,""],drawGL:[31,1,1,""]},"widgets.InputButton":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""],doFunc:[29,1,1,""]},"actors.RotatedActor":{setLineStipple:[31,1,1,""],setColor:[31,1,1,""],setLineWidth:[31,1,1,""]},"widgets.InputColor":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},"widgets.FileSelection":{getFilename:[29,1,1,""]},utils:{setSaneLocale:[53,2,1,""],is_pyFormex:[53,2,1,""],fileTypeFromExt:[53,2,1,""],matchAny:[53,2,1,""],matchAll:[53,2,1,""],killProcesses:[53,2,1,""],mtime:[53,2,1,""],splitEndDigits:[53,2,1,""],pyformexFiles:[53,2,1,""],tildeExpand:[53,2,1,""],prefixDict:[53,2,1,""],changeExt:[53,2,1,""],matchCount:[53,2,1,""],findIcon:[53,2,1,""],all_image_extensions:[53,2,1,""],fileType:[53,2,1,""],hasExternal:[53,2,1,""],selectDict:[53,2,1,""],subDict:[53,2,1,""],fileDescription:[53,2,1,""],NameSequence:[53,3,1,""],checkVersion:[53,2,1,""],splitStartDigits:[53,2,1,""],matchMany:[53,2,1,""],checkExternal:[53,2,1,""],projectName:[53,2,1,""],refreshDict:[53,2,1,""],prefixFiles:[53,2,1,""],strNorm:[53,2,1,""],hasModule:[53,2,1,""],runCommand:[53,2,1,""],stuur:[53,2,1,""],userName:[53,2,1,""],matchNone:[53,2,1,""],spawn:[53,2,1,""],interrogate:[53,2,1,""],timeEval:[53,2,1,""],countLines:[53,2,1,""],listTree:[53,2,1,""],removeTree:[53,2,1,""],checkModule:[53,2,1,""],removeDict:[53,2,1,""]},"colorscale.ColorLegend":{color:[7,1,1,""],overflow:[7,1,1,""]},"widgets.InputPoint":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},"mydict.CDict":{update:[0,1,1,""],setdefault:[0,1,1,""],get:[0,1,1,""]},"decors.Grid":{pickGL:[1,1,1,""],setLineStipple:[1,1,1,""],setColor:[1,1,1,""],setLineWidth:[1,1,1,""]},"lima.Lima":{status:[60,1,1,""],addRule:[60,1,1,""],translate:[60,1,1,""]},"fe.Model":{spherical:[50,1,1,""],projectOnCylinder:[50,1,1,""],mplex:[50,1,1,""],bump:[50,1,1,""],projectOnPlane:[50,1,1,""],replace:[50,1,1,""],bump1:[50,1,1,""],nnodes:[50,1,1,""],bump2:[50,1,1,""],transformCS:[50,1,1,""],swapAxes:[50,1,1,""],superSpherical:[50,1,1,""],egg:[50,1,1,""],scale:[50,1,1,""],mapd:[50,1,1,""],projectOnSphere:[50,1,1,""],rot:[50,1,1,""],affine:[50,1,1,""],write:[50,1,1,""],nelems:[50,1,1,""],elemNrs:[50,1,1,""],translate:[50,1,1,""],shear:[50,1,1,""],map:[50,1,1,""],centered:[50,1,1,""],reflect:[50,1,1,""],splitElems:[50,1,1,""],cylindrical:[50,1,1,""],renumber:[50,1,1,""],copy:[50,1,1,""],rollAxes:[50,1,1,""],rotate:[50,1,1,""],trl:[50,1,1,""],flare:[50,1,1,""],hyperCylindrical:[50,1,1,""],resized:[50,1,1,""],isopar:[50,1,1,""],addNoise:[50,1,1,""],toSpherical:[50,1,1,""],map1:[50,1,1,""],toCylindrical:[50,1,1,""],ngroups:[50,1,1,""],position:[50,1,1,""],getElems:[50,1,1,""],align:[50,1,1,""]},"widgets.InputString":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""],show:[29,1,1,""]},"camera.ViewAngles":{get:[24,1,1,""]},imagearray:{rgb2qimage:[36,2,1,""],gray2qimage:[36,2,1,""]},"viewport.NewMultiCanvas":{setStretch:[68,1,1,""],changeLayout:[68,1,1,""],link:[68,1,1,""],createView:[68,1,1,""],removeView:[68,1,1,""],setCurrent:[68,1,1,""],addView:[68,1,1,""]},"widgets.InputFont":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},"decors.Mark":{pickGL:[1,1,1,""],setLineStipple:[1,1,1,""],setColor:[1,1,1,""],setLineWidth:[1,1,1,""]},"decors.ColorLegend":{pickGL:[1,1,1,""],setLineStipple:[1,1,1,""],setColor:[1,1,1,""],setLineWidth:[1,1,1,""]},"curve.CardinalSpline":{spherical:[79,1,1,""],toFormex:[79,1,1,""],resized:[79,1,1,""],swapAxes:[79,1,1,""],toSpherical:[79,1,1,""],bump:[79,1,1,""],length_intgrnd:[79,1,1,""],setProp:[79,1,1,""],approx:[79,1,1,""],replace:[79,1,1,""],sub_directions_2:[79,1,1,""],bump2:[79,1,1,""],transformCS:[79,1,1,""],pointsOn:[79,1,1,""],endPoints:[79,1,1,""],superSpherical:[79,1,1,""],directionsAt:[79,1,1,""],scale:[79,1,1,""],sub_points:[79,1,1,""],mapd:[79,1,1,""],projectOnSphere:[79,1,1,""],rot:[79,1,1,""],affine:[79,1,1,""],sub_curvature:[79,1,1,""],subPoints:[79,1,1,""],write:[79,1,1,""],projectOnPlane:[79,1,1,""],toMesh:[79,1,1,""],translate:[79,1,1,""],hyperCylindrical:[79,1,1,""],shear:[79,1,1,""],map:[79,1,1,""],extend:[79,1,1,""],approx_by_subdivision:[79,1,1,""],centered:[79,1,1,""],sub_points_2:[79,1,1,""],reflect:[79,1,1,""],cylindrical:[79,1,1,""],part:[79,1,1,""],copy:[79,1,1,""],sub_directions:[79,1,1,""],rollAxes:[79,1,1,""],reverse:[79,1,1,""],bump1:[79,1,1,""],rotate:[79,1,1,""],trl:[79,1,1,""],pointsAt:[79,1,1,""],flare:[79,1,1,""],align:[79,1,1,""],projectOnCylinder:[79,1,1,""],isopar:[79,1,1,""],pointsOff:[79,1,1,""],lengths:[79,1,1,""],length:[79,1,1,""],map1:[79,1,1,""],toCylindrical:[79,1,1,""],position:[79,1,1,""],egg:[79,1,1,""],addNoise:[79,1,1,""]},"widgets.InputFloat":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""],show:[29,1,1,""]},"units.UnitsSystem":{Predefined:[55,1,1,""],Get:[55,1,1,""],Read:[55,1,1,""],Add:[55,1,1,""],Engineering:[55,1,1,""],International:[55,1,1,""]},flatkeydb:{unQuote:[22,2,1,""],splitKeyValue:[22,2,1,""],FlatDB:[22,3,1,""],ignore_error:[22,2,1,""],firstWord:[22,2,1,""]},"curve.CardinalSpline2":{spherical:[79,1,1,""],approx:[79,1,1,""],projectOnCylinder:[79,1,1,""],toSpherical:[79,1,1,""],bump:[79,1,1,""],setProp:[79,1,1,""],toFormex:[79,1,1,""],replace:[79,1,1,""],sub_directions_2:[79,1,1,""],bump2:[79,1,1,""],transformCS:[79,1,1,""],swapAxes:[79,1,1,""],endPoints:[79,1,1,""],superSpherical:[79,1,1,""],directionsAt:[79,1,1,""],scale:[79,1,1,""],mapd:[79,1,1,""],projectOnSphere:[79,1,1,""],rot:[79,1,1,""],affine:[79,1,1,""],subPoints:[79,1,1,""],write:[79,1,1,""],projectOnPlane:[79,1,1,""],translate:[79,1,1,""],shear:[79,1,1,""],map:[79,1,1,""],centered:[79,1,1,""],sub_points_2:[79,1,1,""],reflect:[79,1,1,""],cylindrical:[79,1,1,""],copy:[79,1,1,""],sub_directions:[79,1,1,""],rollAxes:[79,1,1,""],bump1:[79,1,1,""],rotate:[79,1,1,""],trl:[79,1,1,""],pointsAt:[79,1,1,""],flare:[79,1,1,""],hyperCylindrical:[79,1,1,""],resized:[79,1,1,""],isopar:[79,1,1,""],addNoise:[79,1,1,""],length:[79,1,1,""],map1:[79,1,1,""],toCylindrical:[79,1,1,""],position:[79,1,1,""],egg:[79,1,1,""],align:[79,1,1,""]},actors:{CubeActor:[31,3,1,""],TranslatedActor:[31,3,1,""],PlaneActor:[31,3,1,""],GeomActor:[31,3,1,""],Actor:[31,3,1,""],BboxActor:[31,3,1,""],GridActor:[31,3,1,""],CoordPlaneActor:[31,3,1,""],RotatedActor:[31,3,1,""],AxesActor:[31,3,1,""],SphereActor:[31,3,1,""]},"timer.Timer":{read:[76,1,1,""],reset:[76,1,1,""],seconds:[76,1,1,""]},"actors.CubeActor":{setLineStipple:[31,1,1,""],setColor:[31,1,1,""],setLineWidth:[31,1,1,""],drawGL:[31,1,1,""]},units:{UnitsSystem:[55,3,1,""],convertUnits:[55,2,1,""]},"widgets.InputText":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""],show:[29,1,1,""]},fe_abq:{fmtOptions:[12,2,1,""],nsetName:[12,2,1,""],fmtPart:[12,2,1,""],fmtGeneralBeamSection:[12,2,1,""],fmtMaterial:[12,2,1,""],exportMesh:[12,2,1,""],AbqData:[12,3,1,""],Output:[12,3,1,""],fmtAnalyticalSurface:[12,2,1,""],writeElems:[12,2,1,""],fmtEquation:[12,2,1,""],writeDsloads:[12,2,1,""],fmtOrientation:[12,2,1,""],fmtGeneralContact:[12,2,1,""],fmtCmd:[12,2,1,""],fmtSurfaceInteraction:[12,2,1,""],fmtFrameSection:[12,2,1,""],writeElemOutput:[12,2,1,""],fmtConstraint:[12,2,1,""],fmtBeamSection:[12,2,1,""],fmtContactPair:[12,2,1,""],fmtConnectorSection:[12,2,1,""],Interaction:[12,3,1,""],fmtData:[12,2,1,""],writeElemResult:[12,2,1,""],writeNodeResult:[12,2,1,""],fmtInitialConditions:[12,2,1,""],writeCloads:[12,2,1,""],writeSection:[12,2,1,""],writeNodeOutput:[12,2,1,""],fmtData1D:[12,2,1,""],abqInputNames:[12,2,1,""],fmtConnectorBehavior:[12,2,1,""],writeDloads:[12,2,1,""],writeNodes:[12,2,1,""],fmtTransform:[12,2,1,""],Result:[12,3,1,""],writeFileOutput:[12,2,1,""],fmtSurface:[12,2,1,""],esetName:[12,2,1,""],writeSet:[12,2,1,""],writeDisplacements:[12,2,1,""],fmtHeading:[12,2,1,""]},config:{formatDict:[23,2,1,""],Config:[23,3,1,""]},"widgets.ButtonBox":{setValue:[29,1,1,""],name:[29,1,1,""],text:[29,1,1,""],setText:[29,1,1,""],value:[29,1,1,""],setIcon:[29,1,1,""]},"curve.Curve":{spherical:[79,1,1,""],toFormex:[79,1,1,""],resized:[79,1,1,""],toSpherical:[79,1,1,""],projectOnPlane:[79,1,1,""],setProp:[79,1,1,""],approx:[79,1,1,""],replace:[79,1,1,""],sub_directions_2:[79,1,1,""],bump2:[79,1,1,""],transformCS:[79,1,1,""],swapAxes:[79,1,1,""],endPoints:[79,1,1,""],superSpherical:[79,1,1,""],directionsAt:[79,1,1,""],scale:[79,1,1,""],mapd:[79,1,1,""],projectOnSphere:[79,1,1,""],rot:[79,1,1,""],affine:[79,1,1,""],subPoints:[79,1,1,""],write:[79,1,1,""],bump:[79,1,1,""],translate:[79,1,1,""],shear:[79,1,1,""],map:[79,1,1,""],sub_points:[79,1,1,""],centered:[79,1,1,""],sub_points_2:[79,1,1,""],reflect:[79,1,1,""],cylindrical:[79,1,1,""],copy:[79,1,1,""],sub_directions:[79,1,1,""],rollAxes:[79,1,1,""],bump1:[79,1,1,""],rotate:[79,1,1,""],trl:[79,1,1,""],pointsAt:[79,1,1,""],flare:[79,1,1,""],align:[79,1,1,""],projectOnCylinder:[79,1,1,""],isopar:[79,1,1,""],addNoise:[79,1,1,""],length:[79,1,1,""],map1:[79,1,1,""],toCylindrical:[79,1,1,""],position:[79,1,1,""],egg:[79,1,1,""],hyperCylindrical:[79,1,1,""]},draw:{focus:[32,2,1,""],sleep:[32,2,1,""],linewidth:[32,2,1,""],pause:[32,2,1,""],layout:[32,2,1,""],drawText:[32,2,1,""],highlightActors:[32,2,1,""],updateGUI:[32,2,1,""],fforward:[32,2,1,""],highlightPartitions:[32,2,1,""],transparent:[32,2,1,""],askItems:[32,2,1,""],set_material_value:[32,2,1,""],printMessage:[32,2,1,""],drawLinesInter:[32,2,1,""],showMessage:[32,2,1,""],view:[32,2,1,""],flatten:[32,2,1,""],showLineDrawing:[32,2,1,""],askDirname:[32,2,1,""],clear_canvas:[32,2,1,""],pointsize:[32,2,1,""],flyAlong:[32,2,1,""],removeViewport:[32,2,1,""],lights:[32,2,1,""],canvasSize:[32,2,1,""],colormap:[32,2,1,""],error:[32,2,1,""],askNewFilename:[32,2,1,""],drawMarks:[32,2,1,""],step:[32,2,1,""],ask:[32,2,1,""],wait:[32,2,1,""],showInfo:[32,2,1,""],ack:[32,2,1,""],dialogAccepted:[32,2,1,""],pick:[32,2,1,""],drawActor:[32,2,1,""],showDescription:[32,2,1,""],annotate:[32,2,1,""],message:[32,2,1,""],undraw:[32,2,1,""],highlightEdges:[32,2,1,""],linkViewport:[32,2,1,""],zoomAll:[32,2,1,""],bgcolor:[32,2,1,""],askFilename:[32,2,1,""],drawViewportAxes3D:[32,2,1,""],currentDialog:[32,2,1,""],closeGui:[32,2,1,""],showFile:[32,2,1,""],linestipple:[32,2,1,""],checkWorkdir:[32,2,1,""],drawVertexNumbers:[32,2,1,""],removeHighlights:[32,2,1,""],setView:[32,2,1,""],fgcolor:[32,2,1,""],viewport:[32,2,1,""],highlightPoints:[32,2,1,""],zoomBbox:[32,2,1,""],zoomRectangle:[32,2,1,""],decorate:[32,2,1,""],askDrawOptions:[32,2,1,""],setTriade:[32,2,1,""],highlight:[32,2,1,""],drawable:[32,2,1,""],drawBbox:[32,2,1,""],drawText3D:[32,2,1,""],drawAxes:[32,2,1,""],setDrawOptions:[32,2,1,""],addViewport:[32,2,1,""],warning:[32,2,1,""],createView:[32,2,1,""],dialogRejected:[32,2,1,""],dialogTimedOut:[32,2,1,""],drawFreeEdges:[32,2,1,""],closeDialog:[32,2,1,""],delay:[32,2,1,""],drawPropNumbers:[32,2,1,""],wakeup:[32,2,1,""],draw:[32,2,1,""],highlightElements:[32,2,1,""],drawVectors:[32,2,1,""],drawImage:[32,2,1,""],clear:[32,2,1,""],showText:[32,2,1,""],drawNumbers:[32,2,1,""],set_edit_mode:[32,2,1,""]},elements:{elementTypes:[38,2,1,""],printElementTypes:[38,2,1,""],elementType:[38,2,1,""],Element:[38,3,1,""]},arraytools:{sortByColumns:[9,2,1,""],anyVector:[9,2,1,""],orthog:[9,2,1,""],projection:[9,2,1,""],arcsind:[9,2,1,""],niceNumber:[9,2,1,""],uniqueOrdered:[9,2,1,""],isClose:[9,2,1,""],vectorPairNormals:[9,2,1,""],vectorPairArea:[9,2,1,""],readArray:[9,2,1,""],checkArray:[9,2,1,""],arccosd:[9,2,1,""],normalize:[9,2,1,""],rotmat:[9,2,1,""],cosd:[9,2,1,""],rotationAnglesFromMatrix:[9,2,1,""],cubicEquation:[9,2,1,""],growAxis:[9,2,1,""],niceLogSize:[9,2,1,""],solveMany:[9,2,1,""],stack:[9,2,1,""],movingAverage:[9,2,1,""],matchIndex:[9,2,1,""],vectorRotation:[9,2,1,""],length:[9,2,1,""],argNearestValue:[9,2,1,""],addAxis:[9,2,1,""],norm:[9,2,1,""],inverseIndex:[9,2,1,""],movingView:[9,2,1,""],reorderAxis:[9,2,1,""],randomNoise:[9,2,1,""],unitVector:[9,2,1,""],writeArray:[9,2,1,""],checkUniqueNumbers:[9,2,1,""],reverseAxis:[9,2,1,""],checkArray1D:[9,2,1,""],vectorPairAreaNormals:[9,2,1,""],arctand:[9,2,1,""],sind:[9,2,1,""],rotationMatrix:[9,2,1,""],vectorPairAngle:[9,2,1,""],checkArrayDim:[9,2,1,""],vectorNormalize:[9,2,1,""],dotpr:[9,2,1,""],rotMatrix:[9,2,1,""],trfMatrix:[9,2,1,""],arctand2:[9,2,1,""],histogram2:[9,2,1,""],tand:[9,2,1,""],renumberIndex:[9,2,1,""],vectorTripleProduct:[9,2,1,""],vectorPairCosAngle:[9,2,1,""],inverseUniqueIndex:[9,2,1,""],groupArgmin:[9,2,1,""],nearestValue:[9,2,1,""],vectorLength:[9,2,1,""],horner:[9,2,1,""],uniqueRows:[9,2,1,""],inside:[9,2,1,""]},"viewport.MultiCanvas":{showWidget:[68,1,1,""],newView:[68,1,1,""],changeLayout:[68,1,1,""],link:[68,1,1,""],addView:[68,1,1,""],setCurrent:[68,1,1,""]},"widgets.InputWidget":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},"fe_abq.Output":{fmt:[12,1,1,""],setdefault:[12,1,1,""],update:[12,1,1,""],get:[12,1,1,""]},connectivity:{sortAdjacency:[66,2,1,""],connectedLineElems:[66,2,1,""],reduceAdjacency:[66,2,1,""],Connectivity:[66,3,1,""],adjacencyArrays:[66,2,1,""],findConnectedLineElems:[66,2,1,""]},mesh:{mergeNodes:[26,2,1,""],Mesh:[26,3,1,""],mergeMeshes:[26,2,1,""]},"odict.KeyedList":{sort:[47,1,1,""],keys:[47,1,1,""],items:[47,1,1,""],pos:[47,1,1,""],update:[47,1,1,""],values:[47,1,1,""]},fe:{Model:[50,3,1,""],mergedModel:[50,2,1,""]},"widgets.InputInfo":{text:[29,1,1,""],setValue:[29,1,1,""],name:[29,1,1,""],value:[29,1,1,""]},"menu.MenuBar":{index:[70,1,1,""],nextitem:[70,1,1,""],item:[70,1,1,""],actionList:[70,1,1,""],create_insert_action:[70,1,1,""],insert_menu:[70,1,1,""],insert_action:[70,1,1,""],removeItem:[70,1,1,""],action:[70,1,1,""],insert_sep:[70,1,1,""],insertItems:[70,1,1,""]},geomtools:{triangleInCircle:[40,2,1,""],distancesPFS:[40,2,1,""],perpendicularVector:[40,2,1,""],intersectionTimesPOL:[40,2,1,""],lineIntersection:[40,2,1,""],triangleCircumCircle:[40,2,1,""],rotationAngle:[40,2,1,""],displaceLines:[40,2,1,""],intersectionTimesPOP:[40,2,1,""],vertexDistance:[40,2,1,""],distancesPFL:[40,2,1,""],intersectionTimesLWP:[40,2,1,""],intersectionTimesLWT:[40,2,1,""],faceDistance:[40,2,1,""],polygonNormals:[40,2,1,""],intersectionPointsSWP:[40,2,1,""],insideSimplex:[40,2,1,""],insideTriangle:[40,2,1,""],polygonArea:[40,2,1,""],baryCoords:[40,2,1,""],intersectionLinesPWP:[40,2,1,""],intersectionPointsPOP:[40,2,1,""],projectionVOV:[40,2,1,""],segmentOrientation:[40,2,1,""],areaNormals:[40,2,1,""],pointsAtLines:[40,2,1,""],intersectionPointsSWT:[40,2,1,""],intersectionPointsPWP:[40,2,1,""],intersectionPointsLWT:[40,2,1,""],intersectionPointsPOL:[40,2,1,""],anyPerpendicularVector:[40,2,1,""],intersectionPointsLWL:[40,2,1,""],projectionVOP:[40,2,1,""],intersectionTimesLWL:[40,2,1,""],triangleBoundingCircle:[40,2,1,""],pointsAtSegments:[40,2,1,""],triangleObtuse:[40,2,1,""],edgeDistance:[40,2,1,""],intersectionTimesSWP:[40,2,1,""],intersectionPointsLWP:[40,2,1,""],intersectionTimesSWT:[40,2,1,""]},"menu.ActionList":{add:[70,1,1,""],names:[70,1,1,""]},"decors.Line":{pickGL:[1,1,1,""],setLineStipple:[1,1,1,""],setColor:[1,1,1,""],setLineWidth:[1,1,1,""]},"widgets.InputGroup":{setValue:[29,1,1,""],value:[29,1,1,""]},curve:{CardinalSpline:[79,3,1,""],NaturalSpline:[79,3,1,""],Arc3:[79,3,1,""],Curve:[79,3,1,""],Spiral:[79,3,1,""],CardinalSpline2:[79,3,1,""],Arc:[79,3,1,""],PolyLine:[79,3,1,""],convertFormexToCurve:[79,2,1,""],Line:[79,3,1,""],circle:[79,2,1,""],BezierSpline:[79,3,1,""]},section2d:{PlaneSection:[48,3,1,""],sectionChar:[48,2,1,""],princTensor2D:[48,2,1,""],extendedSectionChar:[48,2,1,""]},"actors.GridActor":{setLineStipple:[31,1,1,""],setColor:[31,1,1,""],setLineWidth:[31,1,1,""],drawGL:[31,1,1,""]}},terms:{rotmatrix:9,orthogon:[79,9,10],windownam:74,currentselect:69,yellow:[18,16],interchang:65,four:[39,24,1,27,10,14,34,35,16],prefix:[53,29,64],esc:[29,68],sleep:32,dirnam:[53,42],upsid:59,pick_actor:68,dialogtimedout:32,shear_modulu:[12,14],forget:[4,16,18],whose:[37,38,39,65,20,26,73,6,9,27,53,14,35,16],accur:17,boundvector:39,nedg:[6,26],readfacesblock:33,swap:[39,73],under:[0,45,4,6,8,10,16,17,19,22,68,13,42,54,56,61,65,34,69,35,64,23],readelefil:33,merchant:[65,8,10,16],digit:[28,38,9],everi:[65,20,66,69,26,6,10,22,34,16],risk:[64,65],downstream:65,"void":[13,65],internet:[42,64],sub_points_2:79,implicit:[74,29],eprop:12,delv:34,poisson_ratio:[12,27,14],affect:[22,19,18,65,82],pickabl:68,month:64,highlightpoint:32,viewabl:24,vast:10,hasextern:53,gl_modelview:24,pshape:39,school:65,dxfexport:2,beam:[12,27,14],plural:[73,16],cmd:[12,53,60],ysymm:27,cut2atplan:73,geuz:[],draw_state_rect:68,vector:[39,40,64,24,73,6,32,29,50,9,31,12,79,68,35,16],red:[82,16],initialis:10,properti:[0,45,6,78,10,12,14,16,19,21,26,31,32,68,18,46,54,56,27,65,66,72,73,79,82],repres:[39,71,40,65,66,1,43,26,72,73,6,7,29,9,10,31,32,36,79,35,16],seper:[10,26],direct:[6,8,9,10,12,14,16,26,29,40,32,68,73,38,39,48,49,50,53,27,61,65,24,69,70,72,35,77,79,82],commerci:[8,65],setcheck:29,consequ:[10,65],second:[1,6,9,12,16,29,32,73,37,39,41,48,50,53,55,27,61,63,69,72,35,74,76,23,82],street:27,aggreg:65,perfect:[6,46],"4x4":9,sectiontyp:[12,27,14],even:[4,6,8,10,16,17,26,27,22,34,42,46,49,54,79,65,66,69,74,64,23,82],employ:[13,65],nel:40,oldest:16,hide:29,drawax:32,neg:[38,66,46,26,18,6,29,9,40,79,73],altitud:6,litig:65,children:[6,26],facedist:40,excludefil:53,read_stl:6,compos:[8,16],asiz:39,icosa:38,resfil:57,the_extern:53,"new":[2,6,8,9,10,11,12,14,16,17,19,20,26,29,31,32,33,68,18,37,38,39,40,42,47,49,50,53,54,27,58,60,61,22,63,64,65,66,24,69,70,73,74,77,23,82],net:64,neu:6,ever:[6,8,65,16],topolog:10,cascadingdict:27,told:8,"_default_":[12,0,19,23,14],widget:[19,68,69,81,29,78,64,32,56,34,82],gimbal:24,abov:[39,65,21,42,27,6,9,64,54,34,73,16],displai:[19,65,68,1,70,49,18,53,29,69,31,32,82,80,34,15,16],jobnam:12,never:[29,69,34,27,14],here:[66,69,42,27,49,8,75,31,82,34,73,16],insert_menu:70,met:65,lgpl:65,abqdata:12,path:[4,6,8,20,26,29,48,34,37,38,39,77,42,32,49,53,54,56,27,66,69,64],mergedmodel:50,interpret:[23,39,65,21,42,4,44,49,27,79,68,73,16],forum:[],taper:73,announc:16,removelistitem:14,exludedir:53,precis:[61,65,66,39,6,64,56,35,16],datetim:76,addact:70,permit:[73,65],dra:32,studi:[69,10,16],iyz:62,portabl:[4,64],inputinfo:29,qintvalid:29,curvetonurb:35,addelem:33,filterfil:37,setplaintext:29,nframe:25,substr:[53,22],unix:[6,53,55],sett:73,undetemin:47,succesfulli:8,eload:14,uniq:9,circumvent:65,unit:[38,39,40,55,31,64,44,72,27,6,29,9,10,77,32,79,56,73,16],highli:[69,68,64],plot:[1,52],describ:[23,56,21,24,69,66,26,27,82,8,10,64,11,48,54,79,33,73,16],would:[61,65,1,42,26,27,74,29,8,69,34,73,16],occaion:54,torsional_const:[12,14],smoothwir:[31,16],choosen:[6,27],key3:22,movie1:42,call:[0,2,4,6,9,11,12,16,19,24,26,29,31,32,22,80,73,39,46,53,27,61,65,66,68,69,70,35,74,64,23,82],asset:65,recommend:[42,6,64,11,54,35,16],preview:68,type:[0,2,4,6,8,9,10,11,12,14,16,23,19,20,21,26,29,32,33,68,18,37,38,39,41,42,43,44,46,47,53,55,56,27,61,22,65,66,35,69,73,64,79,82],tell:[65,42,51,16,82],uniqid:9,successful:20,grabbin:74,relat:[39,14,26,27,6,82,62,24],notic:[65,69,26,82,6,9,10,27,16],renumberindex:9,warn:[6,78,10,16,20,26,29,32,34,18,42,44,81,51,54,58,67,35,70,73,74,64,79],lower_onli:66,exce:[6,54],beziersplin:[79,35],mather:[6,26],loss:65,cumul:52,zoomarea:24,matchal:53,unpack:[64,65],must:[39,65,20,16,29,10,22,73,36],shoot:53,anyperpendicularvector:40,word:[23,66,51,55,22,73,10],restor:[20,66,21,77,32,16],setup:[12,24,64],work:[0,35,45,4,6,8,9,11,16,32,62,68,18,39,41,42,46,44,54,55,27,58,65,66,34,69,73,75,64,23],yield_stress:12,exportmesh:12,norm:[6,9],growcollect:45,frequenc:12,root:[74,6,29,9,64],could:[65,66,24,69,42,26,27,6,29,8,64,53,23,34,73,16],distanceofpoint:6,overrid:[61,19,20,26,32,29,12,79,22,34,27,16],findconnectedlineelem:66,webcolor:28,partitionbyconnect:[6,68,46],"_set_coord":61,give:[6,8,9,10,11,16,26,29,32,34,73,39,42,46,81,49,50,54,27,65,66,69,35,28,64,79],household:65,bgmode:19,indic:[17,39,65,66,41,69,26,46,6,29,9,40,32,68,82,16],findlistitem:14,getcel:[6,26],liter:[0,1,2,3,4,5,6,7,9,12,13,14,15,28,19,20,22,23,24,25,26,29,30,31,32,33,68,35,36,37,38,39,40,41,43,44,45,46,47,48,50,52,53,55,57,59,60,61,62,63,66,18,70,71,72,73,74,76,77,79,80],want:[0,2,8,9,10,11,16,23,19,21,29,32,22,68,73,39,42,46,49,53,54,55,56,27,65,34,64,79,82],umin:35,atlength:79,attract:8,shortestedg:6,is_pyformex:[37,53],emit_cancel:68,end:[0,1,2,6,10,16,19,26,29,40,32,33,68,73,37,39,31,48,53,54,23,22,65,34,72,35,74,77,79],findicon:53,backfac:19,quot:[37,22,16],cylind:[72,39,8,10,16],rosett:[10,73],programwil:6,classifi:[37,69],how:[6,8,9,11,16,21,25,29,32,34,73,39,42,44,81,51,54,27,58,65,66,67,69,75,64,78,82],ent:55,env:16,answer:[42,4,53,8,75,64,32,54],disappoint:8,rt345e6:44,config:[6,49,32,23,56,34],updat:[37,17,0,65,19,53,20,80,4,71,47,32,29,64,77,12,54,23,14,68],setlen:24,recogn:[37,65,69,2,71,32,12,27,16],lai:[29,68,82],setrowstretch:42,mess:27,endrec:22,after:[4,6,9,10,16,19,24,26,29,32,68,73,39,64,49,53,54,23,65,66,34,71,35,74,77,79],befor:[8,9,16,23,19,29,32,22,68,24,37,39,42,49,53,54,27,61,63,34,69,70,71,73,74,64,79,82],mesh:[6,8,78,10,12,16,26,31,32,33,68,73,39,42,46,50,56,57,61,66,67,79],setbbox:[68,19],trianglecircumcircl:40,lat:[39,24],irrelev:16,mesa:[42,64],law:65,parallel:[39,72,24,9,48,73,16],demonstr:54,reshap:[39,9],attempt:[38,20,65,26,6,9,64],finish_select:68,third:[39,65,24,73,32,8,9,27,12,35,16],interpol:[39,35,73],dispai:82,nurbssurfac:35,minim:[53,39,31],revolv:[6,26,46],think:[54,64,8,27,16],setslcolor:[68,19],receiv:[65,29,10,53,27,16],maintain:[8,75,65],green:[54,27,16],incorpor:65,enter:[65,69,29,4,49,32,54,68,27,16],exclus:[12,65],drawableobject:18,first:[0,1,2,6,7,8,9,10,12,14,16,23,20,22,26,29,40,32,33,34,73,36,37,38,39,42,46,47,48,49,50,53,54,55,27,61,62,63,65,66,69,70,72,35,74,64,79,82],order:[0,6,8,9,10,12,14,16,26,62,18,37,38,39,41,42,47,50,65,66,35,69,70,73,64,79],wind:10,length_cost:6,oper:[4,6,8,9,10,16,19,21,24,26,29,40,48,62,68,18,39,31,41,46,32,49,53,54,56,60,61,65,66,34,69,70,73,64,82],write_smesh:6,deform:[6,10],atribut:62,my_imag:16,over:[1,4,6,9,10,12,16,23,20,26,40,32,22,68,24,39,50,27,35,69,73,77,79,82],govern:65,becaus:[0,6,8,9,11,12,16,20,21,26,32,73,39,42,49,53,54,56,27,59,65,66,69,71,64,23,82],jpeg:42,surrend:65,removeloc:23,privileg:64,linewidth:[19,1,31,32,68,15,16],vari:10,gl2p:[42,64,16],downgrad:54,lindenmay:[60,56,77],rfunc:79,img:54,readsmesh:[],fix:[38,39,70,64,65,24,1,42,26,46,6,29,71,11,32,54,82,73],cantl:73,lineintersect:40,cla:[39,19,4,18,6,26,73],nset1:27,inadvert:16,better:[17,60,69,6,8,10,64,32,55,34,16],read_gt:6,renumb:[6,50,9,26,66],fade:42,persist:[20,21,69,4,54,58],comprehens:[29,42,64,11],nelem:[62,21,66,26,6,50,9,31,32,33,73,16],easier:[69,8,10,32,73,16],shear:[39,26,6,50,10,61,79,73],descend:23,them:[6,8,12,16,20,21,26,29,40,32,73,39,42,48,49,54,56,27,65,67,69,81,64,82],startat:[6,46],thei:[2,4,6,8,10,11,16,23,19,21,26,29,32,62,68,73,37,39,41,42,49,50,53,56,27,22,65,24,69,72,35,64,79,82],removehighlight:[32,68,19],proce:[32,54,27,64],promin:65,safe:[74,22,16],disturb:1,rectangl:[19,1,72,6,32,68],scene:[19,69,24,78,31,32,82,56,68,15],"break":64,band:42,interrupt:32,distancefromlin:[61,39,73],addsect:[23,14],setvalu:29,choic:[69,4,26,6,29,64,32,54],"40e":12,dload:[12,27,14],"0x32d52a8":[],getedg:[6,26],isoparametr:[39,56,43],getvalu:29,gluttext:[1,56,71],borderedgenr:6,timeout:[32,80,29,34],each:[1,2,4,6,7,8,9,10,12,16,23,18,19,21,24,26,29,40,32,33,34,73,37,38,39,64,44,45,46,47,50,53,55,27,22,65,66,68,69,70,72,35,74,77,79,82],debug:[49,42,23],went:54,oblig:65,side:[39,66,26,72,6,10,31,79,73],mean:[0,1,6,8,10,16,17,20,25,26,29,32,73,38,39,49,56,27,65,66,64,79,82],prohibit:65,ncoord:[6,39,35,16,26],isclosedmanifold:6,mpattern:16,hasannot:18,list:[1,2,4,6,8,9,12,13,14,15,16,28,18,19,26,27,29,31,32,33,68,73,36,37,38,39,40,41,42,43,44,45,46,47,49,50,53,54,55,56,57,60,22,63,65,66,34,69,70,71,35,74,64,79],exactli:[47,26,6,10,79,18,16],overflow:7,fmtcmd:12,nodri:[49,68],misori:6,logo:[54,42,64],avgdirect:79,extract:[37,2,73,16,66],incomprehens:8,printbbox:18,depress:[80,69,34,82],max_edg:6,newmulticanva:68,network:[54,65],sector:[73,72],goe:[54,79,8,82],newli:[68,19],colored_ax:31,predefin:[66,14,26,72,24,6,9,32,55,56,35,16],content:[20,65,69,2,72,53,32,23,82,16],laid:[68,82],sane:53,adapt:[35,65,10],reader:[56,44],got:[54,69],matchin:12,size:[1,6,9,10,15,16,19,26,29,31,32,68,73,39,42,46,50,54,61,69,71,72,24,74,77,79],impract:27,multilin:[37,1,2,71,29,23,22,16],"0x000000":42,linear:[25,26,46,6,53,9,12,79,73],navig:56,written:[4,6,75,12,16,20,21,26,32,22,34,73,37,42,81,50,51,53,54,23,58,61,33,65,67,79],keyw:37,endzerocurv:79,situat:[6,68,26,27],infin:35,free:[65,42,26,13,6,8,10,64,54,27,16],standard:[39,42,53,65,2,6,32,49,11,12,55,27],point2str:73,small:[39,1,2,6,69,12,73,16],"__setitem__":22,customarili:65,bmrk:33,freq:12,postabq:30,praet:64,cut3atplan:73,angl:[39,40,19,31,24,69,26,72,46,6,48,9,10,77,32,79,82,68,73,16],autorecognit:29,colstretch:68,unitvector:9,filter:[37,27,70,4,26,6,32,68,18],fuse:[6,79,39,26,73],iso:[32,54,10,16],unabl:4,firstprop:[6,46],fmt:[74,12,39,68],rang:[59,38,53,66,23,82,26,28,73,6,7,29,50,9,32,79,56,34,35,16],"__add__":73,render:[19,64,65,68,25,71,82,74,49,8,78,51,31,32,56,34,24,16],grade:[68,19],independ:[7,33,27,65,54],wast:27,thereof:[66,18,8,32,79,73],necess:12,restrict:[65,16],glutfont:71,unlik:[53,68,29,19,69],alreadi:[37,17,19,65,68,69,2,14,6,32,29,8,9,64,12,54,79,22,34,27,16],mesher:[33,64],messag:[49,69,42,4,13,6,29,32,54,56,18,16],sensibl:[39,79,35,82],topmenubar:70,thick:[39,27,16],agre:[4,65],primari:[69,22],translatem:73,maxprop:[6,26,73],cartesian:[39,35,16,24],acquaint:[69,10],rasterpo:71,exp2:7,top:[37,19,68,69,42,72,82,53,32,54,23,34,24,16],sometim:[64,49,10,16],downsid:26,checkuniquenumb:9,nplane:6,toi:42,master:12,too:[65,1,42,82,8,32,34,69,16],tol:[61,26,6,50,79,73],autorun:[42,34],dwell:65,john:[27,16],isometr:24,colspan:68,circl:[39,72,79,73,40],namespac:56,fmtmateri:[12,14],tool:[63,65,45,49,8,69,10,64,56,73,16],colornam:28,travers:[6,26],somewhat:[42,64],termin:[39,65,69,2,49,32,54,16],set_material_valu:32,facetarea:6,updateperspectivebutton:80,past:32,norst2html:49,target:[38,66,26,6,53,9,32,54],keyword:[37,39,19,21,69,26,6,32,29,12,16],provid:[0,1,2,4,6,7,8,9,10,11,12,16,23,20,24,26,29,31,32,33,68,35,39,42,49,50,53,54,55,56,27,61,65,34,69,70,71,18,64,79,82],eleg:[32,8],tree:[53,64,11],zero:[39,24,46,26,72,73,6,29,50,9,48,61,79,33,35,16],project:[39,78,19,64,20,21,42,24,29,8,9,10,40,53,54,56,68,73,16,58],matter:6,inputwidget:29,minut:16,beginn:[82,16],provis:[74,65],getprop:[6,27,14,26,73],rad:[9,40],fashion:[29,65,16],edgecosangl:6,close:[1,2,6,9,16,26,29,40,48,22,34,73,39,32,50,54,61,33,65,69,35,79],modern:[69,16],mind:8,mine:23,spent:53,pointsiz:[32,19],val3:22,manner:65,increment:[6,14,74,53,77,12],val2:22,infring:65,seen:[10,16,24],seem:1,incompat:[53,64,65,21],withoutprop:[6,26],minu:[39,73],strength:[8,16],readibl:27,irrevoc:65,u_2:27,latter:[39,63,61,24,6,29,9,10,64,11,82,73,16],largestbyconnect:[6,46],u_1:27,thorough:8,point1:73,buton:80,fname:[6,53],mouse_rectangle_zoom:68,thoma:64,maxc:66,blue:[54,16],stimulu:8,though:[0,6,7,8,10,16,19,21,22,68,24,42,53,55,27,65,66,34,69,73,23,82],usernam:53,glob:[53,60,4,77],getelemedg:[6,26],intersectionpointslwl:40,mergemesh:26,mpeg:42,portion:65,breakpoint:4,read_off:6,pyformexrc:[53,69,42,34],typ:32,glow:42,tradit:[8,16],simplic:[73,16],excus:65,image_formats_qtr:29,doc:[38,16],professor:8,writedisplac:12,metal:10,doe:[0,2,4,6,8,9,12,34,16,17,19,24,26,29,32,22,68,18,38,39,48,53,55,27,23,65,66,80,73,74,64,79],bash_profil:54,getlowerent:[6,38,26],declar:[61,42,16],diag:72,unchang:[60,39,19,18,44,24,4,26,6,29,55,9,73,32,61,79,82,68,35,16],resetwarn:70,sum:[39,64,46,6,9,40,12,73],dot:[1,74,53,9,32,68,82,16],treati:65,has_kei:[0,63],hex20:38,opposit:[1,69,24,72,82],"__str__":[61,73],random:[37,39,66,26,6,9],wait_select:68,radiu:[39,72,14,10,12,79],syntax:[39,66,69,2,0,78,23,34,73,16],arctand:9,admesh:[6,64],radio:29,quad:[32,1,73,72,46],timeoutfunc:29,undul:10,stlread:6,transformc:[39,26,6,50,61,79,73],involv:[12,39,8,27],absolut:[38,39,64,65,69,26,6,9,77],nonmanifoldedg:6,layout:[21,42,24,29,32,82,68,27,16],acquir:[42,65],abq:12,menu:[37,17,56,65,20,69,42,49,8,78,70,81,54,33,34,82,16],explain:[37,64,65,67,69,14,49,8,78,10,58,82,34,27,16,51],configur:[37,19,69,42,4,78,64,53,54,23,56,34,82,16],schemat:16,theme:69,busi:[54,65],setsanelocal:53,rich:32,folder:[54,16],setcamera:[68,19],axiom:60,setvalid:29,predecessor:65,plate:27,nonetyp:42,fprint:[39,21],insidetriangl:40,likewis:[65,16],stop:[6,0,74,32,12,79,16],compli:65,color1:[68,19],writecload:12,color2:[32,68,19],matchcentroid:[6,26],mention:[54,34],popitem:0,report:[66,26,46,6,54,14,34,24],reconstruct:9,incept:11,symmetr:24,bar:[37,1,70,73,10,69,16],surfacetyp:6,testcamera:49,trisurfac:[39,46,26,6,27,32,56,73],"public":[17,65,8,10,64,56,13,16],reload:[37,24],bad:53,intersectionpointslwp:40,removeview:68,essenc:8,stuur:53,histogram2:9,insert_act:70,simultanu:69,fair:65,ndegre:79,light:[19,80,31,32,68,16],datatyp:[39,35],drawactor:32,result:[1,4,6,7,9,10,12,14,16,23,21,26,27,29,31,32,62,68,73,37,39,40,41,42,47,48,53,54,57,61,65,66,24,69,70,72,35,28,77,79,82],initialcondit:12,respons:[39,65,53,29,75,64,32,35],nwire:10,pyopengl:64,usefulli:11,listdupl:66,best:[17,65,1,24,8,64,82,69],subject:[69,13,65],awar:[38,69,34,16,54],said:66,tensor:[6,48,39,62,35],hopefulli:16,databas:[56,66,14,10,12,22,27,16],convex:6,checkarrai:9,copyleft:[8,65],figur:[69,8,10,16],invalu:21,simplest:16,flip:59,drawn:[38,21,1,18,31,32,82,15,16],awai:[42,24,65,82],unique_indic:9,microlevel:53,attribut:[0,1,6,7,10,12,14,15,16,19,20,24,26,29,62,80,18,38,39,42,56,27,61,33,65,66,68,73,23,82],autofix:[6,26],accord:[37,69,19,65,20,41,45,26,14,6,50,9,10,82,68,73],triplet:82,read_stla:6,hexstentextsurf:12,edgeconnect:[6,26],playfil:4,sophist:29,extens:[37,6,39,65,21,42,46,74,53,8,78,11,12,79,56,34,16],drawdot:1,extent:65,toler:[39,26,6,79,73,16],nset:[12,27],zsymm:27,breakpt:4,setstretch:68,protect:65,setmateri:[68,19],irregular:6,howev:[6,10,14,16,17,19,20,26,29,34,73,38,39,42,46,49,54,55,27,61,65,66,69,24,64,79,82],against:[6,39,9,26,65],facet:[6,33,40],vbox:29,"0xff3366":42,avgnod:[6,26],interfer:73,colatitud:39,countri:[27,65],login:54,seri:[6,39,69,42,74,79,16],com:[22,64],col:[29,68],con:64,assur:65,bsphere:[61,39,73],character:65,trunk:64,writenoderesult:12,xpattern:39,height:[39,19,42,26,71,6,32,10,12,68,24],permut:[6,66,9,26,73],mesh_ext:[56,46],signatur:20,guid:[17,1,8,78,10,16],assum:[68,39,19,65],summar:10,speak:65,accustom:69,degener:[6,38,26,66],hijk:[39,73],union:[6,41],hyerelast:12,unwant:6,numpi:[39,21,69,42,4,26,6,29,8,9,73,64,36,56,35,16],three:[6,7,8,9,12,16,29,31,32,24,36,39,43,48,56,27,65,35,70,72,73,28,82],been:[6,7,8,9,10,12,16,19,20,21,26,32,68,24,44,53,54,61,65,69,71,73,64],legend:[7,1,82],trigger:[69,70],intersectionpointspwp:40,drawgl:[1,15,31],interest:[65,69,75,10,64,34,16],basic:[39,64,65,69,4,71,6,48,29,8,78,40,11,32,56,35,16],readgeomfil:4,quickli:[60,69,81,8,12,16],tocoord:35,cooordin:39,regul:64,pointson:79,setsectiondb:14,suppress:[29,16],closedialog:32,writeelem:12,textmark:15,anywher:[6,53,22,26,64],updatelightbutton:80,header_data:20,pyformex:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81],child:53,riverbankcomput:64,prj:[37,70],spin:38,emploi:10,subroutin:65,returnnon:0,ident:[39,61,9,79,73,16],ask1:18,parallellepid:9,gnu:[23,17,0,65,69,42,44,8,10,64,54,55,22,34,13,16],newcoord:[6,26],servic:[8,65],tand:[9,10,24],sourceforg:64,aim:[32,65,34,10,77],calcul:[39,71,19,50,24,26,27,6,8,9,10,40,12,79,68,73,16],pairwis:40,plot2d:[56,52],publicli:65,moment_inertia_22:[12,14],"0x3f44848":[],zoomrectangl:[32,68,19],succes:[6,57],opt:12,seven:[24,16],pyramid:[72,16],start_draw:68,"0x435fc80":0,length_intgrnd:79,cursorshapehandl:68,toolkit:64,contributor:65,canv:68,player:60,brake:16,midval:7,splitstartdigit:53,tediou:[69,16],sever:[39,64,16,82],dri:[49,68],postmultipl:9,perform:[38,39,31,65,66,1,73,6,53,64,69,77,11,32,82,68,15,16],suggest:[29,69],make:[0,1,6,7,8,12,16,17,19,20,21,24,29,32,22,68,73,37,39,46,53,54,55,27,58,61,65,34,69,70,35,64,23,82],transpar:[32,42,80,27,16],closestcolornam:28,start_select:68,elemload:[27,14],complex:[49,69,26,14,6,29,9,10,23,34,27,16],strip_quot:22,split:[23,39,71,1,82,26,72,46,6,50,53,79,22,73],cosangl:6,complet:[17,53,66,26,27,6,32,49,10,12,56,68,73,16],wheel:[68,16],return_invers:9,updown:25,addannot:[68,19],hand:[62,54,42,64,21],fairli:[37,17,69],rais:[38,0,19,53,20,66,39,70,4,26,6,7,29,9,12,23,22,14,73,44],ownership:54,refin:6,keep_orig:73,aspectratio:6,inputbool:29,zoomin:16,techniqu:32,setmaterialdb:14,redefin:[32,29],kept:[32,68,19,60],bewar:[39,42,4,6,10,55],inputdialog:[32,29],thu:[0,1,4,6,9,11,12,16,21,26,40,32,22,34,73,37,38,39,49,55,27,61,65,66,24,69,70,72,35,74,64,82],fixnorm:6,thr:9,inherit:[39,66,26,6,31,73],edg1:9,qtcanva:[74,68],partitionbyangl:[6,46],contact:[12,10,65],shortli:[69,8],greatest:65,thi:[0,1,2,3,4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,29,31,32,33,34,35,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,53,54,55,56,27,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,77,78,79,80,81,82],programm:[8,34,65,16],everyth:[19,64,67,69,9,77,32,54,68,18,16],operandi:10,ncontrol:79,tha:20,left:[39,70,19,68,1,42,26,27,6,24,29,9,69,32,79,82,34,73,16],"1hr":44,bezier:[79,35],identifi:[38,63,65,21,66,14,6,29,9,12,47,27],setcolor:[1,15,31],just:[6,8,12,14,16,21,26,29,32,22,34,24,38,39,42,44,49,54,27,61,35,69,73,64,23,82],setedgesandfac:6,ordin:[39,73,16],isodata:43,cardinalsplin:79,human:[23,16,21],"__dict__":23,feresult:57,nowadai:[54,8],yet:[66,69,64,23,56,34,27,16],languag:[65,69,42,8,56,34,82,16],previous:[37,34,20,16,21],imagearrai:[56,36],cstretch:[32,68],characterist:[48,27,14],begin_2d_draw:[68,19],easi:[70,42,27,29,8,10,64,54,82,73,16],stent:[8,10],had:[50,65],coplanar:40,fortran:64,spread:[6,35],board:[32,49,69,4,82],modelview:24,wheel_zoom:68,els:[45,4,6,7,9,11,14,16,26,29,40,32,22,68,73,37,39,50,53,55,23,61,66,24,74,79],east:[39,10,16,73],dictionai:4,sanit:19,applic:[39,65,66,26,4,6,27,64,81,56,35,16],assig:27,mayb:[42,46,21],preserv:[41,65,16,66],wai:[0,6,7,8,10,11,16,20,21,26,32,34,73,39,42,54,56,27,65,69,70,35,64,79,82],ceas:65,rng:23,coordinatesystem:[32,39,31],fast:[64,42,20,16],runallfil:37,bbox:[39,19,18,46,26,73,6,9,31,32,61,68,35,16],background:[19,42,53,32,68,16],elabor:[39,69],fmtdata1d:12,apart:[55,56,16],measur:[39,65,46,72,6,76,53],writesurfac:33,specif:[37,38,65,21,14,44,27,29,8,9,64,11,12,54,82,56,73,16],arbitrari:40,contradict:65,manual:[37,17,69,70,75,64,12,56,73,16],zoom:[19,1,24,32,82,68,69],graviti:[39,1,71,6,48,32,62,15],nfacet:33,unstabl:64,drawrect:1,tools_menu:34,indefin:32,unnecessari:[27,65],underli:[64,16],repars:22,www:[65,8,10,64,55,16],right:[39,19,65,68,1,42,27,24,8,9,69,64,32,54,82,62,34,73,16],old:[20,66,42,4,26,6,29,50,9,64,32,54,22,18],deal:[29,20,16,73],tetgen:[6,33,64,56],interv:[39,1,72,35,7,54,79,73],maxim:[39,79,10,20,24],nosepar:12,modelview_callback:24,intern:[0,26,35,6,27,55,73,16],flatten:[12,32,39,9,41],tr24:71,usermenu:70,drawabl:[32,1,15,31,18],successfulli:64,transmiss:65,wirest:[8,10],fov:[68,19],total:[39,46,26,72,35,12,79,68,73],ico:42,bottom:[37,19,69,42,72,82,29,32,54,34,24,16],stopatbreakpt:4,subclass:[38,39,70,26,27,6,29,31,79,22,18],circ:[12,14],unipl:73,access:[17,0,65,20,21,69,42,49,72,74,29,64,11,23,56,34,82,16],contactprop1:12,condit:[39,65,27,6,8,77,12,79,14,73,16],removeannot:[68,19,18],cselect:[6,26,73],createmovi:74,localhost:13,intersectiontimesswt:40,peek:53,plu:[39,65,26,72,35,6,29,79,18],addmeannod:[6,26],colormap:[19,1,82,31,32,15,36],uncompress:[42,20],qtextedit:29,payment:65,addtogui:32,read_error:6,scrollabl:29,create_insert_act:70,promot:2,repositori:[54,64],peer:65,post:34,error_msg:22,inputitem:29,chapter:[67,69,42,51,11,81,82,58],obj:45,calpi:[42,64],glsmooth:19,slightli:[39,79,10],groupargmin:9,surround:22,simul:[42,8,25,27,64],dynapan:68,parselin:22,untrain:16,natom:43,commit:65,xfce:54,runcommand:53,produc:[60,39,42,65,66,2,26,72,6,32,54,35,16],matchcount:53,civil:65,xyz:1,"float":[28,39,55,53,1,14,26,73,6,7,29,9,10,40,44,32,76,79,62,35,16],profession:[8,64],bound:[39,19,40,69,46,27,32,29,31,12,14,68,35,16],ppm:[74,10],two:[1,2,6,7,8,9,10,12,15,16,19,21,24,26,29,31,32,33,34,73,37,22,39,40,42,46,50,55,27,61,62,65,66,68,70,71,72,35,74,79,82],down:[59,69,25,72,24,53,29,32,80,27,16],barycentr:40,fmtbeamsect:12,readfacefil:33,rsize:39,opportun:1,storag:[65,22,26,20],"0x43d3668":[],constr1:12,listtre:53,hallo:53,accordingli:12,convertunit:55,suffici:[79,69],nodal:[56,66,26,27,6,50,12,33,73],support:[65,20,69,42,26,27,6,29,8,64,54,18],nogui:[49,4],transform:[39,19,50,82,24,42,43,26,73,6,49,8,9,10,64,12,61,79,56,35,16],happi:[54,69,64],avail:[0,35,4,6,8,10,12,16,17,26,29,32,68,18,37,38,42,44,46,49,53,54,55,56,27,23,61,65,66,34,71,73,74,64,79,82],width:[39,19,1,42,32,9,12,68,24,16],reli:[38,65,16],gid:9,editor:[69,34,16],fraction:24,igyi:48,overhead:53,constantli:65,drawtext3d:32,homogen:35,head:[12,39],medium:[54,64,65,20,21],form:[37,39,65,82,46,72,73,6,53,29,9,10,64,48,23,56,34,35,16],offer:[61,65,69,8,10,82],forc:[39,65,66,24,27,53,32,49,10,12,79,73,16],filetypefromext:53,somehow:29,glflat:19,tearoff:70,distancespf:40,writenod:[12,33],"true":[1,4,6,9,10,12,14,15,16,23,18,19,20,21,24,26,29,31,32,62,68,73,37,38,39,40,41,42,46,45,53,27,59,60,22,66,34,35,74,76,77,79,80],fgcolor:[32,19],coords4:35,reset:[19,70,24,76,10,77,32,68,18,16],absent:49,attr:[33,80,27,14],urgent:[12,54],bugfix:64,coeff:12,maximum:[39,19,24,26,73,6,29,50,9,12,79,62,68,35],until:[65,69,4,6,32,33,80,27,16],arccosd:9,inaccur:65,toggl:[19,80,32,68,18,16],absenc:[27,65],fundament:65,sampl:9,emit:[29,70,68,4],classif:37,featur:[65,69,49,8,10,64,32,54,34,18],removedecor:[68,19],stlconvert:6,classic:10,covert:6,"abstract":[78,21,67,42,8,75,51,64,11,81,54,56,58,16],proven:2,drive:16,backsid:31,postscript:[],fromstr:[39,73,16],exist:[4,6,9,12,16,19,20,26,29,32,22,68,73,37,38,47,53,54,55,27,60,61,34,70,14,64,23],strip_blank:22,nface:6,regulargrid:72,check:[4,6,8,9,73,16,29,40,22,80,18,37,39,53,54,27,63,66,35,14,74,64],assembl:12,pipe:14,readonli:29,surpris:16,tip:42,actor:[19,1,45,15,31,32,63,82,56,68,35],ontop:32,tim:75,fmtinitialcondit:12,scriptnam:[37,49,4],test:[37,39,20,66,24,4,26,6,53,49,9,64,32,54,73,16],tie:12,tiscali:55,roll:[39,64,41,8,9,10,40,16],set_edit_mod:32,node:[38,39,46,78,66,45,14,26,18,6,50,27,12,62,33,73,16],intend:[6,8,10,11,12,16,20,27,29,32,34,37,49,54,79,60,61,65,66,70,23,82],downward:59,confign:23,subdivid:[6,79,26,65],vertexdist:40,notwithstand:65,ntot:79,intens:[28,39,10],intent:[69,8,65,16],consid:[39,65,20,26,46,6,29,9,40,53,13,23,56,73],addnois:[39,26,6,50,61,79,73],tri6:38,uniformli:35,tri3:[38,46,26,6,33,73],subdivis:[79,1],longer:[32,54,42,20],furthermor:[37,10,64,24],arctand2:9,ogden:12,midvertex:6,koenderink:6,berlio:[],ignor:[23,38,39,49,21,18,82,26,4,6,29,55,9,48,79,22,68,73,44],fact:16,time:[4,6,8,9,10,12,13,14,16,20,26,29,32,34,73,39,42,47,53,54,27,65,24,71,35,76,64,79],push:[29,16,77],backward:[41,20],targ:53,daili:8,imageformat:[74,42],coordplaneactor:31,correctnegativevolum:[6,46],concept:[8,16],rom:65,chain:66,skip:[26,6,49,8,9,33,16],consum:[10,65],optionsdict:29,simpleinputitem:[32,29],signific:[9,4,65],supplement:65,hallo23:53,adjn:66,scali:39,partition0:54,hole:[6,33],row:[39,66,42,43,26,82,6,29,9,68,73],hierarch:[6,70,26,66],decid:[17,65],hold:[0,2,4,6,7,9,10,14,16,19,21,24,26,29,32,68,18,39,41,44,27,66,35,69,71,73,64,23],depend:[4,6,9,10,12,34,16,19,26,29,31,32,68,73,42,53,54,27,80,69,70,14,64],zone:1,"_arg":[],formic:[2,4,72,82,10,79,62,73,16],decim:[53,79,9,1,16],readabl:[23,65,16,21],quasi:1,hereof:29,vec:[39,9],centroid:[39,26,6,32,12,61,62,73],followin:77,sourc:[17,65,69,14,4,46,6,49,8,64,11,53,23,56,34,16],string:[1,2,4,6,9,10,12,16,28,19,20,21,26,29,32,33,68,73,37,38,39,43,44,50,53,27,23,60,61,22,66,69,70,71,14,74,77,79,82],adj1:66,adj0:66,triangul:[39,56,6,8,10,64,33,34],getresult:[32,29],drawlin:1,feasibl:10,gridactor:31,autosaveon:74,projectonspher:[39,26,6,50,61,79,73],cyan:16,brows:17,getobjectitem:45,sloppi:29,dim:[6,26,31,66],foo:[0,23],allview:32,administr:[34,64],multisav:[74,42],did:[53,75,27],gui:[4,8,78,11,34,16,17,20,29,32,68,37,42,49,56,65,80,69,70,74,64,82],dia:29,gum:27,dic:4,item:[0,45,2,6,8,9,12,14,16,19,21,26,29,32,68,18,37,41,47,50,53,27,63,65,66,34,69,70,73,78,23,82],sine:[25,9,77],contacttyp:12,quick:[29,42],div:[6,79,39,26,73],inputradio:29,round:[76,10,71],dir:[37,39,26,6,29,10,53,34,73],dict:[38,0,19,53,20,41,24,26,27,6,48,29,77,12,63,23,14,68,73,47],upper:[19,69,14,74,29,9,68],mnpg:42,cosd:[9,77],slower:[6,26,64],regret:8,core:[42,56],hack:20,compens:6,sign:[65,26,6,9,68,73],cost:[6,54,64,65],rtriangl:72,alfr:27,insertmenu:70,removedupl:[6,66,26,73],savemovi:74,appear:[19,66,80,69,70,26,46,6,10,68,82,16],green_el:27,discourag:42,directionalextrem:[61,39,73],setnon:29,pick_edg:68,uniform:9,current:[1,2,4,6,9,12,14,16,23,18,19,20,21,24,26,27,29,31,32,33,34,73,37,38,39,64,42,46,45,47,50,53,54,55,56,57,62,66,68,69,70,71,72,35,74,76,77,79,82],sphere3:72,axial:10,lastmod:12,analyticalsurfac:12,sortset:37,deriv:[6,61,64,38,26,73,74,31,53,79,35],autogener:53,gener:[45,3,6,8,9,10,12,14,16,17,20,26,29,48,33,34,73,39,42,46,49,52,53,54,56,27,23,60,61,65,66,69,70,72,13,74,64,78,79,82],coeffici:[12,9],cload:[12,27,14],agreement:65,disclaim:[8,65],rhead:29,slow:[32,53,73,16],modif:[12,39,69,65],allclos:[39,16],address:[29,27,65,13],resetlight:[68,19],along:[39,40,65,27,24,1,26,72,73,6,9,10,31,32,35,16],popul:[39,56,31],waiv:65,toolbar:[34,69,70,54,56,80,82,16],wait:[4,53,29,32,68,16],box:[39,19,65,14,29,31,32,68,35,16],splitelem:50,star3d:72,volumin:21,shift:[39,69,24],worldwid:65,nedgeconnect:[6,26],relock:32,behav:73,overriden:22,bverheg:[10,16],extrem:[39,42,49],commonli:[39,49,16],nmax:9,oldinputdialog:32,regardless:65,checkkei:22,extendedsectionchar:48,compatinputitem:29,dtype:[6,12,39,9,66],modul:[0,1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,17,18,19,20,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,52,53,55,56,57,59,60,61,62,63,66,68,69,70,71,72,73,74,76,77,79,80],prefer:[65,21,69,42,4,64,32,54,66,34,27,16],assemblag:8,paramat:66,nbmark:33,linecolor:31,visibl:[19,65,7,32,68,16],marker:[22,33,19],instal:[6,49,65,17,68,69,42,44,46,74,29,8,64,32,54,55,34,16],polygonarea:40,beul:75,wang:6,jan:[10,16],compaitbl:[6,26,73],wealth:16,prove:[8,65],sake:[65,73,16],subtract:73,arc3:79,helvetica:[32,71],yasymm:27,subvers:64,everybodi:64,"_karg":[],live:[17,27,64,54],handler:22,value3:12,value2:12,value1:12,criteria:4,msg:[32,4],saveimagedialog:29,checkout:64,autodetect:24,reorder:[6,38,9,26,66],plug:54,capit:38,intersectionlineswithplan:[10,73],nborder:[6,26],afford:54,peopl:[53,75],pyqwt:36,listfil:49,finit:[50,26,7,8,10,64,48,12,56,34,27,16],refrain:65,enhanc:[9,64],visual:[1,7,8,64,82,16],appendix:49,rigid:12,thin_plat:27,vmax:24,effort:[29,65],easiest:[64,16,82],behalf:65,"0x4bfcc80":[],fly:32,graphic:[64,45,82,81,49,8,69,10,77,11,32,78,56,35,16],scalar:[39,9,73,40],discontinu:35,prepar:[54,20,16],pretend:17,focu:[32,68,69,42,19],addcheck:29,alt_report:46,mesh1:[6,26],homogon:24,can:[0,1,2,4,6,7,8,9,10,11,12,13,14,16,17,19,20,21,23,24,26,28,29,30,31,32,33,34,73,37,38,39,40,42,43,46,47,49,50,53,54,55,56,27,58,59,61,22,63,64,65,66,68,69,70,71,72,18,74,77,79,82],cam:[],askitem:[32,10],twodimension:26,purpos:[65,69,82,8,10,31,55,34,27,16],cae:12,cad:[8,10,16],adjj:66,claim:[13,65],setangl:24,rotatedactor:31,feop:8,topic:34,intersectionpointsswt:40,dxf:[2,56],pyformexfil:53,posess:27,occur:[65,20,66,42,25,26,6,53,29,9,32,22,34,35],orgin:39,itemtyp:29,alwai:[0,6,8,9,12,16,19,20,29,40,48,22,68,24,39,31,53,54,56,27,34,69,72,73,64,23,82],differenti:82,multipl:[0,1,2,4,6,9,10,16,26,29,32,22,68,73,38,39,42,49,53,56,23,60,63,66,24,69,35,78,79,82],viewpoint:[32,69,16],tilt:24,fpo:73,check_func:22,write:[2,6,9,12,16,23,20,21,26,22,34,18,42,50,54,56,27,61,33,65,69,73,64,79],till:32,vital:11,anyon:[54,65],writedload:12,pure:27,familiar:[69,16],whereami:[49,34,64],tild:53,underflowcolor:7,minpo:9,tolist:35,product:[60,40,65,24,73,64,9,77,48,54,33,68,35],mat:[6,12,39,9],knotpoint:35,max:[37,39,26,6,29,9,73],dive:16,planeactor:31,southern:39,hyperel:[6,26],accel:14,nodefront:[6,45,46],pertin:65,mac:64,rowspan:68,inputfil:12,mai:[1,4,6,8,9,10,12,14,16,17,19,20,21,24,26,28,29,31,32,33,34,73,37,38,39,40,42,46,49,50,54,56,27,22,65,66,68,69,70,35,74,64,79],currentlei:29,fromfil:[39,21,6,9,73,16],data:[0,1,2,4,6,9,11,12,14,16,23,19,20,21,26,29,31,32,22,34,73,38,39,42,44,47,48,50,52,56,27,58,59,63,65,66,24,69,70,72,35,64,78,79],grow:[6,60,45,73],goal:16,stress:[12,55],practic:[32,65,1,55,16],prefixfil:53,array_lik:[39,79,9,35],hyperelast:12,explicit:[39,66,4,26,6,32,12,61,56,68,24,16],fmtcontactpair:12,quadrant:73,inform:[17,19,65,21,68,69,42,75,49,47,53,29,8,9,64,32,34,73,16],"switch":[42,26,6,10,32,22,24,16],preced:[12,79,34,21],combin:[65,66,1,70,26,71,6,69,12,14,68,27],callabl:29,distancespfl:40,directionals:[61,39,73],unlist:66,rgb2qimag:36,approx:79,possessin:21,refreshdict:53,cardinalspline2:79,drawmark:32,subdict:53,pitch:[10,16],braid:10,pointer:[9,65,16],dynam:[69,68,65],entiti:[38,65,66,2,26,6,10,40,55,16],conjunct:49,unproject:[68,19,24],group:[38,39,2,27,53,29,50,9,64,12,14,73,16],polici:[42,35,20],drawgrid:1,daction:70,reduceadjac:66,toru:16,platform:[54,64],window:[19,65,68,1,42,49,71,74,29,69,64,54,80,34,24,16],tet15:38,tet14:38,curli:79,mail:[65,36,13],m_1:27,main:[69,74,29,75,56,27,16],basenam:[12,53],planesect:48,getfreeent:[6,26],non:[6,9,12,16,26,29,33,68,35,37,39,42,44,53,55,65,66,34,69,70,14,64],oneshot:[32,68],kargs0:32,asfollow:73,nod:73,encod:[74,1,42],nof:12,alist:[12,47],initi:[6,8,9,10,12,16,19,20,26,29,32,22,80,73,39,46,47,54,23,59,60,61,65,68,69,74,79],lesser:65,intpoints2:12,insidesimplex:40,insertlevel:[26,66],cutelements3atplan:73,half:[68,19],nov:[],now:[69,42,27,8,76,10,64,54,82,14,73,16],provision:65,discuss:[27,11],nor:[37,38,65,74,9,32,22,34,27,16],introduct:[17,21,67,82,8,78,54,27,16],fea:27,term:[0,65,14,8,10,12,73,16],create_part:12,name:[0,45,2,4,6,12,14,16,28,20,24,26,29,32,33,80,18,37,38,42,47,49,50,53,54,55,27,23,61,22,65,34,69,70,71,72,73,74,64,79],readfil:22,perspect:[19,69,82,68,24,16],revers:[66,25,26,6,9,79,73],revert:25,glutdrawtext:71,resetarea:24,separ:[4,6,11,12,16,21,26,29,33,68,73,39,41,44,49,50,55,23,61,63,65,34,69,70,79],add_item:29,eacho:39,inputcombo:29,januari:[17,42,21],"0x3fb5de8":[],collaps:[6,12,72,66],attributeerror:42,seldom:[6,49,26],scaledjacobian:[6,46],compil:[42,64,65],specifyin:[],domain:[1,25,73,65],smallestaltitud:6,formerli:16,setcurr:68,pacakg:64,individu:[39,19,65,1,26,50,9,63,23,82,16],receipt:65,continu:[23,6,65,1,46,72,24,74,32,29,8,69,12,54,79,82,35,16],borrow:[3,36],bump1:[39,26,6,50,10,61,79,73],wrap:[82,16],redistribut:[65,8,10,16],bump2:[39,26,6,50,61,79,73],year:[64,65],partitioncollect:[32,45],nolight:[32,15],neighbourhood:[6,53],glutselectfont:71,checkworkdir:32,keepmod:24,accomplish:[6,82,26,73],timedelta:76,space:[38,39,71,21,42,44,72,73,6,9,27,79,68,35,16],listnondegener:66,east_west:39,profil:54,intermix:22,checkstr:14,removeitem:70,formula:16,okpoint:40,correct:[38,65,80,69,42,26,6,9,10,64,12,54,68,24,16],drawdelai:32,namelist:18,earlier:[65,16],"goto":[64,39,73,16],occupi:69,ajac:66,runtimeerror:22,argv:[49,4],plex:[66,2,72,6,48,32,79,73,16],theori:64,qmessagebox:29,org:[65,21,8,10,64,54,55,33,16],unpredict:28,care:42,raster:32,setdefault:[0,19,14,12,23,68],visit:[37,0],reorderaxi:9,prescrib:[27,14],flare:[39,26,6,50,61,79,73],castelj:35,catalog:37,vec2:9,right_hand:62,thing:[64,65],"79e":12,place:[37,39,65,66,73,74,9,10,64,11,32,56,34,35,16],principl:42,typic:[37,39,65,66,34,73,53,68,35,16],imposs:10,frequent:[37,54,69,42,64],lambda:[6,39,10],origin:[39,65,20,66,41,24,42,26,72,73,6,9,10,40,61,35,16],suspend:32,reimplement:31,directli:[6,39,64,65,60,24,69,2,4,26,74,29,8,9,27,54,61,66,34,73,16],subrang:7,carri:65,onc:[39,66,8,9,64,32,54,23,82,16],arrai:[2,6,9,12,16,21,26,29,40,32,62,68,73,36,39,31,43,46,50,56,59,61,33,63,66,72,35,64,79,82],setmodelview:24,yourself:[53,0,42,65],reopen:[8,10,58],addview:68,symmetri:79,ring:[6,45,46],nlabel:1,open:[23,61,20,50,69,24,26,73,6,29,8,9,10,11,32,54,79,58,35,16],angular:[10,73],fijk:[39,73],hypar:16,given:[45,4,6,9,10,12,14,16,23,19,24,26,29,40,32,33,68,18,37,38,39,48,49,50,53,27,61,63,65,66,35,70,71,72,73,28,77,79,82],rank:[6,79,26,73],silent:[32,18,9,4,21],undecor:54,sub_point:79,elementof:82,okpid:40,ixx:[48,62],cutwithplan:[6,79,73],givec:1,citi:27,necessarili:[69,16,66],f_0:27,f_1:27,f_2:27,checkvers:53,white:16,conveni:[6,8,9,10,12,14,16,26,29,32,24,37,39,41,49,53,56,27,60,65,72,73,64,79],friend:54,vectorpaircosangl:9,fmtequat:12,especi:[37,20,64,54,73,16],copi:[0,65,50,61,39,18,42,26,14,73,6,8,10,54,79,66,58,35,16],specifi:[1,4,6,7,9,10,12,14,16,28,18,19,20,21,24,26,29,31,32,33,34,73,37,38,39,40,42,43,44,45,47,48,49,53,54,55,27,23,60,61,22,63,64,65,66,68,69,70,71,72,35,74,76,77,79,82],pyqt4:64,broadcast:[9,40],intimamat:12,enclos:[0,39,72,6,48,32],mostli:[39,20,26,6,8,16],encastr:27,thai:34,adjacencyarrai:66,holder:65,than:[35,1,4,6,8,9,10,14,16,17,24,26,29,32,68,18,39,50,53,54,27,23,65,66,34,69,73,64,79,82],png:[42,74,29,10,53,34,16],serv:[69,22,34,65,16],wide:[64,42,34,65,16],uniqueord:9,reverseaxi:9,datalin:12,hemishper:39,"9x15":[1,71],classmethod:[6,39,19,26,73],fmtsurfac:12,were:[37,65,69,4,7,29,10,64,56,34,16],posit:[1,4,6,7,9,10,12,14,15,16,19,26,29,40,32,68,73,38,39,31,41,42,46,47,48,50,53,27,61,66,24,71,72,35,77,79,82],browser:[69,34],pre:[0,1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,28,18,19,20,22,23,24,25,26,29,30,31,32,33,34,35,36,37,38,39,40,41,43,44,45,46,47,48,50,52,53,55,57,59,60,61,62,63,66,68,70,71,72,73,74,76,77,79,80],analysi:[10,27],sai:[54,65],san:[32,15,71],nicer:21,pro:64,has_light:[68,19],argument:[1,4,6,9,10,12,14,16,23,19,26,29,40,32,22,68,18,37,39,31,46,44,49,50,53,55,56,27,61,66,24,72,73,79,82],ank:33,alleg:65,himself:[6,26,27],turtl:[60,56,16,77],deliv:64,exclud:[53,29,65],segmentorient:40,bash:53,mislead:16,anyvector:9,engin:[55,8,77],squar:[1,72,6,9,77,32,16],advic:[54,64,16],pressureoverclosur:12,destroi:[6,79,26,20,73],moreov:65,ngrp:9,wirestentparametricexampl:10,note:[39,64,42,26,6,29,9,40,53,16],vectorpairnorm:9,ideal:8,includedir:53,take:[4,6,9,12,16,29,31,22,68,18,37,38,39,42,49,53,54,27,65,66,34,69,64,82],advis:[64,65],coordsi:27,environ:[69,42,73,8,10,12,24],asknewfilenam:32,addcamerabutton:80,noth:[65,4,82,74,29,75,31,12,23,68,18],channel:28,funcion:[68,16],surf:6,borderedg:6,begin:[69,44,8,79,22,16],sure:[39,19,65,68,61,29,64,54,23,22,34,16],nprop:12,reloc:[54,42,20],normal:[0,4,6,8,9,11,12,34,16,23,19,20,21,26,29,40,32,68,73,38,39,49,53,56,27,65,66,80,69,70,72,35,64,79,14],multipli:[39,19,1,9,35],price:65,knowingli:65,compress:[29,42,39,20,21],neuman:27,paid:8,toplevel:[37,23,27],beta:[6,10],predeclar:53,intersectiontimespop:40,abus:65,sublicens:65,pair:[37,23,22,40,53,66,73,47,32,9,77,12,79,33,34,57,16],seticon:29,icon:[70,42,74,53,29,32,80],readmesh:57,renam:4,knotvector:35,hmax:24,subinterv:79,actionlist:70,insert_sep:70,"_set_coords_copi":61,adopt:65,sale:65,quantiti:[6,39,62,55,56],loadmodelview:24,savebuff:[68,19],runtim:[7,22,4],colorlegend:[7,1],axi:[1,6,9,10,12,14,16,26,40,32,24,36,38,39,48,27,59,35,69,72,73,77],preambl:65,sda:54,recipi:65,usuali:[],sdb:54,show:[27,78,19,65,1,4,18,49,7,29,69,10,31,52,32,54,82,68,73,16],cclip:[6,26,73],delta:68,pursuant:65,friction:12,sethtml:29,map:[37,39,19,82,70,26,24,6,7,50,9,32,61,79,56,68,73],permiss:[42,64,65],fe_post:[56,30],chead:29,corner:[19,1,46,72,6,29,68,73],delprop:[18,14],rotat:[39,40,31,69,26,24,6,48,50,9,10,77,12,61,79,82,68,73,16],slot:[29,70],onli:[0,1,2,4,6,7,8,9,10,11,12,14,16,23,18,19,20,21,24,26,27,29,31,32,33,34,35,37,38,39,40,46,45,49,53,54,55,56,57,61,65,66,68,69,72,73,74,64,79,82],explicitli:[56,65],ratio:[6,24],transact:65,activ:[17,70,19,65,69,42,24,74,8,32,68,18],mouse_pick:68,helic:16,nall:12,fmtanalyticalsurfac:12,parametr:[9,35,16,10],black:[32,54,31,16],helix:[10,16],ddwrite:54,analyt:[12,67,78,35],analys:[10,27],offici:[69,64,65],unique_invers:9,overwritten:20,generalinteract:12,variou:[67,39,56,82,5],get:[0,45,6,8,9,10,12,14,16,23,19,26,29,40,32,34,73,37,42,54,55,27,61,63,65,24,69,35,64,79,82],extrus:[6,26],contractu:65,soon:[68,64],sleeptim:32,cannot:[79,42,65],etcetera:[10,16],beziercurv:35,requir:[2,4,6,8,10,12,14,20,21,26,29,31,22,24,42,46,27,65,66,35,73,64],vmin:24,redrawal:[68,19],coordsbox:29,trianglut:64,straigth:10,unitssystem:55,dramat:64,yield:[12,48,16],irrespect:82,twelv:16,xmax:[53,72],dofunc:29,london:27,anisotrop:12,where:[45,4,6,8,9,10,12,34,15,16,23,20,26,29,40,32,22,68,73,38,39,64,41,42,44,46,49,53,54,55,27,65,66,80,69,35,74,77,79,14,82],checkrevis:4,summari:12,kernel:65,node2surf:12,atol:[6,39,9,26,73],try_resolv:20,lambda_valu:6,save_p:[],striplin:33,toplog:[6,26],materialdb:14,concern:[23,65,21],infinit:39,pick_numb:68,timeit:53,detect:[66,69,49,46,6,53,29,32,34,24],tabinputitem:[32,29],bgcolor:[32,19,16],hex8:[6,38,26,72,73],review:65,setdist:24,label:[37,1,14,29,12,27],hist:9,enough:[69,42,8,64,27,16],rowwis:[68,82],between:[0,1,4,6,9,10,16,19,21,25,26,40,32,22,68,73,38,39,64,46,48,50,53,55,23,61,65,66,67,34,69,72,35,77,79],autorecogn:29,textbox:[32,29],across:65,assumpt:[10,65],getmousefunc:68,parent:[37,61,70,26,6,29,31,32,68,73],comp:4,screen:[69,74,29,32,54,34,82,16],showlinedraw:32,dome:16,askdrawopt:32,flist:73,cycl:[32,25,49,19,68],inputani:29,gtssmooth:6,spare:65,partitionbyedgefront:6,getcoord:[6,26],inputform:29,come:[42,65,69,2,26,6,10,64,11,32,62,73,16],element2str:73,globalax:12,geomview:6,fit:[65,8,9,10,53,16],mono:[32,29],region:[69,33,24],quiet:[74,53,4],tutori:[17,69,8,10,16],datafield:44,inward:6,mani:[0,69,2,26,82,6,8,9,64,79,34,27,16],emiss:32,fmthead:12,writesect:12,improp:54,unrecogn:71,among:[27,65],acceler:[34,82,64,14],inputinteg:29,rescal:[61,26,6,50,10,79,73],overview:[81,29,10,64],acceptdata:29,period:[32,29,20],insist:55,pole:[39,72],enlarg:[39,9,24],anti:65,contactprop:12,joi:13,cancel:[32,68,29,19,24],timeev:53,updatetext:29,undraw:32,poli:[6,33,46],damag:65,save:[6,58,19,64,20,21,69,42,4,74,29,78,10,77,32,54,56,68,24,16,51],messagebox:29,cosinu:9,partitionbynodefront:[6,46],coupl:[6,55,9,64],stretch:[29,42,68],west:[39,73,16],skew:[39,10],hardli:[54,64],invers:[66,26,6,9,24,16],iterat:22,loadcas:27,buttton:16,geomactor:31,succesful:20,return_indic:[6,41,26],tetrahedron:[40,16],valueerror:9,curvatur:[6,79,35,10],createbackground:[68,19],intersectionpointspop:40,tr10:71,glutfontheight:71,thousand:51,angle_spec:[79,9,39,40],bmp:10,wake:32,pick_el:68,formex:[2,4,6,9,10,11,16,21,26,31,48,62,18,39,42,43,32,50,56,27,61,67,72,73,64,78,79,82],attibrut:16,unreleas:20,impati:16,spirit:65,those:[0,2,6,9,16,26,29,32,34,73,37,39,46,53,54,27,60,65,66,69,35,64,79],displacelin:40,"case":[4,6,7,9,12,14,16,28,20,21,24,26,29,31,32,22,18,37,38,39,42,44,49,53,56,27,61,63,65,66,35,69,70,72,73,74,64,79,82],setcoord:6,guidelin:[1,42,34,64],diagnost:49,mount:54,untransform:61,isopar:[39,43,26,6,50,61,79,56,73],trick:[17,42],currentcoordinatesystem:39,invok:[6,26],fil:[23,39,42,26,57,50,30,12,61,79,33,73],removebutton:80,amenu:70,accept_draw:68,planar:[6,8,10,16,46],showinfo:32,insensit:14,advantag:[27,10,16,26],ctrl:69,mysurf:12,metric:[6,46],penultim:79,dynazoom:68,columnwis:[68,82],scipi:[64,16],clutter:[82,16],draw_free_edg:18,ambient:[32,68,19],theta:39,ascii:[6,55,20,21],nsetnam:12,"__init__":[29,10],develop:[37,17,65,20,42,27,49,8,10,64,54,82,34,35,16],author:[65,21,69,42,8,75,27],setpref:4,media:54,medic:10,min_cost:6,setcursorshap:68,same:[0,1,4,6,9,10,12,14,16,23,19,20,21,26,29,32,22,68,18,39,41,42,43,46,53,54,27,60,61,65,66,35,69,72,73,64,79,82],binari:[6,64,4,20,21],html:[17,65,69,49,29,8,55,34],pad:[9,66],document:[17,78,65,21,67,81,8,75,10,64,11,12,54,56,58,73,16,51],inputlist:29,pan:[69,68,24],finish:[29,69,68,49],closest:[6,28,39,68,40],rstretch:[32,68],doublehelixst:10,capabl:[32,39],rotationangl:40,runtimerror:44,nicenumb:9,eid:12,intersectionpointslwt:40,extern:[38,69,2,14,6,53,10,64,11,32,27,16],bumpix:[17,64,54],submenu:[37,69,16],setrendermod:[68,19],updatedialogitem:29,appropri:[19,65,69,42,26,6,10,64,32,68,27,16],group_by_eset:12,grp:14,resetgui:70,inconsist:[6,46],vec3:9,write_stla:6,find_row:6,abqinputnam:12,section2d:[48,56],without:[1,4,6,7,8,78,10,11,12,14,16,26,29,32,18,39,64,42,46,44,49,53,54,55,56,27,61,65,66,24,73,74,77,79],cubicequ:9,readsmeshfacetsblock:33,intima:12,model:[2,6,8,9,10,12,16,25,26,57,29,31,33,34,73,42,49,50,55,56,27,65,66,67,69,72,14,64,78,79],reward:8,setview:32,dimension:[38,39,66,26,72,46,6,29,8,9,10,79,73],updategui:32,addbutton:80,manifold:6,execut:[37,61,42,49,65,69,2,70,4,74,53,29,10,64,11,32,54,23,34,16,30],conform:[12,55,44],excel:54,resp:[39,19,53,66,24,26,72,73,6,7,32,79,35,16],neon:42,runrandom:37,rest:[29,82,21],bitmap:[59,56,36,16],foot:[6,53],aspect:[69,6,8,34,24,16],touch:10,speed:[64,16,77,82],"0x34fdc08":[],concentr:[8,27,14],equilater:[6,72],miscellan:[53,56],fmtdata:12,tablemodel:29,unpract:55,north_south:39,except:[0,19,14,65,66,39,70,26,18,32,9,12,61,23,22,73],param:9,desktop:[54,49,69],identif:[82,27,14],qction:70,exercis:65,glcolor:[28,82],role:16,suitabl:[69,9],topdown:53,setperspect:24,kingdom:27,ignore_error:[39,22],matrixmod:[],around:[23,38,39,24,1,82,26,27,6,69,9,10,40,48,55,22,73,16],tempfilenam:42,read:[2,4,6,8,9,10,12,14,16,20,21,29,32,33,34,18,37,39,42,44,53,55,56,57,22,65,69,73,76,23],drawpick:15,readhead:20,steel:27,avali:54,inputfloat:29,casteljau:79,grid:[39,1,42,72,73,6,8,10,31,35,16],mon:[10,16],mival:7,cilind:10,oldnam:4,mydict:[0,56],presum:16,color:[59,27,19,1,42,4,18,28,7,29,10,31,32,82,56,68,15,16],mod:[12,29,68],tomesh:[79,26,16,73],whitespac:[39,22,73,16,21],noet:[68,19],oogl:6,therebi:77,the_vers:53,integ:[1,4,6,7,9,10,16,20,26,29,32,33,18,39,27,63,66,73,28,76,79,82],shrink:[32,39,73,24],benefit:[65,35,16],either:[1,4,6,8,9,10,11,12,14,16,26,29,31,32,33,68,18,37,39,43,53,27,22,65,66,24,70,72,73,74,23,82],cascad:[37,0,27],output:[28,39,65,66,2,4,6,53,49,30,12,55,56,16],do_light:[68,19],getfreeentitiesmesh:[6,26],manag:[54,69,64,20],intev:7,surf11:12,smoothlaplacehc:6,surf12:12,front_incr:[6,46],setclip:24,esier:68,splash:34,ascend:[35,66],gtscoarsen:6,possiobl:34,pyformexdir:34,adequ:[69,34,26],polynom:9,setrot:24,intact:65,constitut:[6,65,2,26,16],readelem:57,currentdialog:32,drawnumb:[32,16],polygonnorm:40,subpart:8,confirm:16,translatedactor:31,meshfil:57,definit:[38,22,65,20,21,50,66,4,26,28,8,10,12,79,56,14,27,16],iff:29,legal:[37,65,71,23,22,34,24,16],fileselect:29,evolv:20,exit:[49,4,6,53,29,10,32,54,34,16],cylindr:[39,26,72,27,6,50,10,61,79,73,16],notabl:20,gtssurfac:6,refer:[37,17,27,65,66,24,70,26,73,29,32,61,82,56,34,35,16],surftyp:12,exportdxf:2,power:[65,42,29,44,49,64,82,27,16],glinit:[68,19],pick_part:68,edit_draw:68,modeless:29,quadraticcurv:72,ultim:56,broken:16,vertices2:40,found:[23,38,0,19,65,69,70,14,53,8,64,12,54,79,22,34,27,16],removeactor:[68,19],regexp:53,solvemani:9,handi:16,comparison:[4,82,6,26,14,73],patent:65,draw_node_numb:18,greatli:8,ack:[32,4,10],toolchain:8,fmtgeneralcontact:12,meaning:[9,35],degre:[39,40,24,26,72,27,6,32,64,9,10,77,12,79,35,16],stand:65,ncolor:[1,15,31],read_ascii_larg:6,industri:65,"2x2":[42,9],rebuild:[24,64],routin:[79,64],effici:[39,21,26,4,6,9,64,61,62,16],eval:[53,29],coordsystem:[12,27,14],obj_typ:68,terminolog:[],suicid:16,inputfont:29,drawit:16,strip:[53,22,23,33],coodin:73,"import":[2,4,8,9,10,11,12,16,19,20,29,32,33,34,39,42,56,27,58,65,67,69,72,14,64,78,23,82],mark:[22,65,1,82,53,29,32,56,15,16],your:[65,20,21,64,69,42,49,72,82,29,8,78,10,58,32,54,23,56,34,27,16],reboot:54,certainli:[21,69,2,8,10,64],ymax:53,opengl:[59,28,19,64,1,42,71,24,74,29,31,11,32,63,82,56,68,15],equidist:35,bkcolormap:[32,31],hex:[6,28,29,46],loo:[],overwrit:[54,22,20,60],strict:[37,19,44],interfac:[38,61,56,65,69,42,81,32,49,8,78,64,11,12,33,16],low:[39,66,1,6,64,32],lot:[63,20,42,82,6,8,10,64,53,27,16],ftgl:42,mesh0:[6,26],attirbut:[7,29],strictli:24,porvid:31,uniquerow:9,hierchic:66,verbatim:65,matchindex:9,tupl:[1,4,6,7,9,14,16,24,26,29,40,48,33,68,73,37,38,39,47,50,27,62,63,66,34,69,70,35,28,77,79],regard:65,amongst:64,idea:[53,65,8,16,77],preselect:12,genericdialog:29,xsymm:27,settyp:[6,26],realli:[55,73,16,14],illus:1,denomin:65,viewport:[19,80,1,42,49,24,74,29,78,69,32,82,56,68,15,16],faster:[6,32,39,26,73],iterkei:0,pull:69,tripl:[37,9,82,16],fsf:65,possibl:[0,1,4,6,9,10,12,16,21,26,29,40,32,33,34,18,38,39,42,43,46,49,53,27,61,65,66,35,69,72,73,64],"default":[0,1,4,6,9,10,12,14,16,23,18,19,20,21,24,26,29,31,32,62,68,73,37,38,39,40,41,42,47,49,53,54,55,27,59,60,61,22,63,66,34,69,70,71,72,35,74,76,77,79,82],insertknot:35,set_5:27,surplu:[37,44],listselect:29,embed:[56,68],mousereleaseev:68,delimit:[37,23,22,16],prefixdict:53,minumum:[6,46],testdupl:66,parallellogram:9,end_2d_draw:[68,19],secdb:14,creat:[2,6,8,9,10,11,12,13,14,16,23,19,20,24,26,27,29,30,31,32,33,68,73,37,39,64,42,46,81,49,51,52,53,54,56,57,58,60,61,66,80,69,70,72,35,74,75,77,78,79,82],max_cost:6,frighten:82,subprogram:65,deep:[61,41,50,79,73,16],intro:16,decreas:[6,35],file:[2,4,6,8,9,10,11,12,14,16,17,19,20,21,26,27,29,30,32,33,34,73,37,39,42,49,50,51,53,54,55,56,57,58,23,61,22,65,69,18,74,64,78,79],umax:35,fill:[42,6,9,32,73,16],runallnext:37,again:[42,26,6,32,49,12,80,27,16],toformex:[6,79,26],commiss:13,setopenglformat:68,flatkeydb:[22,56,14],prepend:[37,53,29,9],field:[14,70,44,27,29,78,12,23,22,24],prism:72,valid:[65,21,4,27,74,29,9,11,53,23,14,34,73],idraw:68,spatial:16,writabl:[32,4],pathnam:[37,16],you:[0,4,6,8,9,10,12,16,23,19,20,21,26,29,32,62,68,73,37,39,42,44,46,81,49,51,53,54,55,56,27,58,60,22,65,66,34,69,72,24,75,64,78,79,82],checkextern:53,architectur:11,resolut:32,colon:55,condens:10,abcdefghi:[39,73,16],sequenc:[23,6,39,21,42,25,26,73,74,53,49,8,9,10,48,79,66,34,35,16],symbol:[54,1,42,69],vertex:[66,2,73,6,79,24],docstr:[37,17,49,32,56,16],lucki:64,polynomi:[12,9],track:[24,16],nodeconnect:[6,26],reduc:[38,66,1,26,6,12,54,16],neighbour:[6,33],dotpr:[68,9],matchani:53,directori:[37,69,42,4,53,32,29,64,11,12,54,34,16],descript:[71,53,29,8,32,54],max_object:68,gradient:[32,19],chown:54,laplac:6,timeoutbutton:34,problemless:20,licensor:65,convertinputitem:29,waiver:65,represent:[38,2,82,73,6,7,29,32,23,56,35],all:[0,35,1,4,6,8,9,10,12,14,16,23,19,21,24,26,29,40,32,62,68,18,37,38,39,64,41,42,46,45,47,49,50,53,54,55,56,27,61,22,65,66,34,69,70,72,73,77,79,82],dist:[39,24],consider:[10,27],hallo235:53,illustr:[69,8,10,16],forbidden:27,lack:55,neigh:33,liabil:65,improperli:6,mp4:42,code:[39,42,65,20,69,2,3,36,6,29,8,27,10,64,14,73,16],abil:65,turtlecmd:60,follow:[0,1,2,6,8,9,10,11,12,14,16,23,19,21,26,29,30,31,32,62,34,18,37,38,39,42,43,48,49,54,56,27,61,22,65,66,24,69,70,72,73,28,64,79,82],alt:69,disk:[6,54],nstep:[6,66],scr:[4,77],extra:[37,0,64,66,39,4,26,6,29,9,10,27,12,73,34,35,16],semiconductor:65,fmtconnectorsect:12,intersectiontimesswp:40,read_ascii:6,addnod:[6,26],setdrawopt:[32,16],setcent:24,former:11,program:[2,4,6,8,10,11,16,20,26,29,33,34,13,38,42,49,53,55,56,27,65,69,64,82],intersectionpointspol:40,lamb:26,ymin:53,nodeset:12,neglig:10,introduc:[6,16],unconnect:[6,26,73],createscriptmenu:37,m_0:27,nring:[6,46],interf:65,mpa:55,far:[39,26,32,54,23,24,16],faq:[17,42,54],multi:[74,12,29,18,32],util:[37,53,56,9],asarrai:[9,73],naturalsplin:79,mechan:[27,8,10,32,55,73,16],failur:65,veri:[39,42,78,69,1,2,4,26,6,8,27,10,53,54,55,73,16],unalt:73,pick_window:68,testdegener:66,icon0:80,icon1:80,randomnois:9,colleagu:54,draw_nod:18,adjust:[19,20,1,71,69,12,79,68,24],settriad:[32,68,19],cosin:[12,9,77,14],surfaceinsidebord:6,pbm:10,writearrai:9,axesactor:[32,31],dimens:[39,26,72,36,6,29,9,10,79,73,16],quicker:10,listdegener:66,readdxf:2,sync:64,sinusoid:25,tex:10,rate:42,pressur:[12,55],design:[39,65,69,26,71,28,8,54,61,27,16],overclosur:12,pass:[23,60,39,65,20,66,24,42,4,26,6,48,29,9,73,27,32,79,22,68,35],canvass:[32,16],further:[38,65,21,26,24,6,53,10,64,12,54,82,27,16],val1:22,misrepresent:65,tet:[6,46],glupdat:[68,19],val4:22,fneg:73,sue:65,nonlinear:12,sub:14,sun:[],section:[42,65,69,2,14,48,8,78,64,11,12,23,82,34,27,16],tabledialog:29,abl:[53,42,8,23,21],brief:65,delet:[20,42,4,73,23,14,18,16],onselect:29,splitfloat:44,rgba:28,consecut:[6,38,35,26],deepli:8,menubar:[69,70],awk:6,method:[0,6,7,9,10,12,14,15,16,23,19,21,26,29,40,32,22,68,18,39,31,41,42,46,53,56,27,60,61,64,65,66,24,70,73,77,79,82],contrast:65,movement:[32,69,68,24],full:[6,7,8,9,10,12,16,17,19,20,26,29,32,22,68,24,37,38,39,42,49,50,54,27,65,34,69,72,74,64,79],themselv:[0,64,11],variat:[53,10,27],unmodifi:65,surf21:12,surf22:12,behaviour:[49,23],modular:72,middl:[7,39,69,16],icondir:70,nongui:4,modifi:[39,65,69,46,6,49,8,10,31,68,73,16],invoc:[34,4],valu:[0,1,2,4,6,7,9,10,12,14,16,23,18,19,20,21,22,24,25,26,29,31,32,33,34,35,37,38,39,40,41,43,44,46,47,48,50,53,54,55,56,27,59,61,62,63,66,68,69,70,71,72,73,28,76,77,79,82],hypermesh:[6,26],search:[17,0,66,70,14,49,27],sender:13,adddecor:[68,19],prior:[39,65,26,50,10,61,79,73],amount:[39,16,21],meannod:[6,26],pick:[19,64,1,24,7,69,31,32,54,68,15],action:[37,38,65,80,69,70,29,11,32,68,82,16],narrow:[10,27],drawvertexnumb:32,noattr:14,mortier:75,diamet:[72,39,10,40],via:[38,66,42,26,6,8,73],shorthand:[6,26],packagenam:64,declin:65,intermedi:[6,39,38,66,26,74,79,82,16],drawannot:18,"0x3a48230":[],iswrit:4,readili:[65,16,82],filenam:[2,4,6,73,12,16,20,21,29,32,22,18,37,49,53,55,33,69,70,14,74,23],"_set_coords_inplac":61,lefttext:1,famili:[33,65],runscript:37,ngroup:50,qimag:[32,59,29,36],establish:[42,16],select:[37,38,27,71,41,18,69,70,26,4,6,53,29,10,31,32,82,56,68,73,16],upvector:[32,39],granular:76,hexadecim:[28,82],wirefram:[73,8,10,16,31],distinct:[34,27,11],draw_plan:31,showfil:32,regist:53,rightmost:9,coverag:65,tought:34,formul:43,createview:[32,68],"0000000000000001e":[],taken:[38,39,19,64,26,72,6,29,9,40,53,73],buttonbox:29,runtetgen:33,minor:34,more:[4,6,8,9,10,16,23,19,21,26,29,32,62,68,18,37,39,42,49,50,54,27,61,63,65,66,34,69,73,64,79,82],flat:[22,19,29,31,79,56,23,16],diamond:72,desir:[6,10,16,26],usbdev:54,spline:79,abspath:12,subformic:73,flag:[39,66,24,46,4,26,6,29,9,14,73,44],formexactor:82,stick:[54,64,27,16],particular:[65,66,26,6,8,10,16],known:[26,24,6,9,53,14,18,16],compani:8,requirerevis:4,fastnurb:49,endsect:2,none:[0,1,4,6,7,8,9,12,14,15,16,17,18,19,20,21,22,24,26,28,29,31,32,33,34,35,37,38,39,40,42,44,47,50,53,27,23,60,62,66,68,69,70,71,72,73,74,76,77,79,80,82],endpoint:[6,79,39,35,77],counterclaim:65,writegeomfil:4,valuabl:[17,2,16],imagecolor:[59,56],det:40,fmtframesect:12,dev:[54,42,64],histori:[12,37,16],gl_project:24,remain:[39,19,65,18,69,26,73,6,32,29,50,27,49,12,54,23,22,68,35,16],paragraph:[29,65],caveat:0,learn:[69,42,75,10,64,8,16],enddir:39,dec:1,gijk:[39,73],nodefaultconfig:49,def:10,deg:10,young_modulu:[12,27,14],useabl:17,scan:[12,2,34,10,30],difffer:16,share:[68,65],moment_inertia_11:[12,14],accept:[28,19,65,69,4,46,6,29,11,32,68,73],tabular:[12,29,14],sphere:[6,72,39,31,16],minimum:[38,39,46,2,26,73,6,9,12,15],save_window:74,coorrdin:38,vectorpairarea:9,sharp:[6,46],magenta:16,intersectionpointsswp:40,tocoords4:35,uncheck:29,huge:[42,8,10,16],cours:[65,69,73,29,10,53,82,27],court:65,newlin:[29,33,21],mapd:[39,26,6,50,61,79,73],divid:[39,46,26,72,35,6,7,79,73],programmat:80,anoth:[4,6,12,16,20,26,31,32,22,68,73,39,64,47,49,53,55,27,58,34,70,77,79],moment_inertia_12:[12,14],perhap:8,divis:73,stl2gt:6,readfromfil:18,csy:[12,27,14],setlinewidth:[68,1,19,15,31],reject:[32,29],xfwm4:54,simpl:[39,22,82,1,2,71,72,13,6,29,69,10,40,53,23,56,27,16],onoff:[68,80,19,18,24],fillbord:6,regener:65,resourc:42,referenc:10,algebra:[56,73,16],inputtab:29,pye:[37,4],drawtext:32,variant:[8,10],walkedgefront:6,reflect:[17,39,26,46,6,50,10,61,79,73],unblend:35,ff0000:82,plane:[39,40,24,1,26,72,73,6,8,9,10,31,48,79,68,35,16],buffer:[68,19],blood:[8,10],exisit:4,pat:1,associ:[70,65],diagon:[39,69,72],gaussian:6,stabil:64,circumst:65,"short":[65,29,8,32,54,16],fmtconnectorbehavior:12,qtgui:[29,70,68],ani:[6,7,8,9,10,11,12,14,16,17,19,20,21,23,26,29,40,32,22,68,73,39,41,42,47,49,53,54,55,56,27,59,65,66,34,69,70,72,13,28,64,79,82],confus:[1,16],"__name__":[4,10],selectdict:53,checkarrayoridvalu:14,caus:[6,39,65,26,74,54,34,73,16],map3:39,map1:[39,26,6,50,61,79,73],spheric:[39,24,26,27,6,50,61,79,73],shade:19,asformex:73,inverseuniqueindex:9,egg:[39,26,6,50,61,79,73],matchcoord:[6,26],checkbox:29,savemodelview:24,help:[17,70,20,69,42,6,49,8,10,64,53,56,34,27,16],ith:[],inputbutton:29,autoload:[42,34,56],trade:65,held:80,circumfer:[48,72,1,10,16],through:[0,6,8,9,11,12,16,20,40,32,34,73,37,39,41,42,48,56,27,61,65,24,69,70,72,35,64,79],infanc:69,vec1:9,drawviewportaxes3d:32,inputstr:29,loadimag:32,suffer:42,setmod:19,paramet:[1,4,6,7,9,10,12,16,19,20,26,29,40,32,68,73,38,39,31,49,27,66,24,69,70,72,35,76,79,82],maxcon:9,style:[23,29,69],mulitpl:27,dolli:[68,19,24,82],explor:16,inscrib:[79,72,40],itervalu:0,combobox:[32,29],rasterpoint:71,startgui:[69,4],relev:[65,66],late:64,areanorm:[6,40],wrt:40,lc_numer:53,runal:37,version:[17,0,65,20,21,69,42,4,27,6,13,49,8,10,64,53,54,23,22,18,16],exchang:21,roundoff:73,might:[17,39,65,69,42,10,64,32,54,79,34,55,16],alter:10,barycoord:40,good:[39,46,72,6,8,10,64,16],"return":[0,45,2,4,6,7,9,10,12,13,14,16,28,19,20,22,23,24,25,26,27,29,31,32,33,68,73,37,38,39,40,41,43,44,46,47,48,50,53,55,57,59,61,62,63,65,66,18,70,71,72,35,74,76,77,79],return_index:9,yval:53,framework:[29,56,69,14],subtask:69,key_sep:22,writenodeoutput:12,tofil:[9,21],drawpropnumb:32,secondarili:65,adventur:8,eltyp:[39,21,66,43,26,72,27,6,31,12,14,73,16],eventu:[55,27],addmateri:14,nextitem:70,igxi:48,lpattern:73,solver:9,cross_sect:[12,14],instruct:[19,69,53,64,32,54,22,34,82,16],refresh:[53,68,19],easili:[20,21,69,42,10,64,11,12,54,66,34,16],achiev:[6,64,65],getbord:[6,26,66],radian:[48,9,40],compris:[69,62,73],radial:[6,69,10],igxx:48,fulli:[32,54,69,10,16],trailer:23,dir1:39,inplac:[39,79,18],weight:[6,39,26,27],unequ:42,getelem:[6,50,26],hard:[12,54,4],evend:21,procedur:[39,65,73,49,9,64,35],showmessag:32,heavi:[42,8],flavia:[56,57],connect:[45,6,9,10,16,26,29,48,33,68,73,38,39,46,32,54,56,27,60,65,66,70,72,77,79],nicelogs:9,saveimag:74,xval:53,beyond:[79,65],todo:69,orient:[19,69,46,24,6,10,40,12,82,14,68,27,16],upperlimit:12,ftp:[17,64,54],readsmeshfil:33,uselib:[49,34],py2rst:[37,70],drawchang:18,publish:[65,8,10,16],research:8,footnot:8,sustain:[42,65],debuglevel:49,print:[38,0,53,60,39,18,42,4,73,6,32,49,9,27,12,66,35,16],izz:62,occurr:[1,22,9,4,71],testifi:8,foreground:[32,68,19],qualifi:65,proxi:65,advanc:9,raiseerror:53,dealt:16,dofid:14,pub:54,getrot:24,reason:[39,65,69,42,26,6,64,54],base:[38,55,65,20,26,72,27,6,29,8,10,12,79,82,35,16],believ:65,resfreq:12,put:[37,39,41,69,42,4,72,53,32,22,68,73,16],rect:[12,72,14],extend:[39,65,69,46,8,10,11,48,79,56,34,27,16],thread:[],launch:49,veloc:14,exponenti:[12,39],"0x34c4140":[],showimag:29,caption:[32,29],chordal:79,specular:32,inputpoint:29,perman:[54,65],wihch:9,undergo:10,gif:[74,16],integrand:79,frome:[10,72],showbuff:[68,19],major:[73,64,10,65,21],notifi:65,obviou:[10,64],prevent:[4,65],feel:[54,69,42,16],articl:65,trnasform:[6,46],number:[1,4,6,7,9,10,12,14,15,16,18,19,21,25,26,29,40,32,33,68,73,37,38,39,42,43,44,45,46,48,49,50,53,54,56,27,60,63,65,66,34,69,72,35,74,76,64,79,82],evolut:6,bootabl:54,pop:[49,0,4,77],qhboxlayout:29,done:[0,2,4,6,7,8,16,20,26,29,31,32,22,68,73,37,38,42,54,27,61,24,69,35,74,64],least:[23,65,66,1,70,26,47,53,79,22,35,16],dong:6,blank:[22,21,29,12,55,33,34,23,16],stabl:[9,64,20],miss:[39,26,9,64,12,79],circumcircl:40,gpl:[0,65,8,55,22,34,23],pecis:79,differ:[1,4,6,8,9,10,12,14,16,23,20,24,26,29,31,32,22,34,18,38,39,41,42,46,53,56,27,63,65,66,67,35,73,64,79,82],setarea:24,script:[1,2,4,8,78,10,11,16,17,20,29,30,32,80,37,64,42,49,53,54,56,60,65,34,69,70,72,74,77,23,82],benedict:[0,21,42,13,8,75,10,48,23,22,57,16],setcursorshapefromfunc:68,bock:33,"0x2de70c8":[],cmdline:4,paint:[56,19],harddisk1:54,writeabl:4,statement:[61,65,73,10,32,23,56,27,16],cfg:[23,80],protocol:65,tovp:[32,82],splitrandom:[6,26],scheme:[38,66,1,26,6,9,54],store:[4,6,9,73,12,14,16,19,20,21,26,29,22,68,18,39,53,54,55,27,59,34,69,70,24,23],schema:[39,73,16],assign:[6,78,27,16],stdout:[6,12,49,53],option:[4,6,8,9,10,12,16,19,21,26,29,32,33,68,73,37,38,39,42,43,49,52,53,54,27,60,62,65,34,70,72,24,64,78,82],relationship:65,smoothen:26,fromkei:0,selector:[6,26,66],part:[2,3,6,8,9,10,11,12,16,17,19,26,31,32,68,73,39,42,46,47,53,56,27,61,63,65,66,69,72,74,64,79],pars:22,geomtool:[56,40],toolset:68,sold:65,temin:54,wich:6,reinstal:42,albeit:66,level:[37,38,0,19,20,60,39,66,26,6,32,29,64,12,23,22,41,27,16],quad6:38,ffmpeg:[74,42],quad4:[6,38,46,26,73],whenev:[49,29,22,73],stl:[6,69,10],remov:[35,45,4,6,12,14,16,19,26,29,32,22,80,18,37,39,64,42,49,53,54,23,58,60,63,65,66,68,69,70,73,74,77,79,82],quad9:38,qaction:70,horizont:[19,1,71,72,29,69,36,24,16],reus:21,str:[70,9,14],reinstat:65,arrang:[10,65],selectnod:[6,66,26,73],toward:[24,82],exitrequest:4,grei:[28,29],randomli:[6,38,50,26,66],nentiti:[6,26],flyalong:32,beforehand:24,defect:65,favicon:42,packag:[65,49,8,64,27,16],interactivlei:34,karg:[1,6,73,12,14,15,19,20,26,29,31,32,68,18,38,39,45,50,52,61,35,79],dedic:[78,64,11],autoplai:37,finish_draw:68,settrack:24,sell:65,lie:[72,39,40,73,16],built:[69,6,29,64,55,56,82,16],lib:[49,64],ancillari:65,self:[0,6,9,10,12,19,26,29,22,68,73,37,39,42,46,47,50,79,66,35,14,23],violat:65,npart:79,also:[0,35,4,6,8,9,10,11,12,14,16,17,19,20,21,24,26,29,32,62,68,18,38,39,42,46,47,49,50,53,54,56,27,23,22,65,66,34,69,73,75,64,78,79,82],connbehavior1:12,build:[69,70,43,8,10,16],connbehavior3:12,postprocessor:[8,30,56],rootcrop:74,brace:[73,16],getcfg:4,fmtoption:12,resetdefault:[68,19],distribut:[0,1,2,6,8,10,11,14,16,26,22,34,13,37,42,54,55,27,65,69,35,64,23],writetofil:18,surface_volum:6,previou:[39,65,20,66,6,32,64,77,12,54,79,22,73,16],reach:[6,33,68],imageformatfromext:74,xbin:9,react:23,group_by_group:12,plai:[16,4,77],nplex:[38,39,62,21,66,26,57,6,40,33,73,16],conic:72,cancel_select:68,alpha:[28,64,6,48,31,32,68],charg:[8,65],nearestvalu:9,hereaft:[65,69,14,23,27,16],decastelj:35,inputgroup:29,clear:[0,19,65,27,10,32,63,82,68,18,16],cover:[69,65],gtssphere:6,quadtubeintsurf1:12,coven:65,ext:[74,37,53,10,64],exp:[53,7,39],newvalu:18,growselect:6,latest:[17,64,20,54],blend:35,viewbar:34,plexitud:[38,21,66,26,46,6,50,33,73,16],splitenddigit:53,quadbeziersplin:[],cdata:14,krijgslaan:27,discov:22,marklist:15,sublist:[41,47],gl_pickbuff:19,subsect:65,setselect:29,"__set_state__":42,corectli:46,mousemoveev:68,writedsload:12,font:[1,42,71,29,69,32,56,15],fine:[69,27,16],find:[39,64,65,66,46,42,75,4,26,6,29,8,9,40,53,54,23,14,16],penalti:12,sphere2:72,coerc:12,okdist:40,localestr:53,copyright:[65,10,16,13],off_to_tet:6,unattend:8,setnam:[12,78,27],nurbscurv:35,solut:[54,42,9,64],convertrandom:[6,26],return_doubl:79,couldn:23,nongnu:[64,10,16],templat:[53,16],factor:[39,19,1,24,32,29,10,31,12,68,73],inputtext:29,filedescript:53,tetraed:[6,33,26,73],sectiondb:14,affirm:65,hit:[74,54,29,68,39],unus:[6,29,26,14],fmtgeneralbeamsect:12,vectorlength:9,"__file__":[42,4,16],express:[65,69,53,23,35,16],kind:[65,20,66,14,9,12,68,27,16],strut:10,nativ:[61,21,26,4,50,64,79,73],longest:[6,40,66],basemenu:70,intersectiontimespol:40,liabl:65,edgedist:40,tospher:[39,26,6,50,61,79,73],restart:[32,16],workinhg:69,key_error_handl:22,iso2:24,"_point":39,scriptkeyword:37,pointsonbeziercurv:35,coord:[39,56,43,26,73,6,50,9,27,40,32,61,79,33,68,35,16],acut:40,scriptarg:49,common:[39,65,66,41,42,26,6,29,79,56,34,16],cessat:65,quad8:38,wrote:[23,13],readcoord:57,set:[0,1,4,6,7,8,9,10,11,12,14,15,16,17,18,19,20,24,26,27,29,31,32,33,34,35,37,22,39,40,41,42,43,44,47,49,50,53,54,55,57,23,60,61,62,63,64,66,68,69,72,73,76,77,78,79,82],pick_point:68,dump:34,sep:[39,21,4,26,50,9,12,61,79,73,16],readneigh:33,still:[17,61,64,21,67,69,26,6,81,49,8,78,10,58,32,54,34,16,51],startup:[42,34,64],decompos:35,checkmodul:53,iso1:24,iso0:24,iso3:24,sed:42,"2dim":73,iso4:24,iso7:24,iso6:24,bari:40,reserv:27,dynarot:68,sel:[6,31],newnam:4,outward:38,connectorbehavior:12,hbox:29,ingl:35,updatetransparencybutton:80,inconveni:[49,66],remove_triangl:6,tobi:[],someth:[29,42],iteritem:0,addsepar:70,triangleincircl:40,chosen:[37,21],honour:70,convertibel:32,experi:[1,8,64,16],pushbutton:29,altern:[6,0,65,74,49,64,54,68,16],ar1:9,projectselect:29,popup:[37,70],save_main_window:74,onto:[7,39],hotkei:74,complement:[6,26,73],sole:[6,65,31,16],unrol:10,sortedbyarea:6,incident:65,ugid:9,getbordermesh:[6,26,46],vertiv:31,complementari:[6,53,26,73],splitdegener:[6,26],stlname:6,arraytool:[39,56,9,66],inertia:[6,39,62,56],smesh:[6,33],both:[1,4,6,8,9,10,16,20,26,29,40,32,22,68,73,37,38,39,41,44,50,53,23,61,65,66,24,69,72,35,79,82],find_glob:20,last:[35,6,9,73,16,17,26,29,32,33,68,18,37,39,53,27,66,34,70,71,72,24,76,77,79,82],fmtpart:12,license:65,hv12:71,raisekeyerror:0,hint:[54,29],alon:[53,16],event:[32,68,65],roman:71,context:65,tangent:35,whole:[19,65,82,32,29,64,12,54,23,22,68,73,16],shortlin:4,load:[37,14,20,42,24,53,32,49,64,12,56,34,27,16],latitud:[32,39,24,72],simpli:[6,49,9,22,82,16],point:[1,6,7,8,9,10,12,15,16,19,26,29,40,32,62,68,73,38,39,31,42,44,46,48,53,27,59,60,61,66,24,72,35,76,79,82],instanti:24,schedul:32,nedgeadjac:[6,26],stuck:8,sweep:[6,39,26,46],uptod:6,header:[20,21,42,29,64,12,23,22,34],oldcoord:[39,43],shutdown:54,linux:[17,6,64,53,54,34],averag:[38,39,26,6,9,10,12,79],comput:[39,71,64,65,66,24,14,25,26,27,6,48,29,8,9,40,32,54,62,35],edgeload:14,coincid:[39,26,35,6,9,79,73],"5e3":44,checkidvalu:14,vertic:[59,38,19,40,66,1,2,71,24,6,29,69,31,32,79,73,36],devic:[54,10,65],due:[6,1,64,16],perpetu:65,sinc:[17,61,20,50,69,4,24,8,76,64,32,54,79,22,34,73,16],whom:65,myscript:[49,42,34,69],tetrahedra:33,txt:[16,52],threaten:65,brick:[6,46],setal:29,nonexist:0,strategi:[6,26,35,38,10],invis:32,nrh:9,setopt:68,printmessag:32,matchfac:[6,26],imag:[59,1,42,3,36,74,53,29,78,10,64,32,54,82,56,34,69,16,51],consequenti:65,overflowcolor:7,gap:[6,42,26],coordin:[1,6,8,10,12,14,16,19,26,27,29,40,32,33,68,73,38,39,31,43,48,50,56,57,61,62,24,72,35,77,79,82],req_kei:22,understand:[53,8,73,16,82],func:[39,68,29,32,62,80,73],tangenti:69,educ:11,return_point:[6,40],look:[23,0,19,14,82,41,68,69,42,39,24,8,32,54,55,56,34,73,16],mehod:29,solid:[38,19,14,8,32,68,27,16],straight:[1,72,10,77,48,79,73,16],erron:65,iyi:[48,62],batch:[49,8],intersectionlinespwp:40,"while":[2,6,11,12,16,23,20,26,29,32,68,35,38,39,64,41,42,54,27,59,61,66,34,69,72,77,79,82],setpick:68,behavior:[14,29,8,10,11,12,27],error:[0,4,6,7,9,14,16,20,26,29,32,22,18,38,39,42,46,44,53,66,35,69,73,74,23],fun:23,anonym:64,dialog:[32,81,29,42,78],everyon:65,loop:[6,23,26,16,73],pack:64,groupinputitem:[32,29],propag:[6,26,65],xvzf:64,pointsat:[79,35,73],readi:[54,64,65,16],key2:22,key1:[29,22],readm:64,currentc:39,itself:[0,65,21,39,69,66,26,82,6,29,8,10,31,61,22,27,16],canvasset:19,xtra:42,quadrat:[26,72,46,6,9,10,79,73],triangleobtus:40,gimp:42,decor:[19,1,71,82,74,29,32,56,68,69],autoprefix:29,edg2:9,restructur:73,grant:65,nknot:35,belong:[39,63,42,26,6,50,56,16],inputlabel:29,tetrahedr:[33,64],shorter:[68,50,9],decod:1,outermost:16,checkrecord:22,setbkcolor:31,projectonsurfac:39,wedge6:[38,73],higher:[38,39,66,1,26,6,64,12,79,68,73,16],loadcurrentrot:24,rotx:[],optic:1,imagin:29,optim:[6,8,10],gtsrefin:6,expon:[53,7,1,35],zoombbox:32,bump_z:10,inflat:10,piec:65,mid_vertex:6,moment:[6,48,39,27,16],temporari:[6,22,20],user:[0,35,2,4,6,8,78,10,11,12,14,16,17,20,21,26,29,32,22,68,18,37,39,42,81,49,53,54,56,27,65,34,69,73,64,82],actorlist:[68,19],triad:[32,68,1,19],playscript:4,wherev:[6,12,54],specialis:27,chang:[35,4,6,8,9,11,14,16,23,19,20,21,24,26,29,32,22,68,18,39,42,47,49,53,54,27,61,65,66,34,69,73,64,79,82],focal:82,recent:[69,42,6,64,54,16],lower:[6,0,70,19,38,69,66,26,74,7,9,53,68,27],task:[69,8,13,16,11],equival:[0,2,6,12,14,19,26,40,32,22,68,24,38,39,43,79,63,65,66,35,72,73,23],ampl:[82,14],older:[54,42,64,20,21],entri:[38,63,66,69,9,10,53,55,82],warningbox:29,slcolor:19,pickl:[20,21],person:[69,65],"2gb":54,u_0:27,add_group:29,"52z":[10,16],elev:39,tast:23,anybodi:8,bkcolor:[32,19,31],explan:29,chord:35,cuboid:[39,72],obscur:[69,16],collabor:64,belgium:27,shape:[1,2,6,9,10,16,25,26,40,32,62,68,73,39,31,43,46,56,59,61,66,72,35,79,82],return_al:40,world:[9,24,82],northern:39,matthieu:75,read_gambit_neutr:6,depriv:65,syspath:34,cut:[6,39,73,72],cur:32,find_nod:6,examplesdir:34,restructuredtext:[29,75],elbbox:73,doorn:6,shortcut:[41,49,56,16,70],abaqu:[12,56,14,30,27],addviewport:[32,82],getcent:24,rgb:[59,28,7,32,56,82,36],flatwir:[31,16],draw_bbox:18,input:[6,8,9,12,16,26,29,32,33,34,73,37,39,42,81,50,53,55,56,27,66,69,35,28,78,23],distinguish:[65,82,27,16,72],subsequ:[23,6,39,68,25,72,73,74,8,27,10,32,79,82,34,35,16],fforward:32,float32:[6,39,35],frenet:35,bio:54,"boolean":[39,19,66,26,6,29,9,79,35],march:65,format:[2,4,5,6,10,12,16,17,20,21,26,29,31,32,68,73,39,42,50,53,56,23,61,65,66,67,69,71,74,64,79],big:82,jacobian:[6,46],odict:[47,29,56,18],bia:[73,72],symdiffer:41,halt:[4,16],clear_canva:32,thoroughli:27,bit:[19,10,16,36],discriminatori:65,truncat:[37,72,16],lost:[16,66],autosav:74,max_fold:6,docutil:[0,1,2,3,4,5,6,7,9,12,13,14,15,28,19,20,22,23,24,25,26,29,30,31,32,33,68,35,36,37,38,39,40,41,43,44,45,46,47,48,50,52,53,55,57,59,60,61,62,63,66,18,70,71,72,73,74,76,77,79,80],signal:[53,29,70,4],cubeactor:31,draw_cursor:[68,19],resolv:[38,66,9,65,54],rgbcolor:28,inputivector:29,collect:[1,8,9,10,14,16,19,20,25,29,40,32,33,68,45,37,39,41,53,54,56,27,63,65,72,35,79],princip:[6,48,39,62],vectorpairangl:9,popular:64,encount:[22,73],pointsatseg:40,fmtorient:12,file_descript:53,often:[69,42,26,27,6,29,8,11,53,34,35,16],argnearestvalu:9,acknowledg:[32,65],creation:[39,70,26,73,6,29,76,10,32,35,16],some:[0,1,2,4,6,7,8,9,10,11,12,16,17,18,19,20,21,26,28,29,40,32,34,35,37,38,39,41,42,44,46,48,49,50,53,54,56,27,58,65,68,69,70,72,73,74,75,64,23,82],back:[23,20,21,24,42,4,26,6,53,82,8,10,64,32,79,22,34,73,16],rot:[39,26,24,6,50,9,31,61,79,73],global:[1,4,6,9,12,14,19,20,21,24,26,29,31,32,68,18,39,42,48,49,50,56,27,60,35,72,73,77,82],understood:[29,39,16,82],unspecifi:[39,26,72,6,50,79,73],ncol:[32,29,68,82],mirror:[39,26,6,8,10,73],imageview:[32,29,56,3],server:[17,64,65,54],planecolor:31,poisson_r:12,kedit:34,scale:[39,1,25,26,72,46,6,7,29,50,9,10,31,12,61,79,73],extractkeyword:37,redo:16,shall:65,elset:12,dialogreject:32,per:[28,39,26,6,9,64,73,16],prop:[27,21,45,26,18,6,32,12,14,73,16],addactor:[68,19],recognit:29,substitut:[54,4,64],mathemat:[69,43,49,8,10,16],larg:[39,20,1,42,72,27,6,49,8,64,32,54,73,16],undon:18,highlightel:32,mencod:[74,42],leftmost:9,straightforward:16,newdata:29,collectonlength:41,noncommerci:65,machin:[19,65,42,6,64,54,68,82],constru:65,object:[0,45,2,4,6,9,12,14,16,23,19,20,21,24,26,40,32,68,18,39,42,46,47,50,56,27,61,65,66,35,70,73,76,79,82],run:[37,39,65,20,64,69,42,4,6,53,49,8,78,10,40,32,54,55,33,34,16],analysurf:12,openglformat:68,perpendicular:[39,69,73,6,10,40,24],addhighlight:[68,19],checkdict:19,step:[39,65,82,46,2,25,4,26,6,32,29,8,10,77,12,79,14,73,16],prerequisit:[8,64],example1:16,glenabl:19,vamera:24,impos:65,shrinkag:[6,26],"9999999999999995e":[],testmodul:49,contact0:12,gnome:69,idx:73,constraint:12,materi:[19,65,14,32,8,12,68,27,16],complimentari:[6,26],shini:32,accept_select:68,incircl:40,drawfac:38,dialogaccept:32,vectornorm:9,modal:[32,29],entitit:[6,26],gambit:6,block:[39,33,16,21],reduct:66,repair:65,whereprop:73,real:[34,1,9,76,16],primarili:[54,29,8,82,65],lifetim:[39,35,16],arcsind:9,readsurfac:33,within:[39,65,21,26,6,7,29,9,64,11,73,16],npoint:[39,73,6,31,79,68,35],nodeprop:[12,27,14],nsy:9,nmatch:[6,39],frog:27,namesequ:53,ensur:[32,65,40,16],xwindow:28,durabl:65,connector:[12,14,10,27],inclus:[10,65],ixi:[48,62],span:[0,1,2,3,4,5,6,7,9,12,13,14,15,16,28,19,20,22,23,24,25,26,29,30,31,32,33,68,35,36,37,38,39,40,41,43,44,45,46,47,48,50,52,53,55,57,59,60,61,62,63,66,18,70,71,72,73,74,76,77,79,80],mask:65,xpm:[42,10],errno:42,proprietari:[2,65],question:[42,4,53,29,64,32,54],elem:[66,14,26,46,6,50,31,12,33,73,16],textual:2,custom:[65,69,70,29,78,64,55,56],ixz:62,adjac:[39,66,45,26,46,6],doubt:65,feabq:27,suit:[69,8,23,34,73,16],forward:[41,77],adict:14,propertydb:[12,27,14],settext:29,readresult:57,everyt:55,properli:[54,29,42,82,64],repeatedli:[8,80,16],changelayout:68,rotmat:9,mousepressev:68,mouse_draw:68,unsort:[6,53],cet:[10,16],link:[65,42,10,32,54,68,82],translat:[60,39,40,50,69,26,24,6,8,9,10,31,32,61,79,82,73,16,30],newer:[42,20],atom:[43,68,60],grep:[49,4],line:[1,2,4,6,8,9,10,12,16,23,19,21,26,29,31,32,33,68,73,37,39,40,42,44,46,49,53,54,55,27,61,22,65,66,34,69,72,24,77,78,79],octant:[39,73],inputboolean:[],dull:8,info:[37,20,66,4,49,29,64,32,73],concaten:[39,41,26,6,79,73,16],newview:68,utf:53,save_canva:74,consist:[38,39,70,55,65,50,21,69,66,26,72,73,6,8,27,10,31,48,79,35,16],framedgridlayout:68,getcolor:29,caller:[4,26,6,32,68,24],unrecover:20,selectfont:29,intim:65,getint:33,contin:6,horner:9,en_u:53,similar:[6,65,22,26,16],exl:12,curv:[39,66,2,25,26,72,27,6,53,48,61,79,56,35],skipcom:33,constant:[21,1,53,32,9,12,35,16],createferesult:57,flow:65,elemnr:50,cnt:6,doesn:[29,66],handedit:42,readfac:[],incomplet:[78,46],cure:65,qgridlayout:68,destruct:29,guarante:[38,39,65,0,8,79,34,27],nbin:9,curl:79,relicens:65,singli:6,bracket:[73,16],undochang:18,rainbow:7,unifi:[29,27],zoomal:[32,68,19],polygon:[6,72,40,16],draw_object_nam:18,dummi:22,titl:[37,54,29,70,69],sequenti:[9,16],nan:[6,79,9,35,39],splitprop:[6,26,73],invalid:[79,22,39,65],rotationmatrix:9,listdir:53,newprop:73,codec:42,rrggbb:29,librari:[65,42,49,71,29,64,34],nice:[6,42,9,82,16],changeext:53,excludedir:53,draw:[1,4,6,78,10,11,15,16,19,31,32,33,68,18,38,64,46,49,56,60,69,71,73,74,77,79,82],glutbitmaplength:71,impress:16,wiemersdreef:27,totyp:[6,26],setfgcolor:[68,19],drag:69,slice:[6,26],itemlist:[32,68,19],amplitud:[39,14,25,10,27],splitter:69,mymodul:16,draw_focus_rectangl:[68,19],bot:72,longitud:[32,39,24,72],setambi:[68,19],svn:[54,64],inf:35,algorithm:[66,26,6,9,10,40,79,35,16],vice:36,svg:[64,16],cdict:0,required:[],dataread:[56,44],elementtyp:[38,26],setelem:6,directionsat:79,xbm:10,depth:[78,68],ho9wev:4,highlightactor:32,sciptsdir:37,insertseper:70,colinear:9,runnext:37,colum:[29,43],sectionchar:48,readtetgen:33,multicanva:68,inputslid:29,partial:[17,2],edg:[38,40,66,46,26,72,18,6,32,9,31,12,14,68,73,16],getent:[38,26],removedegener:[6,26,66],scratch:[64,16],superspher:[39,26,6,50,61,79,73],alien:42,export2:4,spawn:53,tooltip:[29,70,80],zplane:68,exportobject:45,compact:[6,26],fdiffer:[68,19],privat:65,procur:65,addaxi:9,drawrectangl:1,elsewher:[56,11],young:[8,27,16],conspicu:65,sub_points2:79,projectoncylind:[39,26,6,50,61,79,73],pulldown:70,boundary_weight:6,ftype:[6,53],indemnif:65,starter:49,aris:65,sent:16,asformexwithprop:73,wait_draw:68,zoomcent:[68,19],arteri:10,oflow:7,outputfil:23,wave:10,mous:[69,82,49,78,32,54,68,24,16],electron:65,fuction:29,volum:[39,65,66,26,46,6,9,32,33,24,16],togglebutton:80,radiobutton:29,wipo:65,untouch:54,implicitli:[22,82],wipe:82,conjug:9,tri:[20,66,70,26,6,29],trl:[39,26,24,6,50,9,31,32,61,79,73],button:[34,69,70,4,49,29,32,54,80,68,82,16],radii:40,tangen:9,trf:43,unquot:[69,22],session:[54,58,20],maxsiz:29,firstword:22,edgeadjac:[6,26],axesmark:15,pleas:[64,65],inputcolor:29,impli:[65,4,49,8,10,16],smaller:[19,20,66,24,26,4,6,29,50,68,35],visualis:[10,31],surf2surf:12,natur:[38,79,65,66],cfd:27,dialogbutton:29,crop:29,uniqu:[39,63,66,24,14,26,73,6,29,50,9,27,22,35,16],sub_directions_2:79,fold:[6,10],ovc:42,blanklin:9,video:42,download:[17,8,64,54],elemlist:6,odb:12,click:[69,70,49,29,32,54,68,82,16],append:[47,39,4,18,6,53,9,12,79,22,34,73],toggleannot:18,compat:[39,20,66,32,9,40,12,79,27,16],index:[17,39,19,66,41,70,4,26,6,29,9,82,64,32,79,56,73,16],turn:[37,39,42,74,29,40,79],nremov:6,compar:[39,42,73,40],cone:72,paramtr:35,feedback:[64,16],mesh2:[6,26],blippo:42,dunplicat:22,henceforth:82,reducedegener:[6,26,66],listuniqu:66,upzoom:69,matchmani:53,deduc:6,whatev:[65,66],impact:10,inspect:82,construct:[37,39,20,69,26,72,27,6,29,10,61,79,22,35,16],mergenod:26,intersection_curv:6,linesep:12,len:[19,41,1,82,32,9,69,12,68,24],bodi:[12,65],myscriptsdir:34,linedraw:1,ubuntu:64,ioerror:42,sind:[9,77],beginrec:22,dxfparser:2,redrawn:[68,19],remark:[12,9,72,16,66],convers:[38,20,1,26,44,6,28,29,9,32,55,56,35],ctr:24,dtyp:[39,35,66],gent:27,larger:[39,65,4,24,6,54,79,35,16],technolog:65,sortbycolumn:9,initialc:39,readelemsblock:33,getdist:24,"8x13":71,connectcurv:72,implement:[6,9,12,16,19,20,21,26,31,68,73,39,42,49,55,56,27,61,63,65,66,69,70,35,64,79],hexagon:72,emit_don:68,picknumb:68,chanc:6,tocylindr:[39,26,6,50,10,61,79,73],bare:16,wid:32,firefox:34,nearest:[9,71],forbid:65,appli:[61,65,43,26,6,32,29,50,9,12,79,68,73,16],approxim:[38,65,26,72,24,6,10,79,33,35],wil:[6,32,79,26,73],foundat:[65,8,10,16],apt:[42,64],scriptdir:[69,34],expect:[39,65,1,0,8,69,23,73,16],nodsel:[6,26],sphereactor:31,writesmesh:33,bgcolor2:19,lima:[60,56,77],numer:[39,53,21,1,42,25,44,32,74,7,9,10,64,12,56,27,16],scalei:39,gridlin:1,rectangluar:72,nest:[41,16],correpond:[61,73],fee:[8,65],from:[0,1,3,4,6,7,8,9,10,11,12,14,15,16,17,18,19,20,21,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,44,45,46,47,48,49,50,52,53,54,55,56,57,58,59,22,63,64,65,66,68,69,70,72,73,74,75,77,79,80,82],usb:[54,27,64],zip:[79,63],commun:[42,65],doubl:[37,42,26,73,6,9,10,22,35,16],projectonplan:[39,26,6,50,61,79,73],upgrad:[54,64],mydir:64,next:[0,6,8,10,11,16,23,26,29,32,33,34,73,37,39,42,46,53,54,27,65,70,74,79,82],websit:[69,42,8,64,34,16],few:[0,42,8,64,54,73,16],rigth:16,camera:[19,80,69,82,49,31,32,56,68,24,16],nctrl:35,usr:[53,64,16],postprocess:[7,56,25,10,16],coorespond:50,nodid:73,panel:[54,29,69,49,16],start:[0,2,4,6,8,9,10,12,16,23,20,21,29,32,22,68,73,37,38,39,42,46,49,53,54,55,56,27,65,34,69,72,35,74,76,77,78,79],remaind:[22,82,21,44,11],sort:[37,47,39,63,66,6,29,9,10,53,62],dash:72,qwidget:29,princtensor2d:48,src:53,writeelemresult:12,colorset:16,transarea:24,addtimeout:29,trail:[74,53,33,22],bisector:73,"0x4ea6578":[],"31kg":44,iii:10,clipatplan:[6,26],executescript:4,account:[6,26,73],chdir:[42,4,16],retriev:[29,20],displ:[12,27,14],augment:6,wirediam:10,alia:[1,27,24],arang:[39,66,26,6,9,73],when:[0,1,4,9,10,12,34,16,19,20,21,24,26,29,32,22,68,73,37,38,39,42,49,53,54,56,27,61,65,66,67,80,69,70,35,74,64,82],annot:[19,15,10,32,56,68,18],cumbersom:[8,16],getfil:37,addrul:60,movingaverag:9,obvious:[69,10,64,55,22,73,16],unhappi:69,executor:49,meet:[64,65,66],setprintfunct:73,control:[65,24,64,79,68,35],hexstentbarsurf:12,quickstart:16,tar:[42,64],be052320:55,process:[53,68,49,82,6,32,29,64,12,34,27,16],lock:[32,68,19,4,24],sudo:54,checkselfintersectionswithtetgen:6,high:[6,64,1,39,72],addactionbutton:80,tag:[12,9,27,14],bene:[37,70],bend:8,tab:[29,16],xmin:[53,72],tan:24,np1:40,serial:[39,26],np3:40,writeset:12,delai:[32,34],surfac:[39,46,64,45,26,72,73,6,48,8,10,56,12,33,34,35,16],fast_sel:29,edgeangl:6,siz:[32,68,1,19],six:24,line3:[38,66],line2:[38,79,73,72],pyqt:64,cardin:79,groupbox:29,scriptmenu:[37,56],subdirectori:[32,37,34,64,11],coarsen:6,palet:7,fe_abq:[12,56,14],np2:40,sin:9,inher:10,circular:[79,73,16,14],npl:12,harddisk:54,circulize1:73,npi:40,overridden:[22,31],edgefront:6,thrre:24,watch:0,act:[70,27,31],coordslist:[6,26],highlight:[32,68,69,19,16],togeht:27,decrib:[48,82],sortadjac:66,modebar:34,simulatiopn:64,intersectionwithplan:[6,73],npt:[6,79,33,39,40],philosophi:[61,65,16],physic:[29,56,55,10,65],vecor:9,essenti:[64,56,65,16,11],hmin:24,seriou:69,congruent:[6,26],element:[45,6,7,8,9,10,12,14,16,21,26,40,32,33,68,18,38,39,31,41,44,46,47,50,56,27,62,63,66,34,70,72,73,64,78,79,82],issu:[64,20],setpoints:[68,19],voskenslaan:27,unaccept:65,allow:[1,2,6,8,9,10,11,12,16,23,20,21,26,29,31,32,22,68,73,38,39,42,55,27,61,65,66,34,69,70,14,74,64,79,82],strnorm:53,mainfram:8,removedict:53,binorm:35,movi:[74,42,82],move:[39,19,40,69,42,24,9,10,77,32,82,68,73,16],closesttopoint:39,own:[37,65,20,24,69,42,71,27,32,49,12,54,79,34,73,16],comma:[12,53,73,16,44],line1:79,interact:[17,64,65,68,69,49,82,32,29,8,78,31,12,56,34,18,16],centripet:35,checkarraydim:9,obtus:40,listal:[4,18],unum:55,outer:26,runallrandom:37,fulfil:65,binnengegaan:[37,70],gtssplit:6,pickle_load:20,send:[69,70,49,29,53,55,56,13],suppli:[74,39,69,24,16],restrain:27,infrastructur:[32,68,64],keyedlist:47,bin:[41,9,64,53,54,16],criterion:65,elemprop:[27,14],therefor:[17,0,65,21,39,69,26,24,49,29,9,10,64,32,13,82,56,34,27],pixel:[32,68,1,19,71],draw_state_lin:68,marksiz:[32,19,31],okedg:[6,46],nurb:[49,56,35],fourth:[39,35,16],later:[0,65,20,21,8,10,32,23,22,58,13,16],python:[0,2,4,8,16,17,20,21,34,73,37,41,42,47,49,53,55,56,27,69,70,14,64,23],frontal:[6,45,46],overal:39,guess:[6,29],dai:[65,16],newmap:73,dat:12,dock:29,facilit:27,scriptfil:[49,4],front:[37,46,82,6,29,9,32,24,16],surviv:65,serif:71,shape_weight:6,sohuld:35,morescript:69,showwidget:68,writefil:22,somewher:[8,16],modu:10,auto:[32,27],anyth:[64,65,14,4,24,28,9,31,11,32,23,22,27,16],edit:[37,20,21,68,69,70,49,32,29,12,54,34,16],unlimit:[9,65],fomrex:73,slide:82,scrollbar:32,mode:[45,4,6,16,19,20,21,24,26,29,40,32,22,68,18,31,49,50,54,61,65,34,69,70,73,74,79,82],disregard:[68,79,9,16],fmtconstraint:12,intersectiontimeslwl:40,nearli:[0,46,27,6,8,10,64,54,73,16],upward:[59,16],subset:[6,41,26,66],tension:79,intellig:23,"try":[39,19,20,66,69,42,26,6,29,8,49,64,32,23,68,16],mpng:42,hasmodul:53,distancefromplan:[61,39,73],foreward:40,gray2qimag:36,meta:75,"static":24,showtext:32,our:[17,2,65,20,69,42,71,82,8,64,12,54,23,27],tubular:[8,10],elaps:[32,29,76,4],special:[6,9,10,16,21,26,29,81,73,39,47,32,55,56,27,65,66,69,70,78,79,82],matdb:14,refnod:12,recuc:[],drawbbox:32,matrix:[39,43,6,8,9,24,16],twice:39,influenc:[32,79,73,24],rep:[39,73],closer:[68,19,24],rev:4,setcolumnstretch:42,grepsourc:4,categori:[8,11],newrecord:22,rel:[38,39,42,26,24,6,77,53,79,73,16],nvalu:[43,57],hardwar:[54,82],rec:[22,27,14],ref:[],defens:65,math:[79,9,10,16],gllinestippl:19,clarifi:11,timedout:29,experiment:[34,46,49,64,54,33,68],insid:[6,9,10,11,16,20,21,26,29,30,40,48,22,68,73,39,31,53,33,65,66,23,82],sendmail:[56,13],tentative_numpy_tutori:16,manipul:[69,42,35,49,10,64,82,24,16],nodesperbox:[39,73],undo:[6,32,26,18],surfacebehavior:12,oerrid:12,scope:65,usabl:54,dictionari:[0,4,12,16,19,21,29,32,22,24,37,38,47,55,56,27,33,18,70,72,14,23],tempt:42,releas:[32,69,68,64,65],van:6,afterward:[42,8],shortest:[6,44,40,66],complianc:65,indent:[37,39,16],printelementtyp:38,financi:[54,64],affero:65,renumberelem:[6,26],unnam:[34,14],zasymm:27,ask:[65,69,42,4,32,29,8,64,12,54,18,16],toworld:24,timer:[29,56,76],keep:[60,39,18,19,65,68,69,42,73,47,24,29,8,27,70,54,55,22,34,35],cell1:10,length:[6,9,10,16,26,29,40,32,68,73,39,31,41,43,50,55,66,71,72,35,79,82],enforc:[6,32,26,65,27],registrar:11,outsid:[38,39,65,26,6,7,9],writefileoutput:12,geometri:[2,4,5,6,8,78,10,12,16,20,21,26,31,32,33,68,18,39,50,56,27,59,61,66,67,69,72,73,79],retain:[38,65,66,26,35,6,31,79,68,73],growaxi:9,tildeexpand:53,south:[72,39,73,16],ndim:[38,39,73,29,9,12,33,57],softwar:[17,65,21,2,4,82,74,49,8,10,64,55,13,16],suffix:53,christoph:[],qualiti:[6,65,42,46,74,64,33],surface_menu:[42,34],echo:[54,4],exact:[65,66],date:[42,65,21],submit:64,owner:54,readdatabas:14,ndiv:79,facil:[19,18,65,11],underscor:23,suffic:[65,82,64,12,27,16],prioriti:69,"long":[20,65,69,24,8,27],countlin:53,movingview:9,unknown:[53,64],licens:[17,65,8,10,34,13,16],libgl1:42,setprop:[26,18,6,10,79,73,16],system:[4,9,11,12,14,16,17,29,31,22,68,24,39,53,54,55,56,27,60,62,65,34,70,73,64],wrapper:[55,42,56],drawimag:32,attach:[27,65,82],lowerlimit:12,nattr:33,input_timeout:29,aaaaaaaaaaaaaaa:[],eall:12,man:4,"final":[17,19,65,60,69,2,26,27,6,49,8,10,64,32,82,34,73,16],iter:[6,23,26],picked_closest:68,shell:[53,54,16,11],vectorrot:9,positon:[1,71],drawvector:32,methodolog:10,lowest:[66,4,46,6,9,12],slider:29,accompani:[65,16],rst:[37,49,70],nobodi:[8,64],haven:27,cyl:27,colat:39,min_edg:6,insertact:70,embodi:65,bother:64,emac:[69,42],structur:[39,21,69,66,49,72,27,47,29,8,10,64,11,48,82,56,73,16],charact:[6,39,71,21,1,70,26,27,74,53,29,38,60,12,54,23,22,73,16],stippl:19,arc:[72,79,2,73,16],sens:[69,34,73],becom:[37,17,39,64,23,69,70,26,72,73,6,29,8,9,27,32,54,79,22,35,16],bind:[64,0,34,69,16],readpolyfil:33,iso5:24,esetnam:12,ignore_signatur:[],nctrlu:35,askdirnam:32,elemsect:[27,14],rtol:[39,9,73],printval:18,find_triangl:6,save_rect:74,clip:[39,26,24,6,10,31,73],mplex:50,viewer:[69,3,29,56,34,1],favorit:16,arg:[61,18,46,24,4,26,6,29,50,9,77,32,79,14,68,73],deprec:[32,27,20],clearli:[65,16],correspond:[35,2,6,7,9,10,12,16,24,26,29,40,32,33,80,18,37,39,46,44,50,54,55,27,59,63,65,66,34,69,73,82],neel:75,fail:[65,64,4,20],have:[0,35,1,6,8,9,10,12,14,16,23,21,24,26,29,40,32,22,68,18,36,37,38,39,41,42,43,44,45,46,47,48,49,53,54,55,27,60,61,63,65,66,34,69,71,72,73,74,64,79,82],tabl:[17,39,38,69,66,26,82,6,29,73,16],need:[2,4,6,8,9,10,12,16,23,21,24,26,29,32,62,34,18,38,39,42,44,81,49,51,53,54,56,27,58,65,66,67,35,69,72,73,74,64,79,82],curved:6,nqi:40,border:[6,66,1,26,46,74,34,73,16],gtscheck:6,saveicon:74,depict:10,thin:27,min:[39,26,6,29,9,73],getfac:[6,26],nsegi:66,accuraci:9,builtin:[32,41,0,71],discret:[26,77],splitbyconnect:[6,46],which:[0,1,2,6,7,8,9,10,11,12,14,16,23,18,19,21,24,26,29,31,32,22,68,73,37,38,39,40,41,42,43,44,46,49,53,55,27,58,59,61,65,66,34,69,72,35,76,64,79,82],hallo245:53,int64:66,setbgcolor:[68,19],divers:27,singl:[35,1,2,6,8,9,10,12,13,14,16,19,21,26,29,40,32,22,68,18,39,45,54,55,27,60,65,66,34,69,72,73,64,79,82],uppercas:38,nq2:40,textur:68,nq1:40,unless:[37,65,39,53,20,26,44,6,7,29,8,9,64,32,23,22,18,16],clash:0,preliminari:17,mimick:[1,15],who:[64,75,65,16],curve1:72,multitud:10,curve2:72,sfn:64,eight:[6,16],deploi:10,segment:[39,40,66,72,6,8,10,77,48,79,73,16],why:[23,42,65],pyf:[42,21],slighli:35,rollax:[39,26,6,50,61,79,73],drawlinesint:[32,68],placement:34,drawopt:32,vectorpairareanorm:9,convertdxf:2,r_0:27,sweepcoord:39,r_2:27,gather:[37,26,48,8,12,34,16],request:[38,39,70,64,20,66,69,42,26,44,6,53,29,9,27,12,54,55,82,68,18],formian:73,face:[38,66,69,26,72,46,6,12,33,16],inde:[82,9,27,16,66],deni:[42,65],hypercylindr:[61,26,6,50,79,73],avgvertexnorm:6,determin:[6,39,65,1,25,71,74,9,40,12,79,82,16],versa:36,north:[72,39,73,16],occasion:[65,39,2,20,16],constrain:27,matchnon:53,qpixmap:29,nmin:9,projectionvov:40,getpoint:[6,26],acknowleg:4,projectionvop:40,text:[1,2,4,6,10,12,15,16,20,21,29,32,22,80,13,42,43,53,56,33,34,69,70,71,23,82],filetyp:[6,53],verbos:[74,6],affin:[39,26,6,50,61,79,73],elif:16,removeviewport:[32,82],polylin:[6,79,2,39],add_tab:29,rough:12,trivial:16,anywai:[50,34,64],redirect:[54,49],buggi:46,locat:[42,10,11,32,54,56,73,16],nois:39,perpendicularvector:40,nre:57,should:[0,1,2,4,6,8,9,10,11,12,13,14,16,23,18,19,20,24,26,29,31,32,33,34,73,37,38,39,42,43,44,45,47,48,49,50,53,54,55,27,61,22,65,66,68,69,70,71,72,35,74,64,79,82],manufactur:65,temperatur:12,smallest:[6,39,9,40],suppos:[42,73,16,71],combo:29,tune:[6,69,26],addtimeoutbutton:80,nonzero:[53,72],local:[17,39,53,65,38,24,66,26,18,6,32,50,27,10,64,12,23,14,73],hope:[65,8,10,16],updatedata:29,cube:[6,72,31,16],contribut:75,xasymm:27,convert:[2,6,9,14,16,28,19,20,21,26,29,32,73,36,37,38,39,42,46,44,49,53,55,56,59,66,67,70,35,74,79,82],geuzain:[],pointsoff:79,autom:[49,8,69,16],set_data_from_head:[],wordt:[37,70],cancel_draw:68,diamat:73,jpg:[29,42,10,16],showdescript:32,increas:[6,12,9,26,38],sub_curvatur:79,"123e4rt345e6":44,expressli:65,some_vari:16,arraymodel:29,triangl:[40,66,1,72,6,10,31,33,73,16],denorm:35,enabl:[39,19,65,68,24,29,8,10,34,18,16],organ:[12,29,34,65],linestippl:[19,1,31,32,68,15],displac:[12,27,14],hollow:14,grai:36,integr:[12,69,27],partit:[66,45,46,26,6,32,54,73],contain:[2,4,6,75,9,10,12,16,17,19,20,21,23,26,29,31,33,68,73,36,37,38,39,44,46,47,50,53,54,55,56,27,59,61,22,65,66,34,69,72,14,64,78,79],menuitem:70,apect:6,grab:[74,64],view:[19,65,68,69,24,9,10,32,78,82,34,73,16],selection_filt:32,attibut:22,legaci:[32,42,20],inabl:65,vectortripleproduct:9,frame:[12,42,68,16],knowledg:[64,69,8,65,16],rc1:64,elast:12,similarli:10,uniformparamvalu:35,processarg:4,script1:37,statist:6,stack:[26,6,29,9,77,16],formatdict:23,updatenormalsbutton:80,modulu:27,appreci:16,danger:[65,16],statu:[60,19,65,69,4,53,32,68,18,16],wire:[8,10,31],draw_elem_numb:18,correctli:[39,34,73,64,22,68,35],nnode:[6,50,57,73],pattern:[49,39,19,65,26,72,4,6,53,29,10,32,68,73,16],boundari:[14,6,40,12,33,27],favor:[32,65],state:[17,65,21,70,77,32,80],baseclass:29,pugin:42,progress:[6,54],neither:[38,65,74,29,9,32,34],updatebutton:80,email:[54,56,13,64],slect:29,tent:16,centr:10,qcolor:28,qt4:[29,56,68,64],ngrid:1,kei:[0,4,6,12,19,26,29,32,22,68,73,37,38,41,43,47,53,54,27,33,63,65,34,69,70,14,74,64,23],canvasmousehandl:68,"03d":42,isol:10,rotationanglesfrommatrix:9,job:[12,30],entir:65,entit:55,printal:4,disconnect:73,lawsuit:65,dsload:12,regular:[39,20,42,72,74,64,53,54,73,16],addit:[65,9,10,64,56,16],actual:[65,66,26,72,82,29,10,55,73,16],askfilenam:32,plugin:[39,42,64,2,27,7,8,78,77,11,48,56,34,18,16],swapax:[39,26,6,50,9,61,79,73],logfil:32,ramp:25,equal:[1,4,6,9,12,16,26,29,22,68,73,39,42,43,46,44,50,66,72,35,74,79],getfreeedgesmesh:[6,26],etc:[66,45,44,74,9,34],instanc:[0,70,39,18,1,24,43,4,26,6,7,29,27,64,12,61,22,68,73,16],equat:[12,39,9],freeli:42,includefil:53,afraid:16,r_1:27,comment:[22,21,4,29,10,23,33,34,27,16],bordernodenr:6,readnodesblock:33,triangular:[6,33],password:[54,65],obstruct:[8,10],chmod:54,pickgl:[1,15,31],solv:[54,42,9],empti:[1,4,6,12,14,23,21,26,29,32,22,18,37,38,39,50,53,27,33,66,35,69,71,73,74,77,79],cps3:27,mouse_draw_lin:68,respect:[39,65,66,1,42,71,47,69,9,10,48,73],distancefrompoint:[61,39,73],removetre:53,withdrawn:32,torsion:35,quit:[21,4,7,29,10,64,34,27,16],smoothlowpass:6,pin:27,tet10:[38,33],addition:[6,73,64],nnod:[6,39,26],quotat:[54,64],trademark:65,obstacl:64,abc:53,compon:[39,70,65,24,2,73,6,29,9,27,12,78,79,35,16],glutrendertext:71,besid:[29,34,10,16,14],treat:[39,65,69,14,29,53,82,27,16],certain:[6,7,64,65,82],writeelemoutput:12,immedi:[29,20],linkviewport:[32,82],ntri:[6,40],nctrlv:35,headerless:[],ps2gl:[],maxheight:29,hex16:38,nextfilenam:33,pid:53,defaultitemtyp:29,showhistogram:52,fmtsurfaceinteract:12,getcollect:45,addmenu:70,nodeadjac:[6,26],togeth:[26,82,6,29,64,53,56,34,73,16],m_2:27,withprop:[6,26,73],present:[70,65,21,69,42,4,29,10,32,27,16],hv10:71,replic:[39,8,10,16,73],getnod:[6,26],cypher:10,closegui:32,plain:[21,14,47,29,32,34],align:[39,69,2,26,6,50,61,79,73],twhi03:[],rectangular:[6,39,1,72,14,74,8,69,68,27,16],cursor:[68,29,19,69,54],defin:[0,1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,28,18,19,20,22,23,24,25,26,27,29,30,31,32,33,34,35,36,37,38,39,40,41,43,44,45,46,47,48,50,52,53,55,56,57,59,60,61,62,63,65,66,68,70,71,72,73,74,76,77,79,80,82],suport:[],killprocess:53,fall:[26,6,7,40,11,73],importdxf:2,timemark:12,volume_weight:6,observ:82,submesh:[6,26],homothet:[39,10],layer:[42,2],checkimageformat:74,cell:[38,26,72,6,10,16],rowstretch:68,almost:[39,14,66],site:[49,34,64],hv18:71,projectnam:53,archiv:64,nouselib:49,avi:42,substanti:65,maxval:7,highlightedg:32,forgetal:4,surprisingli:42,tosurfac:[6,26,73],readnod:[],tarbal:64,let:[27,64,69,18,40,32,82,68,73,16],welcom:65,insertitem:70,parti:[12,8,65],balloon:10,verheggh:[0,21,42,13,8,75,10,48,23,22,57,16],satisfi:[39,27,65],cross:[65,72,14,6,8,9,10,40,73,16],sqrt:[39,9],member:[32,60,73],handl:[39,63,20,66,24,53,32,29,12,79,56,68,55,16],verlaten:[37,70],makelib:54,availabal:38,see:[2,4,6,7,8,10,12,14,16,21,26,29,32,33,73,37,39,64,42,48,50,53,54,27,61,65,66,24,69,72,35,77,79,82],largest:[6,35,40,46],inch:44,wakeup:32,inc:65,deem:65,difficult:[64,10,16,27],innermost:16,incorrect:54,phi:39,workdir:[32,42],slave:12,olist:[41,56],cubic:[79,73,31],expans:[53,10],interrog:[53,16],upon:[69,35],effect:[39,65,66,26,24,6,12,23,22,73,16],columna:66,dsize:[61,39,73],cubiq:9,student:[54,8,64],pdf:[53,17,10,64],canva:[19,1,71,82,74,7,49,78,69,31,32,56,68,24,16],micro:10,php:64,expand:[39,26,53,10,11,12,35],cosmet:34,dmesg:54,retard:32,framescal:25,off:[6,19,42,26,72,24,74,53,10,32,54,79,22,68,18,16],center:[1,6,73,16,19,26,40,48,62,68,18,39,32,50,61,35,69,71,72,24,79,10],bboxactor:31,intpoints1:12,well:[0,64,65,21,39,69,2,4,18,74,8,9,27,40,11,12,54,68,35,16],versatil:[39,73],createmenudata:70,thought:73,monospac:32,exampl:[1,2,3,4,6,8,9,10,11,12,16,17,21,26,29,48,34,73,37,38,39,64,42,44,49,53,55,27,61,65,66,69,72,35,77,23,82],command:[4,6,78,10,11,12,16,19,30,68,77,42,44,49,53,54,55,65,34,69,64,82],readnodefil:33,choos:[38,65,69,26,73,6,64,18],undefin:[47,9,73,16,66],revis:[54,64,65,4,20],usual:[4,6,7,9,10,11,16,23,26,29,32,24,39,42,53,55,56,27,66,35,69,72,73,74,64,79,82],distanc:[39,19,69,26,24,6,10,40,79,82,68,73],newest:20,paus:[37,4,29,10,32,54],less:[39,1,82,26,4,6,69,64,79,33,34,73],savannah:[64,10,16],tet4:[38,39,26,6,33,16],glut:[56,71],nval:9,obtain:[39,64,50,27,69,2,26,73,6,7,8,9,10,40,53,54,66,35,16],interim:64,equivalenc:73,heavili:16,increasingli:8,skill:[29,8,69],simultan:[9,65],systemat:65,readarrai:9,web:[23,42],globalinterpolationcurv:35,rapid:8,trfmatrix:[39,9],omit:[37,33],trolltech:[3,64],hypothet:65,extrud:[6,46,26,73],resetopt:68,pgf:[61,20,21,26,50,79],removeal:[68,19],add:[0,1,6,9,10,12,34,16,23,19,20,26,29,32,33,80,18,37,38,39,42,47,53,54,55,27,58,60,61,22,63,65,68,69,70,73,28,79,14,82],cleanup:10,densiti:[12,55,27,14],lookup:[6,23,0,26],possess:65,adj:66,match:[0,1,4,6,7,9,14,16,26,27,29,40,32,22,68,18,37,38,39,43,44,50,53,57,66,70,73,82],directionalwidth:[61,39,73],intersectiontimeslwp:40,candid:64,royalti:65,all_image_extens:53,outnam:6,nvp:[32,68,82],triangleboundingcircl:40,libglu1:64,fovi:24,getpartit:45,assert:65,five:[],know:[0,65,69,75,64,54,27,16],knot:35,lc_all:53,press:[68,69,42,4,29,32,80],intersectiontimeslwt:40,"2nd":19,recurs:[32,37,41,31],isclos:9,insert:[39,70,66,1,42,15,71,35,47,9,53,23,22,73],rotz:[],like:[35,4,8,9,12,14,16,19,20,26,29,40,32,68,18,39,41,42,53,54,27,61,65,66,34,69,73,74,64,23,82],success:[54,79,39,64],safest:65,corpor:64,delaunai:33,roti:[],simplex:40,serverurl:13,necessari:[38,65,20,42,6,64,34,16],lose:79,resiz:[61,69,26,6,50,32,79,73],meridion:72,soft:12,page:[17,29,10,64,82,16],recurr:4,exceed:[54,64],drop:[37,29],intersect:[6,41,10,40,73],convei:65,specfi:79,kde:69,postproc:[7,56,25],postion:27,vessel:[8,10],bklormap:19,newviewport:49,"export":[27,42,20,21,67,45,2,4,5,26,6,69,64,12,54,56,61,18,16],superclass:[29,61],convey:65,flush:[68,19],proper:[61,69,2,29,32,54],home:[37,70,69,42,10,64,53,54,55,16],peter:75,mainli:[37,20,69,46,6,77,79,34],avgdir:39,tmp:42,view_angl:24,lead:[19,20,42,53,22,68],checkbord:6,avoid:[0,65,20,73,6,29,27],colorscal:[7,1,56],overlap:1,someinputwidget:29,estim:[39,9],leav:[19,42,46,6,64,12,54,34],createhistogram:52,record_error_handl:22,duplic:[39,66,26,6,22,27],leader:[32,15],lowpass:[6,26],drawfreeedg:32,sub_direct:79,inputpush:29,encourag:[32,42],investig:10,slight:8,imaginari:9,usag:[49,73],symlink:[54,64],paper:65,host:65,fmttransform:12,obei:[32,16,11],although:[8,65],offset:[39,26,27,6,32,12,33,73],area:[19,65,46,6,9,40,48,68,24,16],geometr:[2,4,6,8,10,16,21,25,26,40,32,73,39,31,46,49,50,55,56,27,61,66,67,69,72,14,82],microsecond:76,simpler:[69,73,16],loadproject:24,longestedg:6,about:[17,65,42,24,6,29,75,9,64,53,82,14,68,73,16],redraw:[32,68,19],rare:29,column:[66,42,26,6,29,9,68,82,16],freedom:[12,8,27,65],http:[65,21,8,10,64,55,33,16],setlinestippl:[68,1,19,15,31],ismanifold:6,highlightpartit:32,connectedel:6,formex_menu:[42,34],bridg:21,includ:[2,4,6,8,9,10,11,12,16,23,19,26,29,31,22,34,35,37,38,39,64,42,53,54,55,27,65,69,24,74,77,79],constructor:[37,39,19,14,61,27,53,32,29,10,12,79,56,18,16],fals:[35,1,4,6,9,12,14,16,23,19,20,24,26,29,31,32,62,68,18,37,38,39,40,41,44,45,46,52,53,27,60,22,66,34,70,73,74,76,79,80],embryon:17,disabl:[19,1,29,49,32,24],rendermod:[68,19],lc_number:53,orthog:9,flexibel:27,lenght:16,automat:[6,0,49,65,20,17,39,42,26,73,74,32,29,9,27,12,79,56,14,35,16],warranti:[65,8,10,16],readflavia:57,pointsatlin:40,inputfslid:29,central:[6,68,72,16],store_closest:68,mere:[19,65,68,26,6,64,55,34,16],merg:[6,65,50,26,16],inverseindex:[9,66],val:[28,63,24,70,26,73,6,7,29,9,32,15],pictur:[1,51,16,82],testexecutor:49,transfer:65,ugent:[21,42,10,54,13,16],cell2:10,spiral:[79,10,16],splitkeyvalu:22,bump:[39,26,6,50,10,54,79,61,73],much:[39,20,69,42,10,32,23,27,16],properi:[6,26,82],tracker:[42,8,64,20],scalex:39,replac:[37,23,0,70,19,14,39,42,26,18,6,32,29,50,9,64,12,61,79,22,73],getfilenam:29,object_typ:18,semicolon:[73,16,77,21],"function":[0,1,2,3,4,5,6,7,9,10,11,12,13,14,15,16,28,18,19,20,22,23,24,25,26,27,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,52,53,55,56,57,59,60,61,62,63,64,65,66,68,69,70,71,72,73,74,76,77,78,79,80,82],loadfil:37,tangibl:65,interspers:4,advers:65,keyerror:[12,0,19,23,14],dialogdutton:29,histogram:[9,52],"0x424daa0":[],neutral:6,bui:54,grati:65,eas:[17,64,16,82],highest:[38,26,6,9,64,73],bug:[71,64],prespecifi:34,nonplanar:[40,16],count:[39,22,68,73,16],minval:7,succe:[6,54,29,26,64],made:[38,65,20,69,13,10,64,54,34,73],flatdb:[22,14],helper:[49,66],nint:33,whether:[63,64,65,24,4,73,6,40,53,27],wish:[69,64,65,21],smooth:[19,26,24,6,31,35,16],unlock:24,troubl:[53,54,64],dof:27,record:[14,20,42,27,53,78,12,22,68,73],below:[37,38,39,78,65,66,6,9,53,54,27,16],viewangl:24,limit:[39,63,65,66,26,73,6,7,8,9,27,53,23,82,35],indefinit:4,find_first_nod:6,nnodeadjac:[6,26],savenext:74,otherwis:[6,39,65,20,1,24,74,29,69,73,16],problem:[20,65,42,6,54,55],reliabl:16,signet:73,what:[0,65,26,82,6,8,78,58,64,11,54,23,68,27,16],instead:[23,6,39,70,19,65,20,38,42,26,74,66,29,40,53,54,79,22,68,27],convertformextocurv:79,checkarray1d:9,evalu:[43,26,73,6,29,9,53,23,34,35],"int":[39,71,63,66,1,14,26,44,6,29,9,10,82,79,33,68,35],rather:[39,69,24,8,64,55,56,34,27,16],dure:[19,69,70,4,46,6,29,32,68,27,16],footpoint:40,inp:12,ndof:9,twist:[32,24,31],novemb:[],cap:72,replica:[10,73],circul:73,ini:23,mtime:[6,53],ind:9,computesect:14,upvec:9,hemispher:39,inb:1,probabl:[39,66,69,42,46,6,8,10,64,54,73],add_input:29,tick:29,oac:42,boot:54,detail:[39,65,50,1,26,82,6,32,8,69,10,64,12,54,79,61,73,16],virtual:[39,79,10,64],other:[0,35,1,4,6,8,9,10,12,14,16,23,20,21,26,29,32,68,18,37,38,39,42,46,81,50,53,54,56,27,65,34,69,72,73,28,64,78,79,82],bool:[1,9,40,66],futur:[6,32,26,65],rememb:[53,64,8,18,16],varieti:[56,42,16],mass:[39,62,55],most:[6,8,78,11,12,16,26,29,32,34,73,36,37,39,41,53,54,56,27,61,65,69,70,35,28,75,64,82],out:[0,19,65,68,69,2,82,6,32,29,8,64,12,54,34,24,16],connectedlineelem:66,replic2:[10,73],variabl:[1,4,9,10,12,16,20,21,29,32,22,34,18,37,42,49,27,69,14,74,78,23],stat:6,repeat:[39,19,66,80,4,26,6,27,79,68,73,16],star:72,changevalu:18,"class":[0,1,2,3,4,5,6,7,9,10,12,13,14,15,16,28,19,20,21,22,23,24,25,26,27,29,30,31,32,33,68,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,52,53,55,56,57,59,60,61,62,63,64,65,66,18,69,70,71,72,73,74,76,77,78,79,80,82],june:65,canvas:68,ndarrai:[39,26,73,6,29,9,12,14,35,16],subpoint:79,setalpha:31,log:[6,32],throughout:[38,39,35,16],wheelev:68,debian:[42,64],shown:[24,1,27,29,69,10,32,82,35,16],stai:[29,80],"3x3":[39,9,24],experienc:69,sphinx:75,eof:12,nrow:[32,29,68,82],qicon:70,propset:[6,26,73],quadrilater:[72,73,16],rule:[60,65,9,10,11,27,16],nnodeconnect:[6,26],approx_by_subdivis:79,image2glcolor:59,"0x4fe0140":[],matnam:[68,19],decemb:65,readdata:44},objtypes:{"0":"py:module","1":"py:method","2":"py:function","3":"py:class","4":"py:classmethod","5":"py:attribute"},titles:["55. mydict —","18. decors — 2D decorations for the OpenGL canvas.","48. dxf — Read/write geometry in DXF format.","27. imageViewer — A general image viewer","4. script — Basic pyFormex script functions","49. export — Classes and functions for exporting geometry in various formats.","32. trisurface — Operations on triangulated surfaces.","16. colorscale — Color mapping of a range of values.","Introduction to pyFormex","3. arraytools — A collection of numerical array utilities.","pyFormex example scripts","pyFormex plugins","42. fe_abq — Exporting finite element models in Abaqus\u2122 input file format.","60. sendmail — sendmail.py: a simple program to send an email message","40. properties — General framework for attributing properties to geometrical elements.","19. marks — OpenGL marks for annotating 3D actors.","pyFormex tutorial","pyFormex Documentation","52. objects — Selection of objects from the global dictionary.","21. canvas — This implements an OpenGL drawing widget for painting 3D scenes.","10. project — project.py","pyFormex file formats","59. flatkeydb — Flat Text File Database.","58. config — A general yet simple configuration class.","23. camera — OpenGL camera handling","44. postproc — Postprocessing functions","13. mesh — Finite element meshes in pyFormex.","Assigning properties to geometry","6. colors — Definition of some RGB colors and color conversion functions","14. widgets — A collection of custom widgets used in the pyFormex GUI","43. fe_post — A postprocessor for ABAQUS output files.","17. actors — OpenGL actors for populating the 3D scene.","5. draw — Create 3D graphical representations.","50. tetgen — Interface with tetgen","Configuring pyFormex","34. nurbs — Using NURBS in pyFormex.","25. imagearray — Convert bitmap images into numpy arrays.","28. scriptMenu — Menu with pyFormex scripts.","12. elements — Definition of elements.","1. coords — A structured collection of 3D coordinates.","33. geomtools — Basic geometrical operations.","54. olist — Some convenient shortcuts for common list operations.","pyFormex FAQ ‘n TRICKS","35. isopar — Isoparametric transformations","39. datareader — Numerical data reader","51. tools — tools.py","31. mesh_ext — Extended functionality of the Mesh class.","56. odict — Specialized dictionary type structures.","36. section2d — Some functions operating on 2D structures.","Running pyFormex","41. fe — Finite Element Models in pyFormex.","Creating Images","53. plot2d — plot2d.py","11. utils — A collection of miscellaneous utility functions.","BuMPix Live GNU/Linux system","38. units — A Python wrapper for unit conversion of physical quantities.","pyFormex reference manual","45. flavia —","Using Projects","26. imagecolor — Using bitmap images as colors.","46. lima — Lindenmayer Systems","7. geometry — A generic interface to the Coords transformation methods","37. inertia — inertia.py","57. collection — Tools for handling collections of elements belonging to multiple parts.","Installing pyFormex","GNU GENERAL PUBLIC LICENSE","8. connectivity — A class and functions for handling nodal connectivity.","Modeling Geometry","22. viewport — Interactive OpenGL Canvas embedded in a Qt4 widget.","The Graphical User Interface","15. menu — Menus for the pyFormex GUI.","20. gluttext — 2D text decorations using GLUT fonts","9. simple — Predefined geometries with a simple shape.","2. formex — Formex algebra in Python","24. image — Saving OpenGL renderings to image files.","About the pyFormex documentation","61. timer — A timer class.","47. turtle — Turtle graphics for pyFormex","pyFormex user guide","30. curve — Definition of curves in pyFormex.","29. toolbar — Toolbars for the pyFormex GUI.","Using Widgets","The Canvas"],objnames:{"0":["py","module","Python module"],"1":["py","method","Python method"],"2":["py","function","Python function"],"3":["py","class","Python class"],"4":["py","classmethod","Python class method"],"5":["py","attribute","Python attribute"]},filenames:["ref/mydict","ref/decors","ref/dxf","ref/imageViewer","ref/script","ref/export","ref/trisurface","ref/colorscale","intro","ref/arraytools","examples","plugins","ref/fe_abq","ref/sendmail","ref/properties","ref/marks","tutorial","index","ref/objects","ref/canvas","ref/project","file_format","ref/flatkeydb","ref/config","ref/camera","ref/postproc","ref/mesh","properties","ref/colors","ref/widgets","ref/fe_post","ref/actors","ref/draw","ref/tetgen","config","ref/nurbs","ref/imagearray","ref/scriptMenu","ref/elements","ref/coords","ref/geomtools","ref/olist","faq","ref/isopar","ref/datareader","ref/tools","ref/mesh_ext","ref/odict","ref/section2d","running","ref/fe","imaging","ref/plot2d","ref/utils","bumpix","ref/units","refman","ref/flavia","projects","ref/imagecolor","ref/lima","ref/geometry","ref/inertia","ref/collection","install","license","ref/connectivity","geometry","ref/viewport","gui","ref/menu","ref/gluttext","ref/simple","ref/formex","ref/image","meta","ref/timer","ref/turtle","user-guide","ref/curve","ref/toolbar","widgets","drawing"]})pyformex-0.8.6/pyformex/doc/html/drawing.html0000644000211500021150000004350611705104245021144 0ustar benebene00000000000000 The Canvas — pyFormex v0.8.6 documentation

Table Of Contents

Previous topic

Modeling Geometry

Next topic

Creating Images

[FSF Associate Member]

Valid XHTML 1.0 Transitional

The Canvas

Introduction

When you have created a nice and powerful script to generate a 3D structure, you will most likely want to visually inspect that you have indeed created that what you intended. Usually you even will want or need to see intermediate results before you can continue your development. For this purpose the GUI offers a canvas where structures can be drawn by functions called from a script and interactively be manipulated by menus options and toolbar buttons.

The 3D drawing and rendering functionality is based on OpenGL. Therefore you will need to have OpenGL available on your machine, either in hardware or software. Hardware accelerated OpenGL will of course speed up and ease operations.

The drawing canvas of actually is not a single canvas, but can be split up into multiple viewports. They can be used individually for drawing different items, but can also be linked together to show different views of the same scene. The details about using multiple viewports are described in section Multiple viewports. The remainder of this chapter will treat the canvas as if it was a single viewport.

distinguishes three types of items that can be drawn on the canvas: actors, marks and decorations. The most important class are the actors: these are 3D geometrical structures defined in the global world coordinates. The 3D scene formed by the actors is viewed by a camera from a certain position, with a certain orientation and lens. The result as viewed by the camera is shown on the canvas. The scripting language and the GUI provide ample means to move the camera and change the lens settings, allowing translation, rotation, zooming, changing perspective. All the user needs to do to get an actor displayed with the current camera settings, is to add that actor to the scene. There are different types of actors available, but the most important is the FormexActor: a graphical representation of a Formex. It is so important that there is a special function with lots of options to create a FormexActor and add it to the OpenGL scene. This function, draw(), will be explained in detail in the next section.

The second type of canvas items, marks, differ from the actors in that only their position in world coordinates is fixed, but not their orientation. Marks are always drawn in the same way, irrespective of the camera settings. The observer will always have the same view of the item, though it can (and will) move over the canvas when the camera is changed. Marks are primarily used to attach fixed attributes to certain points of the actors, e.g. a big dot, or a text dispaying some identification of the point.

Finally, offers decorations, which are items drawn in 2D viewport coordinates and unchangeably attached to the viewport. This can e.g. be used to display text or color legends on the view.

Drawing a Formex

The most important action performed on the canvas is the drawing of a Formex. This is accomplished with the draw() function. If you look at the reference page of the draw() function, the number of arguments looks frightening. However, most of these arguments have sensible default values, making the access to drawing functionality easy even for beginners. To display your created Formex F on the screen, a simple draw(F) will suffice in many cases.

If you draw several Formices with subsequent draw() commands, they will clutter the view. You can use the clear() instruction to wipe out the screen before drawing the next one. If you want to see them together in the same view, you can use different colors to differentiate. Color drawing is as easy as draw(F,color=’red’). The color specification can take various forms. It can be a single color or an array of colors or even an array of indices in a color table. In the latter case you use draw(F,color=indices,colormap=table) to draw the Formex. If multiple colors are specified, each elementof the Formex will be drawn with the corresponding color, and if the color array (or the color indices array) has less entries than the number of elements, it is wrapped around.

A single color entry can be specified by a string (‘red’) or by a triple of RGB values in the range 0.0..1.0 (e.g. red is (1.0,0.0,0.0)) or a triplet of integer values in the range 0..255 or a hexadecimal string (‘#FF0000’) or generally any of the values that can be converted by the colors.glColor() function to a triplet of RGB values.

If no color is specified and your Formex has no properties, will draw it with the current drawing color. If the Formex has properties, will use the properies as a color index into the specified color map or a (configurable) default color map.

There should be some examples here. Draw object(s) with specified settings and direct camera to it.

Viewing the scene

Once the Formex is drawn, you can manipulate it interactively using the mouse: you can rotate, translate and zoom with any of the methods decribed in Mouse interactions on the canvas. You should understand though that these methods do not change your Formex, but only how it is viewed by the observer.

Our drawing board is based on OpenGL. The whole OpenGL drawing/viewing process can best be understood by making the comparison with the set of a movie, in which actors appear in a 3D scene, and a camera that creates a 2D image by looking at the scene with a certain lens from some angle and distance. Drawing a Formex then is nothing more than making an actor appear on the scene. The OpenGL machine will render it according to the current camera settings.

Viewing transformations using the mouse will only affect the camera, but not the scene. Thus, if you move the Formex by sliding your mouse with button 3 depressed to the right, the Formex will look like it is moving to the right, though it is actually not: we simply move the camera in the opposite direction. Therefore in perspective mode, you will notice that moving the scene will not just translate the picture: its shape will change too, because of the changing perspective.

Using a camera, there are two ways of zooming: either by changing the focal length of the lens (lens zooming) or by moving the camera towards or away from the scene (dolly zooming). The first one will change the perspective view of the scene, while the second one will not.

The easiest way to set all camera parameters for properly viewing a scene is by justing telling the direction from which you want to look, and let the program determine the rest of the settings itself. even goes a step further and has a number of built in directions readily available: ‘top’, ‘bottom’, ‘left’, ‘right’, ‘front’, ‘back’ will set up the camera looking from that direction.

Other canvas items

Actors

Marks

Decorations

Multiple viewports

Drawing in is not limited to a single canvas. You can create any number of canvas widgets laid out in an array with given number of rows or columns. The following functions are available for manipulating the viewports.

layout(nvps=None, ncols=None, nrows=None)

Set the viewports layout. You can specify the number of viewports and the number of columns or rows.

If a number of viewports is given, viewports will be added or removed to match the number requested. By default they are layed out rowwise over two columns.

If ncols is an int, viewports are laid out rowwise over ncols columns and nrows is ignored. If ncols is None and nrows is an int, viewports are laid out columnwise over nrows rows.

addViewport()

Add a new viewport.

removeViewport()

Remove the last viewport.

linkViewport(vp, tovp)

Link viewport vp to viewport tovp.

Both vp and tovp should be numbers of viewports. The viewport vp will now show the same contents as the viewport tovp.

viewport(n)

Select the current viewport. All drawing related functions will henceforth operate on that viewport.

This action is also implicitly called by clicking with the mouse inside a viewport.

pyformex-0.8.6/pyformex/doc/html/search.html0000644000211500021150000001342011705104257020751 0ustar benebene00000000000000 Search — pyFormex v0.8.6 documentation

[FSF Associate Member]

Valid XHTML 1.0 Transitional

Search

Please activate JavaScript to enable the search functionality.

From here you can search these documents. Enter your search words into the box below and click "search". Note that the search function will automatically search for all of the words. Pages containing fewer words won't appear in the result list.

pyformex-0.8.6/pyformex/doc/html/py-modindex.html0000644000211500021150000005063711705104257021754 0ustar benebene00000000000000 Python Module Index — pyFormex v0.8.6 documentation

[FSF Associate Member]

Valid XHTML 1.0 Transitional

Python Module Index

a | c | d | e | f | g | i | l | m | n | o | p | s | t | u | v | w
 
a
actors OpenGL actors for populating the 3D scene.
arraytools A collection of numerical array utilities.
 
c
camera OpenGL camera handling
canvas This implements an OpenGL drawing widget for painting 3D scenes.
collection Tools for handling collections of elements belonging to multiple parts.
colors Definition of some RGB colors and color conversion functions
colorscale Color mapping of a range of values.
config A general yet simple configuration class.
connectivity A class and functions for handling nodal connectivity.
coords A structured collection of 3D coordinates.
curve Definition of curves in pyFormex.
 
d
datareader Numerical data reader
decors 2D decorations for the OpenGL canvas.
draw Create 3D graphical representations.
dxf Read/write geometry in DXF format.
 
e
elements Definition of elements.
export Classes and functions for exporting geometry in various formats.
 
f
fe Finite Element Models in pyFormex.
fe_abq Exporting finite element models in Abaqus\ |trade| input file format.
fe_post A postprocessor for ABAQUS output files.
flatkeydb Flat Text File Database.
flavia
formex Formex algebra in Python
 
g
geometry A generic interface to the Coords transformation methods
geomtools Basic geometrical operations.
gluttext 2D text decorations using GLUT fonts
 
i
image Saving OpenGL renderings to image files.
imagearray Convert bitmap images into numpy arrays.
imagecolor Using bitmap images as colors.
imageViewer A general image viewer
inertia inertia.py
isopar Isoparametric transformations
 
l
lima Lindenmayer Systems
 
m
marks OpenGL marks for annotating 3D actors.
menu Menus for the pyFormex GUI.
mesh Finite element meshes in pyFormex.
mesh_ext Extended functionality of the Mesh class.
mydict
 
n
nurbs Using NURBS in pyFormex.
 
o
objects Selection of objects from the global dictionary.
odict Specialized dictionary type structures.
olist Some convenient shortcuts for common list operations.
 
p
plot2d plot2d.py
postproc Postprocessing functions
project project.py
properties General framework for attributing properties to geometrical elements.
 
s
script Basic pyFormex script functions
scriptMenu Menu with pyFormex scripts.
section2d Some functions operating on 2D structures.
sendmail sendmail.py: a simple program to send an email message
simple Predefined geometries with a simple shape.
 
t
tetgen Interface with tetgen
timer A timer class.
toolbar Toolbars for the pyFormex GUI.
tools tools.py
trisurface Operations on triangulated surfaces.
turtle Turtle graphics for pyFormex
 
u
units A Python wrapper for unit conversion of physical quantities.
utils A collection of miscellaneous utility functions.
 
v
viewport Interactive OpenGL Canvas embedded in a Qt4 widget.
 
w
widgets A collection of custom widgets used in the pyFormex GUI
pyformex-0.8.6/pyformex/doc/html/intro.html0000644000211500021150000004356511705104246020652 0ustar benebene00000000000000 Introduction to pyFormex — pyFormex v0.8.6 documentation

Table Of Contents

Previous topic

pyFormex Documentation

Next topic

Installing pyFormex

[FSF Associate Member]

Valid XHTML 1.0 Transitional

Introduction to pyFormex

Abstract

This part explains shortly what pyFormex is and what it is not. It sets the conditions under which you are allowed to use, modify and distribute the program. Next is a list of prerequisite software parts that you need to have installed in order to be able to run this program. We explain how to download and install pyFormex. Finally, you’ll find out what basic knowledge you should have in order to understand the tutorial and succesfully use pyFormex.

What is pyFormex?

You probably expect to find here a short definition of what pyFormex is and what it can do for you. I may have to disappoint you: describing the essence of pyFormex in a few lines is not easy to do, because the program can be (and is being) used for very different tasks. So I will give you two answers here: a short one and a long one.

The short answer is that pyFormex is a program to generate large structured sets of coordinates by means of subsequent mathematical transformations gathered in a script. If you find this definition too dull, incomprehensible or just not descriptive enough, read on through this section and look at some of the examples in this documentation and on the pyFormex website. You will then probably have a better idea of what pyFormex is.

The initial intent of pyFormex was the rapid design of three-dimensional structures with a geometry that can easier be obtained through mathematical description than through interactive generation of its subparts and assemblage thereof. Although the initial development of the program concentrated mostly on wireframe type structures, surface and solid elements have been part of pyFormex right from the beginning. There is already an extensive plugin for working with triangulated surfaces, and pyFormex is increasingly being used to generate solid meshes of structures. Still, many of the examples included with the pyFormex distribution are of wireframe type, and so are most of the examples in the pyFormex tutorial.

A good illustration of what pyFormex can do and what it was intended for is the stent [1] structure in the figure WireStent example. It is one of the many examples provided with pyFormex.

A wire stent

WireStent example

The structure is composed of 22032 line segments, each defined by 2 points. Nobody in his right mind would ever even try to input all the 132192 coordinates of all the points describing that structure. With pyFormex, one could define the structure by the following sequence of operations, illustrated in the figure First three steps in building the WireStent example:

  1. Create a nearly planar base module of two crossing wires. The wires have a slight out-of-plane bend, to enable the crossing.
  2. Extend the base module with a mirrored and translated copy.
  3. Replicate the base module in both directions to create a (nearly planar) rectangular grid.
  4. Roll the planar grid into a cylinder.

pyFormex provides all the operations needed to define the geometry in this way.

A wire stent

First three steps in building the WireStent example

pyFormex does not fit into a single category of traditional (mostly commercial) software packages, because it is not being developed as a program with a specific purpose, but rather as a collection of tools and scripts which we needed at some point in our research projects. Many of the tasks for which we now use pyFormex could be done also with some other software package, like a CAD program or a matrix calculation package or a solid modeler/renderer or a finite element pre- and postprocessor. Each of these is probably very well suited for the task it was designed for, but none provides all the features of pyFormex in a single consistent environment, and certainly not as free software.

Perhaps the most important feature of pyFormex is that it was primarily intended to be an easy scripting language for creating geometrical models of 3D-structures. The graphical user interface (GUI) was only added as a convenient means to visualize the designed structure. pyFormex can still run without user interface, and this makes it ideal for use in a batch toolchain. Anybody involved in the simulation of the mechanical behavior of materials and structures will testify that most of the work (often 80-90%) goes into the building of the model, not into the simulations itself. Repeatedly building a model for optimization of your structure quickly becomes cumbersome, unless you use a tool like pyFormex, allowing for automated and unattended building of model variants.

The author of pyFormex, professor in structural engineering and heavy computer user and programmer since mainframe times, deeply regrets that computing skills of nowadays engineering students are often limited to using graphical interfaces of mostly commercial packages. This greatly limits their skills, because in their way of thinking: ‘If there is no menu item to do some task, then it can not be done!’ The hope to get some of them back into coding has been a stimulus in continuing our work on pyFormex. The strength of the scripting language and the elegance of Python have already attracted many users on this path.

Finally, pyFormex is, and always will be, free software in both meanings of free: guaranteeing the freedom of the user (see License and Disclaimer) and without charging a fee for it. [2]

License and Disclaimer

pyFormex is ©2004-2011 Benedict Verhegghe

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GNU GPL), as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

The full details of the GNU GPL are available in the GNU GENERAL PUBLIC LICENSE part of the documentation, in the file COPYING included with the distribution, under the Help->License item of the pyFormex Graphical User Interface or from http://www.gnu.org/copyleft/gpl.html.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

Installation

Information on how to obtain and install pyFormex can be found in the Installing pyFormex document.

Using pyFormex

Once you have installed and want to start using pyFormex, you will probably be looking for help on how to do it.

If you are new to pyFormex, you should start with the pyFormex tutorial, which will guide you step by step, using short examples, through the basic concepts of Python, NumPy and pyFormex. You have to understand there is a lot to learn at first, but afterwards the rewards will prove to be huge. You can skip the sections on Python and NumPy if you already have some experience with it.

If you have used pyFormex before or you are of the adventurous type that does not want to be told where to go and how to do it, skip the tutorial and go directly to the pyFormex user guide. It provides the most thorough information of all aspects of pyFormex.

Getting Help

If you get stuck somewhere with using (or installing) pyFormex and you need help, the best way is to go to the pyFormex website and ask for help via the Support tracker. There’s a good change you will get helped quickly there. Remember though that pyFormex is a free and open source software project and its developers are not paid to develop or maintain pyFormex, they just do this because they find pyFormex very helpful in their normal daily activities.

If you are a professional pyFormex user and require guaranteed support, you can check with FEops, a young company providing services with and support for pyFormex. [2]

Footnotes

[1]A stent is a tubular structure that is e.g. used to reopen (and keep open) obstructed blood vessels.
[2]Third parties may offer pyFormex extensions and/or professional support that are fee-based.
pyformex-0.8.6/pyformex/doc/html/genindex.html0000644000211500021150000105034311705104257021313 0ustar benebene00000000000000 Index — pyFormex v0.8.6 documentation

[FSF Associate Member]

Valid XHTML 1.0 Transitional

Index

A | B | C | D | E | F | G | H | I | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

A

AbqData (class in fe_abq)
abqInputNames() (in module fe_abq)
accept_draw() (viewport.QtCanvas method)
accept_drawing() (viewport.QtCanvas method)
accept_selection() (viewport.QtCanvas method)
acceptData() (widgets.InputDialog method)
(widgets.ListSelection method)
ack() (in module draw)
(in module script)
action() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
ActionList (class in menu)
actionList() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
Actor (class in actors)
actor() (nurbs.Coords4 method)
(nurbs.NurbsCurve method)
(nurbs.NurbsSurface method)
actors (module)
add() (collection.Collection method)
(menu.ActionList method)
(scriptMenu.ScriptMenu method)
Add() (units.UnitsSystem method)
add_group() (widgets.InputDialog method)
(widgets.ListSelection method)
add_input() (widgets.InputDialog method)
(widgets.ListSelection method)
add_items() (widgets.InputDialog method)
(widgets.ListSelection method)
add_tab() (widgets.InputDialog method)
(widgets.ListSelection method)
addActionButtons() (in module toolbar)
addActor() (canvas.Canvas method)
(viewport.QtCanvas method)
addAnnotation() (canvas.Canvas method)
(viewport.QtCanvas method)
addAxis() (in module arraytools)
addButton() (in module toolbar)
addCameraButtons() (in module toolbar)
addCheck() (widgets.MessageBox method)
addDecoration() (canvas.Canvas method)
(viewport.QtCanvas method)
addElem() (in module tetgen)
addHighlight() (canvas.Canvas method)
(viewport.QtCanvas method)
addMaterial() (properties.ElemSection method)
addMeanNodes() (mesh.Mesh method)
(trisurface.TriSurface method)
addNodes() (mesh.Mesh method)
(trisurface.TriSurface method)
addNoise() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
addRule() (lima.Lima method)
addSection() (properties.ElemSection method)
addTimeOut() (in module widgets)
addTimeoutButton() (in module toolbar)
addView() (viewport.MultiCanvas method)
(viewport.NewMultiCanvas method)
addViewport() (built-in function)
(in module draw)
adjacency() (connectivity.Connectivity method)
adjacencyArrays() (in module connectivity)
affine() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
align() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
all_image_extensions() (in module utils)
alt_report() (in module mesh_ext)
Amplitude (class in properties)
an() (in module turtle)
annotate() (in module draw)
anyPerpendicularVector() (in module geomtools)
anyVector() (in module arraytools)
append() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Line method)
(curve.PolyLine method)
(flatkeydb.FlatDB method)
(formex.Formex method)
(objects.DrawableObjects method)
(objects.Objects method)
(trisurface.TriSurface method)
approx() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
approx_by_subdivision() (curve.BezierSpline method)
(curve.CardinalSpline method)
Arc (class in curve)
Arc3 (class in curve)
arccosd() (in module arraytools)
arcsind() (in module arraytools)
arctand() (in module arraytools)
arctand2() (in module arraytools)
area() (in module mesh_ext)
(trisurface.TriSurface method)
areaNormals() (in module geomtools)
(trisurface.TriSurface method)
areas() (in module mesh_ext)
(trisurface.TriSurface method)
argNearestValue() (in module arraytools)
ArrayModel (class in widgets)
arraytools (module)
asArray() (formex.Formex method)
asFormex() (formex.Formex method)
asFormexWithProp() (formex.Formex method)
ask() (in module draw)
(in module script)
(objects.DrawableObjects method)
(objects.Objects method)
ask1() (objects.DrawableObjects method)
(objects.Objects method)
askDirname() (in module draw)
askDrawOptions() (in module draw)
askFilename() (in module draw)
askItems() (in module draw)
askNewFilename() (in module draw)
aspectRatio() (trisurface.TriSurface method)
atLength() (curve.Line method)
(curve.PolyLine method)
autorun
autoSaveOn() (in module image)
average() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
avgDirections() (curve.Line method)
(curve.PolyLine method)
avgNodes() (mesh.Mesh method)
(trisurface.TriSurface method)
avgVertexNormals() (trisurface.TriSurface method)
axes() (coords.CoordinateSystem method)
AxesActor (class in actors)
AxesMark (class in marks)

B

baryCoords() (in module geomtools)
BaseMenu (class in menu)
bbox() (actors.Actor method)
(coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(in module coords)
(nurbs.Coords4 method)
(nurbs.NurbsCurve method)
(nurbs.NurbsSurface method)
BboxActor (class in actors)
begin_2D_drawing() (canvas.Canvas method)
(viewport.QtCanvas method)
BezierSpline (class in curve)
bgcolor() (in module draw)
boolean() (trisurface.TriSurface method)
border() (trisurface.TriSurface method)
borderEdgeNrs() (trisurface.TriSurface method)
borderEdges() (trisurface.TriSurface method)
borderNodeNrs() (trisurface.TriSurface method)
BoundVectors (class in coords)
breakpt() (in module script)
bsphere() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
bump() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
bump1() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
bump2() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
ButtonBox (class in widgets)

C

Camera (class in camera)
camera (module)
cancel_draw() (viewport.QtCanvas method)
cancel_drawing() (viewport.QtCanvas method)
cancel_selection() (viewport.QtCanvas method)
Canvas (class in canvas)
canvas (module)
CanvasMouseHandler (class in viewport)
CanvasSettings (class in canvas)
canvasSize() (in module draw)
CardinalSpline (class in curve)
CardinalSpline2 (class in curve)
cascade() (in module mydict)
cclip() (formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
CDict (class in mydict)
center() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(in module inertia)
centered() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
centroid() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
centroids() (formex.Formex method)
(in module inertia)
(mesh.Mesh method)
(trisurface.TriSurface method)
changeExt() (in module utils)
changeLayout() (viewport.MultiCanvas method)
(viewport.NewMultiCanvas method)
changeValues() (objects.DrawableObjects method)
(objects.Objects method)
chdir() (in module script)
check() (objects.DrawableObjects method)
(objects.Objects method)
(trisurface.TriSurface method)
checkArray() (in module arraytools)
checkArray1D() (in module arraytools)
checkArrayDim() (in module arraytools)
checkArrayOrIdValue() (in module properties)
checkBorder() (trisurface.TriSurface method)
checkDict() (canvas.CanvasSettings class method)
checkExternal() (in module utils)
checkIdValue() (in module properties)
checkImageFormat() (in module image)
checkKeys() (flatkeydb.FlatDB method)
checkModule() (in module utils)
checkRecord() (flatkeydb.FlatDB method)
checkRevision() (in module script)
checkSelfIntersectionsWithTetgen() (trisurface.TriSurface method)
checkString() (in module properties)
checkUniqueNumbers() (in module arraytools)
checkVersion() (in module utils)
checkWorkdir() (in module draw)
circle() (in module curve)
(in module simple)
circulize() (formex.Formex method)
circulize1() (formex.Formex method)
classify() (scriptMenu.ScriptMenu method)
clear() (canvas.Canvas method)
(in module draw)
(objects.DrawableObjects method)
(objects.Objects method)
(viewport.QtCanvas method)
clear_canvas() (in module draw)
clip() (formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
clipAtPlane() (mesh.Mesh method)
(trisurface.TriSurface method)
close() (curve.Line method)
(curve.PolyLine method)
(dxf.DxfExporter method)
closeDialog() (in module draw)
closeGui() (in module draw)
closestColorName() (in module colors)
closestToPoint() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
coarsen() (trisurface.TriSurface method)
Collection (class in collection)
collection (module)
collectOnLength() (in module olist)
color() (colorscale.ColorLegend method)
(colorscale.ColorScale method)
ColorLegend (class in colorscale)
(class in decors)
colormap() (in module draw)
colorName() (in module colors)
colors (module)
ColorScale (class in colorscale)
colorscale (module)
combine() (connectivity.Connectivity method)
compact() (mesh.Mesh method)
(trisurface.TriSurface method)
compatInputItem() (in module widgets)
computeSection() (properties.ElemSection method)
concatenate() (coords.BoundVectors class method)
(coords.CoordinateSystem class method)
(coords.Coords class method)
(formex.Formex class method)
(in module olist)
(mesh.Mesh class method)
(trisurface.TriSurface class method)
Config (class in config)
config (module)
connect() (in module formex)
(mesh.Mesh method)
(trisurface.TriSurface method)
connectCurves() (in module simple)
connectedElements() (trisurface.TriSurface method)
connectedLineElems() (in module connectivity)
Connectivity (class in connectivity)
connectivity (module)
convert() (mesh.Mesh method)
(project.Project method)
(trisurface.TriSurface method)
convertDXF() (in module dxf)
convertFormexToCurve() (in module curve)
convertInputItem() (in module widgets)
convertRandom() (mesh.Mesh method)
(trisurface.TriSurface method)
convertUnits() (in module units)
coord() (formex.Formex method)
CoordinateSystem (class in coords)
CoordPlaneActor (class in actors)
Coords (class in coords)
coords (module)
Coords4 (class in nurbs)
CoordsBox (class in widgets)
CoordSystem (class in properties)
copy() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
correctNegativeVolumes() (in module mesh_ext)
(trisurface.TriSurface method)
cosd() (in module arraytools)
(in module turtle)
countLines() (in module utils)
create_insert_action() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
createBackground() (canvas.Canvas method)
(viewport.QtCanvas method)
createFeResult() (in module flavia)
createHistogram() (in module plot2d)
createMenuData() (in module menu)
createMovie() (in module image)
createScriptMenu() (in module scriptMenu)
createView() (in module draw)
(viewport.NewMultiCanvas method)
cselect() (formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
Cube() (in module trisurface)
CubeActor (class in actors)
cubicEquation() (in module arraytools)
cuboid() (in module simple)
currentDialog() (in module draw)
CursorShapeHandler (class in viewport)
curvature() (in module trisurface)
(trisurface.TriSurface method)
Curve (class in curve)
curve (module)
curveToNurbs() (in module nurbs)
cut2AtPlane() (in module formex)
cut3AtPlane() (in module formex)
cutElements3AtPlane() (in module formex)
cutWithPlane() (curve.Line method)
(curve.PolyLine method)
(formex.Formex method)
(trisurface.TriSurface method)
cylinder() (in module simple)
cylindrical() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)

D

DAction (class in menu)
Database (class in properties)
datareader (module)
deCasteljou() (in module nurbs)
decompose() (nurbs.NurbsCurve method)
decorate() (in module draw)
Decoration (class in decors)
decors (module)
defaultItemType() (in module widgets)
degenerate() (in module trisurface)
delay() (in module draw)
delete() (project.Project method)
delProp() (objects.DrawableObjects method)
(properties.PropertyDB method)
deNormalize() (nurbs.Coords4 method)
derivatives() (nurbs.NurbsCurve method)
dialogAccepted() (in module draw)
dialogButtons() (in module widgets)
dialogRejected() (in module draw)
dialogTimedOut() (in module draw)
Dict (class in mydict)
difference() (in module olist)
directionalExtremes() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
directionalSize() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
directionalWidth() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
directions() (curve.Line method)
(curve.PolyLine method)
directionsAt() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
displaceLines() (in module geomtools)
display() (canvas.Canvas method)
(viewport.QtCanvas method)
distanceFromLine() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
distanceFromPlane() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
distanceFromPoint() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
distanceOfPoints() (trisurface.TriSurface method)
distancesPFL() (in module geomtools)
distancesPFS() (in module geomtools)
divide() (formex.Formex method)
do_lighting() (canvas.Canvas method)
(viewport.QtCanvas method)
doFunc() (widgets.InputButton method)
dolly() (camera.Camera method)
dotpr() (in module arraytools)
(in module viewport)
draw (module)
draw() (in module draw)
draw_bbox() (in module objects)
draw_cursor() (canvas.Canvas method)
(viewport.QtCanvas method)
draw_elem_numbers() (in module objects)
draw_focus_rectangle() (canvas.Canvas method)
(viewport.QtCanvas method)
draw_free_edges() (in module objects)
draw_node_numbers() (in module objects)
draw_nodes() (in module objects)
draw_object_name() (in module objects)
draw_state_line() (viewport.QtCanvas method)
draw_state_rect() (viewport.QtCanvas method)
drawable() (in module draw)
DrawableObjects (class in objects)
drawActor() (in module draw)
drawAnnotation() (objects.DrawableObjects method)
drawAxes() (in module draw)
drawBbox() (in module draw)
drawChanges() (objects.DrawableObjects method)
drawDot() (in module decors)
drawFreeEdges() (in module draw)
drawGL() (actors.Actor method)
(actors.AxesActor method)
(actors.BboxActor method)
(actors.CoordPlaneActor method)
(actors.CubeActor method)
(actors.GeomActor method)
(actors.GridActor method)
(actors.PlaneActor method)
(actors.SphereActor method)
(decors.Decoration method)
(decors.GlutText method)
(decors.Triade method)
(marks.Mark method)
drawGrid() (in module decors)
drawImage() (in module draw)
drawLine() (in module decors)
drawLinesInter() (in module draw)
(viewport.QtCanvas method)
drawMarks() (in module draw)
drawNumbers() (in module draw)
drawpick() (marks.MarkList method)
drawPropNumbers() (in module draw)
drawRect() (in module decors)
drawRectangle() (in module decors)
drawText() (in module draw)
drawText3D() (in module draw)
drawVectors() (in module draw)
drawVertexNumbers() (in module draw)
drawViewportAxes3D() (in module draw)
dsize() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
dxf (module)
DxfExporter (class in dxf)
dynapan() (viewport.QtCanvas method)
dynarot() (viewport.QtCanvas method)
dynazoom() (viewport.QtCanvas method)

E

edgeAdjacency() (mesh.Mesh method)
(trisurface.TriSurface method)
edgeAngles() (trisurface.TriSurface method)
edgeConnections() (mesh.Mesh method)
(trisurface.TriSurface method)
edgeCosAngles() (trisurface.TriSurface method)
edgeDistance() (in module geomtools)
edgeFront() (trisurface.TriSurface method)
EdgeLoad (class in properties)
edit_drawing() (viewport.QtCanvas method)
egg() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
elbbox() (formex.Formex method)
element
Element (class in elements)
element() (formex.Formex method)
element2str() (formex.Formex class method)
elements (module)
elementType() (in module elements)
elementTypes() (in module elements)
ElemLoad (class in properties)
elemNrs() (fe.Model method)
elemProp() (properties.PropertyDB method)
ElemSection (class in properties)
emit_cancel() (viewport.QtCanvas method)
emit_done() (viewport.QtCanvas method)
end_2D_drawing() (canvas.Canvas method)
(viewport.QtCanvas method)
endPoints() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
endSection() (dxf.DxfExporter method)
Engineering() (units.UnitsSystem method)
entities() (dxf.DxfExporter method)
error() (in module draw)
(in module script)
esetName() (in module fe_abq)
evaluate() (in module isopar)
executeScript() (in module script)
exit() (in module script)
export (module)
export() (in module script)
export2() (in module script)
exportDXF() (in module dxf)
exportMesh() (in module fe_abq)
exportObjects() (in module tools)
extend() (curve.BezierSpline method)
(curve.CardinalSpline method)
extendedSectionChar() (in module section2d)
extractKeyword() (in module scriptMenu)
extrude() (formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)

F

faceDistance() (in module geomtools)
facetArea() (trisurface.TriSurface method)
fd() (in module turtle)
fe (module)
fe_abq (module)
fe_post (module)
fforward() (in module draw)
fgcolor() (in module draw)
fileDescription() (in module utils)
fileName() (scriptMenu.ScriptMenu method)
FileSelection (class in widgets)
fileType() (in module utils)
fileTypeFromExt() (in module utils)
fillBorder() (in module trisurface)
(trisurface.TriSurface method)
filterFiles() (scriptMenu.ScriptMenu method)
find_first_nodes() (in module trisurface)
find_global() (in module project)
find_nodes() (in module trisurface)
find_row() (in module trisurface)
find_triangles() (in module trisurface)
findConnectedLineElems() (in module connectivity)
findIcon() (in module utils)
FindListItem() (in module properties)
finish_draw() (viewport.QtCanvas method)
finish_drawing() (viewport.QtCanvas method)
finish_selection() (viewport.QtCanvas method)
firstWord() (in module flatkeydb)
fixNormals() (trisurface.TriSurface method)
flags() (widgets.ArrayModel method)
(widgets.TableModel method)
flare() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
FlatDB (class in flatkeydb)
flatkeydb (module)
flatten() (in module draw)
(in module olist)
flavia (module)
flyAlong() (in module draw)
fmt() (fe_abq.Output method)
fmtAnalyticalSurface() (in module fe_abq)
fmtBeamSection() (in module fe_abq)
fmtCmd() (in module fe_abq)
fmtConnectorBehavior() (in module fe_abq)
fmtConnectorSection() (in module fe_abq)
fmtConstraint() (in module fe_abq)
fmtContactPair() (in module fe_abq)
fmtData() (in module fe_abq)
fmtData1D() (in module fe_abq)
fmtEquation() (in module fe_abq)
fmtFrameSection() (in module fe_abq)
fmtGeneralBeamSection() (in module fe_abq)
fmtGeneralContact() (in module fe_abq)
fmtHeading() (in module fe_abq)
fmtInitialConditions() (in module fe_abq)
fmtMaterial() (in module fe_abq)
fmtOptions() (in module fe_abq)
fmtOrientation() (in module fe_abq)
fmtPart() (in module fe_abq)
fmtSurface() (in module fe_abq)
fmtSurfaceInteraction() (in module fe_abq)
fmtTransform() (in module fe_abq)
focus() (in module draw)
forget() (in module script)
(objects.DrawableObjects method)
(objects.Objects method)
forgetAll() (in module script)
formatDict() (in module config)
Formex
(class in formex)
formex (module)
fprint() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
FramedGridLayout (class in viewport)
frameScale() (in module postproc)
frenet() (in module nurbs)
fromfile() (coords.BoundVectors class method)
(coords.CoordinateSystem class method)
(coords.Coords class method)
(formex.Formex class method)
fromstring() (coords.BoundVectors class method)
(coords.CoordinateSystem class method)
(coords.Coords class method)
(formex.Formex class method)
fuse() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)

G

GenericDialog (class in widgets)
GeomActor (class in actors)
Geometry (class in geometry)
geometry (module)
geomtools (module)
get() (camera.ViewAngles method)
(canvas.CanvasSettings method)
(collection.Collection method)
(config.Config method)
(fe_abq.Interaction method)
(fe_abq.Output method)
(fe_abq.Result method)
(mydict.CDict method)
(mydict.Dict method)
(properties.Database method)
(properties.EdgeLoad method)
(properties.ElemLoad method)
(properties.ElemSection method)
(properties.MaterialDB method)
(properties.PropertyDB method)
(properties.SectionDB method)
Get() (units.UnitsSystem method)
getBorder() (mesh.Mesh method)
(trisurface.TriSurface method)
getBorderMesh() (mesh.Mesh method)
(trisurface.TriSurface method)
getCells() (mesh.Mesh method)
(trisurface.TriSurface method)
getCenter() (camera.Camera method)
getcfg() (in module script)
getCollection() (in module tools)
getColor() (in module widgets)
getCoords() (mesh.Mesh method)
(trisurface.TriSurface method)
getDist() (camera.Camera method)
getEdges() (mesh.Mesh method)
(trisurface.TriSurface method)
getElemEdges() (mesh.Mesh method)
(trisurface.TriSurface method)
getElems() (fe.Model method)
(mesh.Mesh method)
(trisurface.TriSurface method)
getEntities() (elements.Element method)
getFaces() (mesh.Mesh method)
(trisurface.TriSurface method)
getFilename() (widgets.FileSelection method)
(widgets.ProjectSelection method)
(widgets.SaveImageDialog method)
getFiles() (scriptMenu.ScriptMenu method)
getFreeEdgesMesh() (mesh.Mesh method)
(trisurface.TriSurface method)
getFreeEntities() (mesh.Mesh method)
(trisurface.TriSurface method)
getFreeEntitiesMesh() (mesh.Mesh method)
(trisurface.TriSurface method)
getInts() (in module tetgen)
getLowerEntities() (mesh.Mesh method)
(trisurface.TriSurface method)
getMouseFunc() (viewport.CanvasMouseHandler method)
(viewport.QtCanvas method)
getNodes() (mesh.Mesh method)
(trisurface.TriSurface method)
getObjectItems() (in module tools)
getPartition() (in module tools)
getPoints() (mesh.Mesh method)
(trisurface.TriSurface method)
getProp() (formex.Formex method)
(mesh.Mesh method)
(properties.PropertyDB method)
(trisurface.TriSurface method)
getResult() (widgets.InputDialog method)
(widgets.ListSelection method)
(widgets.MessageBox method)
getResults() (widgets.InputDialog method)
(widgets.ListSelection method)
getRot() (camera.Camera method)
getValues() (widgets.CoordsBox method)
gl_pickbuffer() (in module canvas)
GLColor() (in module colors)
glEnable() (in module canvas)
glFlat() (in module canvas)
glinit() (canvas.Canvas method)
(viewport.QtCanvas method)
glLineStipple() (in module canvas)
glob() (utils.NameSequence method)
globalInterpolationCurve() (in module nurbs)
Globals() (in module script)
glSmooth() (in module canvas)
glupdate() (canvas.Canvas method)
(viewport.QtCanvas method)
glutBitmapLength() (in module gluttext)
glutDrawText() (in module gluttext)
glutFont() (in module gluttext)
glutFontHeight() (in module gluttext)
glutRenderText() (in module gluttext)
glutSelectFont() (in module gluttext)
GlutText (class in decors)
gluttext (module)
go() (in module turtle)
gray2qimage() (in module imagearray)
grepSource() (in module script)
GREY() (in module colors)
Grid (class in decors)
GridActor (class in actors)
groupArgmin() (in module arraytools)
groupInputItem() (in module widgets)
growAxis() (in module arraytools)
growCollection() (in module tools)
growSelection() (trisurface.TriSurface method)

H

has_key() (collection.Collection method)
has_lighting() (canvas.Canvas method)
(viewport.QtCanvas method)
hasAnnotation() (objects.DrawableObjects method)
hasExternal() (in module utils)
hasModule() (in module utils)
header_data() (project.Project method)
heads() (coords.BoundVectors method)
highlight() (in module draw)
highlightActors() (in module draw)
highlightEdges() (in module draw)
highlightElements() (in module draw)
highlightPartitions() (in module draw)
highlightPoints() (in module draw)
histogram2() (in module arraytools)
horner() (in module arraytools)
hyperCylindrical() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)

I

idraw() (viewport.QtCanvas method)
ignore_error() (in module flatkeydb)
image (module)
image2glcolor() (in module imagecolor)
imagearray (module)
imagecolor (module)
imageFormatFromExt() (in module image)
imageFormats() (in module image)
ImageView (class in widgets)
imageViewer (module)
importDXF() (in module dxf)
index() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
inertia (module)
inertia() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(in module inertia)
(trisurface.TriSurface method)
info() (formex.Formex method)
initialize() (in module image)
(in module mesh_ext)
inputAny() (in module widgets)
InputBool (class in widgets)
InputButton (class in widgets)
InputColor (class in widgets)
InputCombo (class in widgets)
InputDialog (class in widgets)
InputFloat (class in widgets)
InputFont (class in widgets)
InputForm (class in widgets)
InputFSlider (class in widgets)
InputGroup (class in widgets)
InputInfo (class in widgets)
InputInteger (class in widgets)
InputItem (class in widgets)
InputIVector (class in widgets)
InputLabel (class in widgets)
InputList (class in widgets)
InputPoint (class in widgets)
InputPush (class in widgets)
InputRadio (class in widgets)
InputSlider (class in widgets)
InputString (class in widgets)
InputTab (class in widgets)
InputText (class in widgets)
InputWidget (class in widgets)
insert() (flatkeydb.FlatDB method)
insert_action() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
insert_menu() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
insert_sep() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
insertItems() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
insertKnots() (nurbs.NurbsCurve method)
insertLevel() (connectivity.Connectivity method)
inside() (in module arraytools)
insideSimplex() (in module geomtools)
insideTriangle() (in module geomtools)
Interaction (class in fe_abq)
International() (units.UnitsSystem method)
interpolate() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(in module formex)
interrogate() (in module utils)
intersection() (in module olist)
intersectionLinesPWP() (in module geomtools)
intersectionLinesWithPlane() (in module formex)
intersectionPointsLWL() (in module geomtools)
intersectionPointsLWP() (in module geomtools)
intersectionPointsLWT() (in module geomtools)
intersectionPointsPOL() (in module geomtools)
intersectionPointsPOP() (in module geomtools)
intersectionPointsPWP() (in module geomtools)
intersectionPointsSWP() (in module geomtools)
intersectionPointsSWT() (in module geomtools)
intersectionTimesLWL() (in module geomtools)
intersectionTimesLWP() (in module geomtools)
intersectionTimesLWT() (in module geomtools)
intersectionTimesPOL() (in module geomtools)
intersectionTimesPOP() (in module geomtools)
intersectionTimesSWP() (in module geomtools)
intersectionTimesSWT() (in module geomtools)
intersectionWithPlane() (formex.Formex method)
(trisurface.TriSurface method)
inverse() (connectivity.Connectivity method)
inverseIndex() (in module arraytools)
inverseUniqueIndex() (in module arraytools)
is_pyFormex() (in module utils)
isClose() (in module arraytools)
isClosedManifold() (trisurface.TriSurface method)
isManifold() (trisurface.TriSurface method)
Isopar (class in isopar)
isopar (module)
isopar() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
isWritable() (in module script)
item() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
items() (collection.Collection method)
(odict.KeyedList method)
(odict.ODict method)

K

keep() (objects.DrawableObjects method)
(objects.Objects method)
key_error_handler() (flatkeydb.FlatDB method)
KeyedList (class in odict)
keys() (collection.Collection method)
(config.Config method)
(odict.KeyedList method)
(odict.ODict method)
killProcesses() (in module utils)
knotPoints() (nurbs.NurbsCurve method)
knotVector() (in module nurbs)

L

largestByConnection() (in module mesh_ext)
(trisurface.TriSurface method)
layer() (dxf.DxfExporter method)
layout() (built-in function)
(in module draw)
length() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(in module arraytools)
(in module viewport)
length_intgrnd() (curve.BezierSpline method)
(curve.CardinalSpline method)
lengths() (curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.Line method)
(curve.PolyLine method)
lights() (in module draw)
Lima (class in lima)
lima (module)
lima() (in module lima)
Line (class in curve)
(class in decors)
line() (dxf.DxfExporter method)
(in module simple)
LineDrawing (class in decors)
lineIntersection() (in module geomtools)
linestipple() (in module draw)
linewidth() (in module draw)
link() (viewport.MultiCanvas method)
(viewport.NewMultiCanvas method)
linkViewport() (built-in function)
(in module draw)
listAll() (in module script)
(objects.DrawableObjects method)
(objects.Objects method)
listDegenerate() (connectivity.Connectivity method)
listDuplicate() (connectivity.Connectivity method)
listNonDegenerate() (connectivity.Connectivity method)
ListSelection (class in widgets)
listTree() (in module utils)
listUnique() (connectivity.Connectivity method)
load() (project.Project method)
loadCurrentRotation() (camera.Camera method)
loadFiles() (scriptMenu.ScriptMenu method)
loadModelView() (camera.Camera method)
loadProjection() (camera.Camera method)
lock() (camera.Camera method)
longestEdge() (trisurface.TriSurface method)
lpattern() (in module formex)

M

map() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
map1() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
mapd() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
Mark (class in decors)
(class in marks)
MarkList (class in marks)
marks (module)
match() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(flatkeydb.FlatDB method)
matchAll() (in module utils)
matchAny() (in module utils)
matchCentroids() (mesh.Mesh method)
(trisurface.TriSurface method)
matchCoords() (mesh.Mesh method)
(trisurface.TriSurface method)
matchCount() (in module utils)
matchFaces() (mesh.Mesh method)
(trisurface.TriSurface method)
matchIndex() (in module arraytools)
matchMany() (in module utils)
matchNone() (in module utils)
MaterialDB (class in properties)
maxProp() (formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
maxSize() (in module widgets)
meanNodes() (mesh.Mesh method)
(trisurface.TriSurface method)
Menu (class in menu)
menu (module)
MenuBar (class in menu)
mergedModel() (in module fe)
mergeMeshes() (in module mesh)
mergeNodes() (in module mesh)
Mesh (class in mesh)
mesh (module)
mesh_ext (module)
message() (in module draw)
(in module sendmail)
MessageBox (class in widgets)
mirror() (formex.Formex method)
Model (class in fe)
mouse_draw() (viewport.QtCanvas method)
mouse_draw_line() (viewport.QtCanvas method)
mouse_pick() (viewport.QtCanvas method)
mouse_rectangle_zoom() (viewport.QtCanvas method)
mouseMoveEvent() (viewport.QtCanvas method)
mousePressEvent() (viewport.QtCanvas method)
mouseReleaseEvent() (viewport.QtCanvas method)
move() (camera.Camera method)
movingAverage() (in module arraytools)
movingView() (in module arraytools)
mplex() (fe.Model method)
mtime() (in module utils)
MultiCanvas (class in viewport)
mv() (in module turtle)
mydict (module)

N

name() (widgets.ButtonBox method)
(widgets.InputBool method)
(widgets.InputButton method)
(widgets.InputColor method)
(widgets.InputCombo method)
(widgets.InputFSlider method)
(widgets.InputFloat method)
(widgets.InputFont method)
(widgets.InputIVector method)
(widgets.InputInfo method)
(widgets.InputInteger method)
(widgets.InputItem method)
(widgets.InputLabel method)
(widgets.InputList method)
(widgets.InputPoint method)
(widgets.InputPush method)
(widgets.InputRadio method)
(widgets.InputSlider method)
(widgets.InputString method)
(widgets.InputText method)
(widgets.InputWidget method)
named() (in module script)
names() (menu.ActionList method)
NameSequence (class in utils)
NaturalSpline (class in curve)
ncoords() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(nurbs.Coords4 method)
ndarray
ndim() (formex.Formex method)
nearestValue() (in module arraytools)
nEdgeAdjacent() (mesh.Mesh method)
(trisurface.TriSurface method)
nEdgeConnected() (mesh.Mesh method)
(trisurface.TriSurface method)
nedges() (mesh.Mesh method)
(trisurface.TriSurface method)
nelems() (connectivity.Connectivity method)
(fe.Model method)
(formex.Formex method)
NewMultiCanvas (class in viewport)
newRecord() (flatkeydb.FlatDB method)
newView() (viewport.MultiCanvas method)
next() (utils.NameSequence method)
nextFilename() (in module tetgen)
nextitem() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
nfaces() (trisurface.TriSurface method)
ngroups() (fe.Model method)
niceLogSize() (in module arraytools)
niceNumber() (in module arraytools)
nNodeAdjacent() (mesh.Mesh method)
(trisurface.TriSurface method)
nNodeConnected() (mesh.Mesh method)
(trisurface.TriSurface method)
nnodes() (fe.Model method)
(formex.Formex method)
nodeAdjacency() (mesh.Mesh method)
(trisurface.TriSurface method)
nodeConnections() (mesh.Mesh method)
(trisurface.TriSurface method)
nodeFront() (in module mesh_ext)
(trisurface.TriSurface method)
nodeProp() (properties.PropertyDB method)
nonManifoldEdges() (trisurface.TriSurface method)
norm() (in module arraytools)
normalize() (in module arraytools)
(nurbs.Coords4 method)
nplex() (connectivity.Connectivity method)
(formex.Formex method)
npoints() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(formex.Formex method)
(nurbs.Coords4 method)
nsetName() (in module fe_abq)
nurbs (module)
NurbsCurve (class in nurbs)
NurbsSurface (class in nurbs)

O

object_type() (objects.DrawableObjects method)
(objects.Objects method)
Objects (class in objects)
objects (module)
ODict (class in odict)
odict (module)
odict() (objects.DrawableObjects method)
(objects.Objects method)
off_to_tet() (in module trisurface)
offset() (trisurface.TriSurface method)
olist (module)
onOff() (in module canvas)
open() (curve.Line method)
(curve.PolyLine method)
OpenGLFormat() (in module viewport)
origin() (coords.CoordinateSystem method)
(in module coords)
origins() (coords.BoundVectors method)
orthog() (in module arraytools)
out() (dxf.DxfExporter method)
Output (class in fe_abq)
overflow() (colorscale.ColorLegend method)

P

pan() (camera.Camera method)
parse() (flatkeydb.FlatDB method)
parseLine() (flatkeydb.FlatDB method)
part() (curve.BezierSpline method)
(curve.CardinalSpline method)
partitionByAngle() (in module mesh_ext)
(trisurface.TriSurface method)
partitionByConnection() (in module mesh_ext)
(trisurface.TriSurface method)
partitionByEdgeFront() (trisurface.TriSurface method)
partitionByNodeFront() (in module mesh_ext)
(trisurface.TriSurface method)
partitionCollection() (in module tools)
pattern() (in module coords)
pause() (in module draw)
peek() (utils.NameSequence method)
perpendicularVector() (in module geomtools)
pick() (in module draw)
(viewport.QtCanvas method)
pick_actors() (viewport.QtCanvas method)
pick_edges() (viewport.QtCanvas method)
pick_elements() (viewport.QtCanvas method)
pick_numbers() (viewport.QtCanvas method)
pick_parts() (viewport.QtCanvas method)
pick_points() (viewport.QtCanvas method)
pickGL() (actors.GeomActor method)
(decors.ColorLegend method)
(decors.Decoration method)
(decors.GlutText method)
(decors.Grid method)
(decors.Line method)
(decors.LineDrawing method)
(decors.Mark method)
(decors.Rectangle method)
(decors.Triade method)
(marks.AxesMark method)
(marks.Mark method)
(marks.MarkList method)
(marks.TextMark method)
pickle_load() (in module project)
pickNumbers() (viewport.QtCanvas method)
PlaneActor (class in actors)
PlaneSection (class in section2d)
play() (in module script)
(in module turtle)
playFile() (in module script)
playScript() (in module script)
plexitude
plot2d (module)
point() (formex.Formex method)
(in module simple)
point2str() (formex.Formex class method)
points() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(formex.Formex method)
pointsAt() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(in module formex)
(nurbs.NurbsCurve method)
(nurbs.NurbsSurface method)
pointsAtLines() (in module geomtools)
pointsAtSegments() (in module geomtools)
pointsize() (in module draw)
pointsOff() (curve.BezierSpline method)
(curve.CardinalSpline method)
pointsOn() (curve.BezierSpline method)
(curve.CardinalSpline method)
pointsOnBezierCurve() (in module nurbs)
polygon() (in module simple)
polygonArea() (in module geomtools)
polygonNormals() (in module geomtools)
PolyLine (class in curve)
pop() (in module turtle)
pos() (odict.KeyedList method)
(odict.ODict method)
position() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
postproc (module)
Predefined() (units.UnitsSystem method)
prefixDict() (in module utils)
prefixFiles() (in module utils)
principal() (in module inertia)
princTensor2D() (in module section2d)
printall() (in module script)
printbbox() (objects.DrawableObjects method)
(objects.Objects method)
printElementTypes() (in module elements)
printMessage() (in module draw)
printval() (objects.DrawableObjects method)
(objects.Objects method)
processArgs() (in module script)
Project (class in project)
project (module)
project() (camera.Camera method)
(canvas.Canvas method)
(viewport.QtCanvas method)
projection() (in module arraytools)
(in module viewport)
projectionVOP() (in module geomtools)
projectionVOV() (in module geomtools)
projectName() (in module utils)
projectOnCylinder() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
projectOnPlane() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
projectOnSphere() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
projectOnSurface() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
ProjectSelection (class in widgets)
Prop() (properties.PropertyDB method)
properties (module)
PropertyDB (class in properties)
propSet() (formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
pshape() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
push() (in module turtle)
pyformexFiles() (in module utils)

Q

QtCanvas (class in viewport)
quadraticCurve() (in module simple)
quit() (in module script)

R

raiseKeyError() (in module mydict)
randomNoise() (in module arraytools)
read() (config.Config method)
(formex.Formex class method)
(timer.Timer method)
(trisurface.TriSurface class method)
Read() (units.UnitsSystem method)
read_ascii_large() (in module trisurface)
read_error() (in module trisurface)
read_gambit_neutral() (in module trisurface)
read_gts() (in module trisurface)
read_off() (in module trisurface)
read_stl() (in module trisurface)
read_stla() (in module trisurface)
readArray() (in module arraytools)
readCoords() (in module flavia)
readData() (in module datareader)
readDatabase() (properties.Database method)
(properties.MaterialDB method)
(properties.SectionDB method)
readDXF() (in module dxf)
readEleFile() (in module tetgen)
readElems() (in module flavia)
readElemsBlock() (in module tetgen)
readFaceFile() (in module tetgen)
readFacesBlock() (in module tetgen)
readFile() (flatkeydb.FlatDB method)
readFlavia() (in module flavia)
readFromFile() (objects.DrawableObjects method)
(objects.Objects method)
readGeomFile() (in module script)
readHeader() (project.Project method)
readMesh() (in module flavia)
readNeigh() (in module tetgen)
readNodeFile() (in module tetgen)
readNodesBlock() (in module tetgen)
readPolyFile() (in module tetgen)
readResult() (in module flavia)
readResults() (in module flavia)
readSmeshFacetsBlock() (in module tetgen)
readSmeshFile() (in module tetgen)
readSurface() (in module tetgen)
readTetgen() (in module tetgen)
record_error_handler() (flatkeydb.FlatDB method)
rect() (in module simple)
Rectangle (class in decors)
rectangle() (in module simple)
Rectangle() (in module trisurface)
redrawAll() (canvas.Canvas method)
(viewport.QtCanvas method)
reduceAdjacency() (in module connectivity)
reduceDegenerate() (connectivity.Connectivity method)
(mesh.Mesh method)
(trisurface.TriSurface method)
refine() (trisurface.TriSurface method)
reflect() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
refreshDict() (in module utils)
regularGrid() (in module simple)
reload() (scriptMenu.ScriptMenu method)
remember() (objects.DrawableObjects method)
(objects.Objects method)
remove() (canvas.Canvas method)
(collection.Collection method)
(formex.Formex method)
(viewport.QtCanvas method)
remove_triangles() (in module trisurface)
removeActor() (canvas.Canvas method)
(viewport.QtCanvas method)
removeActors() (canvas.Canvas method)
(viewport.QtCanvas method)
removeAll() (canvas.Canvas method)
(viewport.QtCanvas method)
removeAnnotation() (canvas.Canvas method)
(objects.DrawableObjects method)
(viewport.QtCanvas method)
removeAnnotations() (canvas.Canvas method)
(viewport.QtCanvas method)
removeButton() (in module toolbar)
removeDecoration() (canvas.Canvas method)
(viewport.QtCanvas method)
removeDecorations() (canvas.Canvas method)
(viewport.QtCanvas method)
removeDegenerate() (connectivity.Connectivity method)
(mesh.Mesh method)
(trisurface.TriSurface method)
removeDict() (in module utils)
removeDuplicate() (connectivity.Connectivity method)
(formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
removeHighlight() (canvas.Canvas method)
(viewport.QtCanvas method)
removeHighlights() (canvas.Canvas method)
(in module draw)
(viewport.QtCanvas method)
removeItem() (menu.BaseMenu method)
(menu.Menu method)
(menu.MenuBar method)
RemoveListItem() (in module properties)
removeTree() (in module utils)
removeView() (viewport.NewMultiCanvas method)
removeViewport() (built-in function)
(in module draw)
rename() (in module script)
renumber() (fe.Model method)
(mesh.Mesh method)
(trisurface.TriSurface method)
renumberElems() (mesh.Mesh method)
(trisurface.TriSurface method)
renumberIndex() (in module arraytools)
reorder() (connectivity.Connectivity method)
reorderAxis() (in module arraytools)
rep() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(formex.Formex method)
replace() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
replic() (formex.Formex method)
replic2() (formex.Formex method)
replicate() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(formex.Formex method)
report() (camera.Camera method)
(connectivity.Connectivity method)
(in module mesh_ext)
(mesh.Mesh method)
(trisurface.TriSurface method)
requireRevision() (in module script)
reset() (canvas.CanvasSettings method)
(in module turtle)
(timer.Timer method)
resetArea() (camera.Camera method)
resetDefaults() (canvas.Canvas method)
(viewport.QtCanvas method)
resetGUI() (in module menu)
resetLighting() (canvas.Canvas method)
(viewport.QtCanvas method)
resetOptions() (viewport.QtCanvas method)
resetWarnings() (in module menu)
resized() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
resolve() (connectivity.Connectivity method)
Result (class in fe_abq)
returnNone() (in module mydict)
reverse() (curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.Line method)
(curve.PolyLine method)
(formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
reverseAxis() (in module arraytools)
revolve() (mesh.Mesh method)
(trisurface.TriSurface method)
rgb2qimage() (in module imagearray)
RGBA() (in module colors)
RGBcolor() (in module colors)
rings() (in module mesh_ext)
(trisurface.TriSurface method)
ro() (in module turtle)
roll() (in module olist)
rollAxes() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
ros() (formex.Formex method)
rosette() (formex.Formex method)
rot() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
rotate() (camera.Camera method)
(coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
RotatedActor (class in actors)
rotationAngle() (in module geomtools)
rotationAnglesFromMatrix() (in module arraytools)
rotationMatrix() (in module arraytools)
rotmat() (in module arraytools)
rotMatrix() (in module arraytools)
run() (scriptMenu.ScriptMenu method)
runAll() (scriptMenu.ScriptMenu method)
runAllFiles() (scriptMenu.ScriptMenu method)
runAllNext() (scriptMenu.ScriptMenu method)
runAllRandom() (scriptMenu.ScriptMenu method)
runCommand() (in module utils)
runNext() (scriptMenu.ScriptMenu method)
runRandom() (scriptMenu.ScriptMenu method)
runScript() (scriptMenu.ScriptMenu method)
runTetgen() (in module tetgen)
runtime() (in module script)

S

save() (in module image)
(project.Project method)
save_canvas() (in module image)
save_main_window() (in module image)
save_rect() (in module image)
save_window() (in module image)
saveBuffer() (canvas.Canvas method)
(viewport.QtCanvas method)
saveIcon() (in module image)
saveImage() (in module image)
SaveImageDialog (class in widgets)
saveModelView() (camera.Camera method)
saveMovie() (in module image)
saveNext() (in module image)
scale() (colorscale.ColorScale method)
(coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
scaledJacobian() (in module mesh_ext)
(trisurface.TriSurface method)
script (module)
scriptKeywords() (in module scriptMenu)
ScriptMenu (class in scriptMenu)
scriptMenu (module)
seconds() (timer.Timer method)
section() (dxf.DxfExporter method)
section2d (module)
sectionChar() (in module section2d)
SectionDB (class in properties)
sector() (in module simple)
segmentOrientation() (in module geomtools)
select() (actors.GeomActor method)
(formex.Formex method)
(in module olist)
(mesh.Mesh method)
(trisurface.TriSurface method)
selectDict() (in module utils)
selectFont() (in module widgets)
selectNodes() (connectivity.Connectivity method)
(formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
sendmail (module)
sendmail() (in module sendmail)
set() (collection.Collection method)
(coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(objects.DrawableObjects method)
(objects.Objects method)
set_edit_mode() (in module draw)
set_material_value() (in module draw)
setAll() (widgets.InputList method)
setAlpha() (actors.GeomActor method)
setAmbient() (canvas.Canvas method)
(viewport.QtCanvas method)
setAngles() (camera.Camera method)
setArea() (camera.Camera method)
setBbox() (canvas.Canvas method)
(viewport.QtCanvas method)
setBgColor() (canvas.Canvas method)
(viewport.QtCanvas method)
setBkColor() (actors.GeomActor method)
setCamera() (canvas.Canvas method)
(viewport.QtCanvas method)
setCenter() (camera.Camera method)
setChecked() (widgets.InputList method)
setClip() (camera.Camera method)
setColor() (actors.Actor method)
(actors.AxesActor method)
(actors.BboxActor method)
(actors.CoordPlaneActor method)
(actors.CubeActor method)
(actors.GeomActor method)
(actors.GridActor method)
(actors.PlaneActor method)
(actors.RotatedActor method)
(actors.SphereActor method)
(actors.TranslatedActor method)
(decors.ColorLegend method)
(decors.Decoration method)
(decors.GlutText method)
(decors.Grid method)
(decors.Line method)
(decors.LineDrawing method)
(decors.Mark method)
(decors.Rectangle method)
(decors.Triade method)
(marks.AxesMark method)
(marks.Mark method)
(marks.MarkList method)
(marks.TextMark method)
setCoords() (trisurface.TriSurface method)
setCurrent() (viewport.MultiCanvas method)
(viewport.NewMultiCanvas method)
setCursorShape() (viewport.CursorShapeHandler method)
(viewport.QtCanvas method)
setCursorShapeFromFunc() (viewport.CursorShapeHandler method)
(viewport.QtCanvas method)
setDefault() (canvas.CanvasSettings method)
setdefault() (canvas.CanvasSettings method)
(config.Config method)
(fe_abq.Interaction method)
(fe_abq.Output method)
(fe_abq.Result method)
(mydict.CDict method)
(mydict.Dict method)
(properties.Database method)
(properties.EdgeLoad method)
(properties.ElemLoad method)
(properties.ElemSection method)
(properties.MaterialDB method)
(properties.PropertyDB method)
(properties.SectionDB method)
setDefaults() (canvas.Canvas method)
(viewport.QtCanvas method)
setDist() (camera.Camera method)
setDrawOptions() (in module draw)
setEdgesAndFaces() (trisurface.TriSurface method)
setElems() (trisurface.TriSurface method)
setFgColor() (canvas.Canvas method)
(viewport.QtCanvas method)
setIcon() (widgets.ButtonBox method)
(widgets.InputPush method)
setLens() (camera.Camera method)
setLineStipple() (actors.Actor method)
(actors.AxesActor method)
(actors.BboxActor method)
(actors.CoordPlaneActor method)
(actors.CubeActor method)
(actors.GeomActor method)
(actors.GridActor method)
(actors.PlaneActor method)
(actors.RotatedActor method)
(actors.SphereActor method)
(actors.TranslatedActor method)
(canvas.Canvas method)
(decors.ColorLegend method)
(decors.Decoration method)
(decors.GlutText method)
(decors.Grid method)
(decors.Line method)
(decors.LineDrawing method)
(decors.Mark method)
(decors.Rectangle method)
(decors.Triade method)
(marks.AxesMark method)
(marks.Mark method)
(marks.MarkList method)
(marks.TextMark method)
(viewport.QtCanvas method)
setLineWidth() (actors.Actor method)
(actors.AxesActor method)
(actors.BboxActor method)
(actors.CoordPlaneActor method)
(actors.CubeActor method)
(actors.GeomActor method)
(actors.GridActor method)
(actors.PlaneActor method)
(actors.RotatedActor method)
(actors.SphereActor method)
(actors.TranslatedActor method)
(canvas.Canvas method)
(decors.ColorLegend method)
(decors.Decoration method)
(decors.GlutText method)
(decors.Grid method)
(decors.Line method)
(decors.LineDrawing method)
(decors.Mark method)
(decors.Rectangle method)
(decors.Triade method)
(marks.AxesMark method)
(marks.Mark method)
(marks.MarkList method)
(marks.TextMark method)
(viewport.QtCanvas method)
setMaterial() (canvas.Canvas method)
(viewport.QtCanvas method)
setMaterialDB() (properties.PropertyDB method)
setMode() (canvas.CanvasSettings method)
setModelView() (camera.Camera method)
setNone() (widgets.InputList method)
setOpenGLFormat() (in module viewport)
setOptions() (viewport.QtCanvas method)
setPerspective() (camera.Camera method)
setPickable() (viewport.QtCanvas method)
setPointSize() (canvas.Canvas method)
(viewport.QtCanvas method)
setPrefs() (in module script)
setPrintFunction() (formex.Formex class method)
setProp() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(formex.Formex method)
(mesh.Mesh method)
(objects.DrawableObjects method)
(trisurface.TriSurface method)
setRenderMode() (canvas.Canvas method)
(viewport.QtCanvas method)
setRotation() (camera.Camera method)
setSaneLocale() (in module utils)
setSectionDB() (properties.PropertyDB method)
setSelected() (widgets.InputList method)
setSlColor() (canvas.Canvas method)
(viewport.QtCanvas method)
setStretch() (viewport.NewMultiCanvas method)
setText() (widgets.ButtonBox method)
(widgets.InputPush method)
setTracking() (camera.Camera method)
setTriade() (canvas.Canvas method)
(in module draw)
(viewport.QtCanvas method)
setType() (mesh.Mesh method)
(trisurface.TriSurface method)
setValue() (widgets.ButtonBox method)
(widgets.InputBool method)
(widgets.InputButton method)
(widgets.InputColor method)
(widgets.InputCombo method)
(widgets.InputFSlider method)
(widgets.InputFloat method)
(widgets.InputFont method)
(widgets.InputGroup method)
(widgets.InputIVector method)
(widgets.InputInfo method)
(widgets.InputInteger method)
(widgets.InputItem method)
(widgets.InputLabel method)
(widgets.InputList method)
(widgets.InputPoint method)
(widgets.InputPush method)
(widgets.InputRadio method)
(widgets.InputSlider method)
(widgets.InputString method)
(widgets.InputText method)
(widgets.InputWidget method)
(widgets.ListSelection method)
setValues() (widgets.CoordsBox method)
setView() (in module draw)
shape() (formex.Formex method)
(in module simple)
(trisurface.TriSurface method)
shear() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
shortestEdge() (trisurface.TriSurface method)
show() (widgets.InputDialog method)
(widgets.InputFSlider method)
(widgets.InputFloat method)
(widgets.InputInteger method)
(widgets.InputSlider method)
(widgets.InputString method)
(widgets.InputText method)
(widgets.ListSelection method)
showBuffer() (canvas.Canvas method)
(viewport.QtCanvas method)
showDescription() (in module draw)
showFile() (in module draw)
showHistogram() (in module plot2d)
showImage() (widgets.ImageView method)
showInfo() (in module draw)
showLineDrawing() (in module draw)
showMessage() (in module draw)
showText() (in module draw)
showWidget() (viewport.MultiCanvas method)
shrink() (formex.Formex method)
simple (module)
simpleInputItem() (in module widgets)
sind() (in module arraytools)
(in module turtle)
sizes() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
skipComments() (in module tetgen)
sleep() (in module draw)
slice() (trisurface.TriSurface method)
smallestAltitude() (trisurface.TriSurface method)
smooth() (mesh.Mesh method)
(trisurface.TriSurface method)
smoothLaplaceHC() (trisurface.TriSurface method)
smoothLowPass() (trisurface.TriSurface method)
solveMany() (in module arraytools)
sort() (odict.KeyedList method)
(odict.ODict method)
sortAdjacency() (in module connectivity)
sortByColumns() (in module arraytools)
sortSets() (in module scriptMenu)
spawn() (in module utils)
Sphere() (in module trisurface)
sphere2() (in module simple)
sphere3() (in module simple)
SphereActor (class in actors)
spherical() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
Spiral (class in curve)
splash image
split() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Line method)
(curve.PolyLine method)
(formex.Formex method)
(trisurface.TriSurface method)
splitByConnection() (in module mesh_ext)
(trisurface.TriSurface method)
splitDegenerate() (mesh.Mesh method)
(trisurface.TriSurface method)
splitElems() (fe.Model method)
splitEndDigits() (in module utils)
splitFloat() (in module datareader)
splitKeyValue() (flatkeydb.FlatDB method)
(in module flatkeydb)
splitProp() (formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
splitRandom() (mesh.Mesh method)
(trisurface.TriSurface method)
splitStartDigits() (in module utils)
st() (in module turtle)
stack() (in module arraytools)
start_draw() (viewport.QtCanvas method)
start_drawing() (viewport.QtCanvas method)
start_selection() (viewport.QtCanvas method)
startGui() (in module script)
stats() (trisurface.TriSurface method)
status() (lima.Lima method)
step() (in module draw)
stlConvert() (in module trisurface)
stopatbreakpt() (in module script)
stripLine() (in module tetgen)
strNorm() (in module utils)
stuur() (in module utils)
sub_curvature() (curve.BezierSpline method)
(curve.CardinalSpline method)
sub_directions() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
sub_directions_2() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
sub_points() (curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.Curve method)
(curve.Line method)
(curve.PolyLine method)
(curve.Spiral method)
sub_points2() (curve.Line method)
(curve.PolyLine method)
sub_points_2() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
subDict() (in module utils)
subPoints() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
superSpherical() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
surface_volume() (in module trisurface)
surfaceType() (trisurface.TriSurface method)
swapAxes() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
sweep() (mesh.Mesh method)
(trisurface.TriSurface method)
sweepCoords() (in module coords)
symdifference() (in module olist)
system() (in module script)

T

tabInputItem() (in module widgets)
Table (class in widgets)
TableDialog (class in widgets)
TableModel (class in widgets)
Tabs (class in widgets)
tand() (in module arraytools)
(in module camera)
test() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
testDegenerate() (connectivity.Connectivity method)
testDuplicate() (connectivity.Connectivity method)
tetgen (module)
Text (in module decors)
text() (widgets.ButtonBox method)
(widgets.InputBool method)
(widgets.InputButton method)
(widgets.InputColor method)
(widgets.InputCombo method)
(widgets.InputFSlider method)
(widgets.InputFloat method)
(widgets.InputFont method)
(widgets.InputIVector method)
(widgets.InputInfo method)
(widgets.InputInteger method)
(widgets.InputItem method)
(widgets.InputLabel method)
(widgets.InputList method)
(widgets.InputPoint method)
(widgets.InputPush method)
(widgets.InputRadio method)
(widgets.InputSlider method)
(widgets.InputString method)
(widgets.InputText method)
(widgets.InputWidget method)
TextBox (class in widgets)
TextMark (class in marks)
tildeExpand() (in module utils)
tilt() (camera.Camera method)
timedOut() (widgets.InputDialog method)
(widgets.ListSelection method)
timeEval() (in module utils)
timeout() (in module toolbar)
(widgets.InputDialog method)
(widgets.ListSelection method)
Timer (class in timer)
timer (module)
toCoords() (nurbs.Coords4 method)
toCoords4() (in module nurbs)
toCylindrical() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
toFormex() (curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(mesh.Mesh method)
(trisurface.TriSurface method)
toggleAnnotation() (objects.DrawableObjects method)
toggleButton() (in module toolbar)
toMesh() (curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.Line method)
(curve.PolyLine method)
(formex.Formex method)
toolbar (module)
tools (module)
toSpherical() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
toSurface() (formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
toWorld() (camera.Camera method)
transArea() (camera.Camera method)
transform() (camera.Camera method)
(isopar.Isopar method)
transformCS() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
translate() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(lima.Lima method)
(mesh.Mesh method)
(trisurface.TriSurface method)
TranslatedActor (class in actors)
translatem() (formex.Formex method)
transparent() (in module draw)
trfMatrix() (in module arraytools)
Triade (class in decors)
triangle() (in module simple)
triangleBoundingCircle() (in module geomtools)
triangleCircumCircle() (in module geomtools)
triangleInCircle() (in module geomtools)
triangleObtuse() (in module geomtools)
TriSurface (class in trisurface)
trisurface (module)
trl() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(fe.Model method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
turtle (module)

U

uncompress() (project.Project method)
undoChanges() (objects.DrawableObjects method)
(objects.Objects method)
undraw() (in module draw)
uniformParamValues() (in module nurbs)
union() (in module olist)
unique() (formex.Formex method)
uniqueOrdered() (in module arraytools)
uniqueRows() (in module arraytools)
units (module)
UnitsSystem (class in units)
unitVector() (in module arraytools)
unProject() (camera.Camera method)
(canvas.Canvas method)
(viewport.QtCanvas method)
unQuote() (in module flatkeydb)
update() (canvas.CanvasSettings method)
(config.Config method)
(fe_abq.Interaction method)
(fe_abq.Output method)
(fe_abq.Result method)
(mydict.CDict method)
(mydict.Dict method)
(odict.KeyedList method)
(odict.ODict method)
(properties.Database method)
(properties.EdgeLoad method)
(properties.ElemLoad method)
(properties.ElemSection method)
(properties.MaterialDB method)
(properties.PropertyDB method)
(properties.SectionDB method)
updateButton() (in module toolbar)
updateData() (widgets.InputDialog method)
(widgets.ListSelection method)
updateDialogItems() (in module widgets)
updateGUI() (in module draw)
updateLightButton() (in module toolbar)
updateNormalsButton() (in module toolbar)
updatePerspectiveButton() (in module toolbar)
updateText() (in module widgets)
updateTransparencyButton() (in module toolbar)
userName() (in module utils)
utils (module)

V

value() (widgets.ButtonBox method)
(widgets.InputBool method)
(widgets.InputButton method)
(widgets.InputColor method)
(widgets.InputCombo method)
(widgets.InputFSlider method)
(widgets.InputFloat method)
(widgets.InputFont method)
(widgets.InputGroup method)
(widgets.InputIVector method)
(widgets.InputInfo method)
(widgets.InputInteger method)
(widgets.InputItem method)
(widgets.InputLabel method)
(widgets.InputList method)
(widgets.InputPoint method)
(widgets.InputPush method)
(widgets.InputRadio method)
(widgets.InputSlider method)
(widgets.InputString method)
(widgets.InputText method)
(widgets.InputWidget method)
(widgets.ListSelection method)
values() (odict.KeyedList method)
(odict.ODict method)
vectorLength() (in module arraytools)
vectorNormalize() (in module arraytools)
vectorPairAngle() (in module arraytools)
vectorPairArea() (in module arraytools)
vectorPairAreaNormals() (in module arraytools)
vectorPairCosAngle() (in module arraytools)
vectorPairNormals() (in module arraytools)
vectorRotation() (in module arraytools)
vectors() (coords.BoundVectors method)
(curve.Line method)
(curve.PolyLine method)
vectorTripleProduct() (in module arraytools)
vertexDistance() (in module geomtools)
vertices() (actors.GeomActor method)
(formex.Formex method)
(trisurface.TriSurface method)
view() (formex.Formex method)
(in module draw)
ViewAngles (class in camera)
viewport (module)
viewport() (in module draw)
volume() (mesh.Mesh method)
(trisurface.TriSurface method)
volumes() (mesh.Mesh method)
(trisurface.TriSurface method)

W

w() (nurbs.Coords4 method)
wait() (in module draw)
wait_drawing() (viewport.QtCanvas method)
wait_selection() (viewport.QtCanvas method)
wakeup() (in module draw)
walkEdgeFront() (trisurface.TriSurface method)
warning() (in module draw)
WarningBox (class in widgets)
WEBcolor() (in module colors)
wheel_zoom() (viewport.QtCanvas method)
wheelEvent() (viewport.QtCanvas method)
whereProp() (formex.Formex method)
widgets (module)
withoutProp() (mesh.Mesh method)
(trisurface.TriSurface method)
withProp() (formex.Formex method)
(mesh.Mesh method)
(trisurface.TriSurface method)
writable() (in module script)
write() (config.Config method)
(curve.Arc method)
(curve.Arc3 method)
(curve.BezierSpline method)
(curve.CardinalSpline method)
(curve.CardinalSpline2 method)
(curve.Curve method)
(curve.Line method)
(curve.NaturalSpline method)
(curve.PolyLine method)
(curve.Spiral method)
(dxf.DxfExporter method)
(fe.Model method)
(fe_abq.AbqData method)
(formex.Formex method)
(geometry.Geometry method)
(mesh.Mesh method)
(trisurface.TriSurface method)
write_smesh() (in module trisurface)
write_stla() (in module trisurface)
writeArray() (in module arraytools)
writeCloads() (in module fe_abq)
writeDisplacements() (in module fe_abq)
writeDloads() (in module fe_abq)
writeDsloads() (in module fe_abq)
writeElemOutput() (in module fe_abq)
writeElemResult() (in module fe_abq)
writeElems() (in module fe_abq)
writeFile() (flatkeydb.FlatDB method)
writeFileOutput() (in module fe_abq)
writeGeomFile() (in module script)
writeNodeOutput() (in module fe_abq)
writeNodeResult() (in module fe_abq)
writeNodes() (in module fe_abq)
(in module tetgen)
writeSection() (in module fe_abq)
writeSet() (in module fe_abq)
writeSmesh() (in module tetgen)
writeSurface() (in module tetgen)
writeToFile() (objects.DrawableObjects method)
(objects.Objects method)

X

x() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(nurbs.Coords4 method)
xpattern() (in module coords)

Y

y() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(nurbs.Coords4 method)

Z

z() (coords.BoundVectors method)
(coords.CoordinateSystem method)
(coords.Coords method)
(nurbs.Coords4 method)
zoom() (canvas.Canvas method)
(viewport.QtCanvas method)
zoomAll() (canvas.Canvas method)
(in module draw)
(viewport.QtCanvas method)
zoomArea() (camera.Camera method)
zoomBbox() (in module draw)
zoomCentered() (canvas.Canvas method)
(viewport.QtCanvas method)
zoomRectangle() (canvas.Canvas method)
(in module draw)
(viewport.QtCanvas method)
pyformex-0.8.6/pyformex/doc/html/user-guide.html0000644000211500021150000003054011705104257021557 0ustar benebene00000000000000 pyFormex user guide — pyFormex v0.8.6 documentation

Previous topic

pyFormex tutorial

Next topic

Running pyFormex

[FSF Associate Member]

Valid XHTML 1.0 Transitional

pyformex-0.8.6/pyformex/doc/html/properties.html0000644000211500021150000012521111705104246021700 0ustar benebene00000000000000 Assigning properties to geometry — pyFormex v0.8.6 documentation

Assigning properties to geometry

As of version 0.7.1, the way to define properties for elements of the geometry has changed thoroughly. As a result, the property system has become much more flexibel and powerful, and can be used for Formex data structures as well as for TriSurfaces and Finite Element models.

With properties we mean any data connected with some part of the geometry other than the coordinates of its points or the structure of points into elements. Also, values that can be calculated purely from the coordinates of the points and the structure of the elements are usually not considerer properties.

Properties can e.g. define material characteristics, external loading and boundary conditions to be used in numerical simulations of the mechanics of a structure. The properties module includes some specific functions to facilitate assigning such properties. But the system is general enough to used it for any properties that you can think of.

Properties are collected in a PropertyDB object. Before you can store anything in this database, you need to create it. Usually, you will start with an empty database.

P = PropertyDB()

General properties

Now you can start entering property records into the database. A property record is a lot like a Python dict object, and thus it can contain nearly anything. It is implemented however as a CascadingDict object, which means that the key values are strings and can also be used as attributes to address the value. Thus, if P is a property record, then a field named key can either be addressed as P[‘key’] or as P.key. This implementation was choosen for the convenience of the user, but has no further advantages over a normal dict object. You should not use any of the methods of Python’s dict class as key in a property record: it would override this method for the object.

The property record has four more reserved (forbidden) keys: kind, tag, set, setname and nr. The kind and nr should never be set nor changed by the user. kind is used internally to distinguish among different kind of property records (see Node properties). It should only be used to extend the PropertyDB class with new kinds of properties, e.g. in subclasses. nr will be set automatically to a unique record number. Some application modules use this number for identification and to create automatic names for property sets.

The tag, set and setname keys are optional fields and can be set by the user. They should however only be used for the intended purposes explained hereafter, because they have a special meaning for the database methods and application modules.

The tag field can be used to attach an identification string to the property record. This string can be as complex as the user wants and its interpretation is completely left to the user. The PropertyDB class just provides an easy way to select the records by their tag name or by a set of tag names. The set and setname fields are treated further in Using the set and setname fields.

So let’s create a property record in our database. The Prop() method does just that. It also returns the property record, so you can directly use it further in your code.

>>> Stick = P.Prop(color='green',name='Stick',weight=25, \
        comment='This could be anything: a gum, a frog, a usb-stick,...'})
>>> print Stick

  color = green
  comment = This could be anything: a gum, a frog, a usb-stick,...
  nr = 0
  name = Stick
  weight = 25

Notice the auto-generated nr field. Here’s another example, with a tag:

>>> author = P.Prop(tag='author',name='Alfred E Neuman',\
        address=CascadingDict({'street':'Krijgslaan', 'city':'Gent','country':'Belgium'}))
>>> print author

  nr = 1
  tag = author
  name = Alfred E Neuman
  address =
    city = Gent
    street = Krijgslaan
    country = Belgium

This example shows that record values can be complex structured objects. Notice how the CascadingDict object is by default printed in a very readible layout, offsetting each lower level dictionary two more postions to the right.

The CascadingDict has yet another fine characteristic: if an attribute is not found in the toplevel, all values that are instances of CascadingDict or Dict (but not the normal Python dict) will be searched for the attribute. If needed, this searching is even repeated in the values of the next levels, and further on, thus cascading though all levels of CascadingDict structures until the attribute can eventually be found. The cascading does not proceed through values in a Dict. An attribute that is not found in any of the lower level dictionaries, will return a None value.

If you set an attribute of a CascadingDict, it is always set in the toplevel. If you want to change lower level attributes, you need to use the full path to it.

>>> print author.st
  Krijgslaan
>>> author.street = 'Voskenslaan'
>>> print author.street
  Voskenslaan
>>> print author.address.street
  Krijgslaan
>>> author.address.street = 'Wiemersdreef'
>>> print author.address.street
  Wiemersdreef
>>> author = P.Prop(tag='author',alias='John Doe',\
        address={'city': 'London', 'street': 'Downing Street 10', 'country': 'United Kingdom'})
>>> print author

  nr = 2
  tag = author
  alias = John Doe
  address = {'city': 'London', 'street': 'Downing Street 10', 'country': 'United Kingdom'}

In the examples above, we have given a name to the created property records, so that we could address them in the subsequent print and field assigment statements. In most cases however, it will be impractical and unnecessary to give your records a name. They all are recorded in the PropertyDB database, and will exist as long as the database variable lives. There should be a way though to request selected data from that database. The getProp() method returns a list of records satisfying some conditions. The examples below show how it can be used.

>>> for p in P.getProp(rec=[0,2]):
        print p.name
Stick
John Doe
>>>  for p in P.getProp(tag=['author']):
        print p.name
None
John Doe
>>>  for p in P.getProp(attr=['name']):
        print p.nr
0
2
>>>  for p in P.getProp(tag=['author'],attr=['name']):
        print p.name
John Doe

The first call selects records by number: either a single record number or a list of numbers can be specified. The second method selects records based on the value of their tag field. Again a single tag value or a list of values can be specified. Only those records having a ‘tag’ filed matching any of the values in the list will be returned. The third selection method is based on the existence of some attribute names in the record. Here, always a list of attribute names is required. Records are returned that posess all the attributes in the list, independent from the value of those attributes. If needed, the user can add a further filtering based on the attribute values. Finally, as is shown in the last example, all methods of record selection can be combined. Each extra condition will narrow the selection further down.

Using the set and setname fields

In the examples above, the property records contained general data, not related to any geometrical object. When working with large geometrical objects (whether Formex or other type), one often needs to specify properties that only hold for some of the elements of the object.

The set can be used to specify a list of integer numbers identifying a collection of elements of the geometrical object for which the current property is valid. Absence of the set usually means that the property is assigned to all elements; however, the property module itself does not enforce this behavior: it is up to the application to implement it.

Any record that has a set field, will also have a setname field, whose value is a string. If the user did not specify one, a set name will be auto-generated by the system. The setname field can be used in other records to refer to the same set of elements without having to specify them again. The following examples will make this clear.

>>> P.Prop(set=[0,1,3],setname='green_elements',color='green')
    P.Prop(setname='green_elements',transparent=True)

>>> a = P.Prop(set=[0,2,4,6],thickness=3.2)
    P.Prop(setname=a.setname,material='steel')

>>> for p in P.getProp(attr=['setname']):
        print p

color = green
nr = 3
set = [0 1 3]
setname = green_elements

nr = 4
transparent = True
setname = green_elements

nr = 5
set = [0 2 4 6]
setname = Set_5
thickness = 3.2

nr = 6
material = steel
setname = Set_5

In the first case, the user specifies a setname himself. In the second case, the auto-generated name is used. As a convenience, the user is allowed to write set=name instead of setname=name when referring to an already defined set.

>>> P.Prop(set='green_elements',transparent=False)
    for p in P.getProp(attr=['setname']):
        if p.setname == 'green_elements':
            print p.nr,p.transparent

3 None
4 True
7 False

Record 3 does not have the transparent attribute, so a value None is printed.

Specialized property records

The property system presented above allows for recording any kind of values. In many situations however we will want to work with a specialised and limited set of attributes. The main developers of e.g. often use the program to create geometrical models of structures of which they want to analyse the mechanical behavior. These numerical simulations (FEA, CFD) require specific data that support the introduction of specialised property records. Currently there are two such property record types: node properties (see Node properties), which are attributed to a single point in space, and element properties (Element properties), which are attributed to a structured collection of points.

Special purpose properties are distincted by their kind field. General property records have kind=”, node properties haven kind=’n’ and kind=’e’ is set for element properties. Users can create their own specialised property records by using other value for the kind parameter.

Node properties

Node properties are created with the nodeProp() method, rather than the general Prop(). The kind field does not need to be set: it will be done automatically. When selecting records using the getProp() method, add a kind=’n’ argument to select only node properties.

Node properties will recognize some special field names and check the values for consistency. Application plugins such as the Abaqus input file generator depend on these property structure, so the user should not mess with them. Currently, the following attributes are in use:

cload
A concentrated load at the node. This is a list of 6 items: three force components in axis directions and three force moments around the axes: [F_0, F_1, F_2, M_0, M_1, M_2].
bound
A boundary condition for the nodal displacement components. This can be defined in 2 ways:
  • as a list of 6 items [ u_0, u_1, u_2, r_0, r_1, r_2 ]. These items have 2

    possible values:

    0

    The degree of freedom is not restrained.

    1

    The degree of freedom is restrained.

  • as a string. This string is a standard boundary type. Abaqus will recognize

    the following strings:

  • PINNED

  • ENCASTRE

  • XSYMM

  • YSYMM

  • ZSYMM

  • XASYMM

  • YASYMM

  • ZASYMM

displacement
Prescribed displacements. This is a list of tuples (i,v), where i is a DOF number (1..6) and v is the prescribed value for that DOF.
coords
The coordinate system which is used for the definition of cload, bound and displ fields. It should be a CoordSys object.

Some simple examples:

P.nodeProp(cload=[5,0,-75,0,0,0])
P.nodeProp(set=[2,3],bound='pinned')
P.nodeProp(5,displ=[(1,0.7)])

The first line sets a concentrated load all the nodes, the second line sets a boundary condition ‘pinned’ on nodes 2 and 3. The third line sets a prescribed displacement on node 5 with value 0.7 along the first direction. The first positional argument indeed corresponds to the ‘set’ attribute.

Often the properties are computed and stored in variables rather than entered directly.

P1 = [ 1.0,1.0,1.0, 0.0,0.0,0.0 ]
P2 = [ 0.0 ] * 3 + [ 1.0 ] * 3
B1 = [ 1 ] + [ 0 ] * 5
CYL = CoordSystem('cylindrical',[0,0,0,0,0,1])
P.nodeProp(bound=B1,csys=CYL)

The first two lines define two concentrated loads: P1 consists of three point loads in each of the coordinate directions; P2 contains three force moments around the axes. The third line specifies a boundary condition where the first DOF (usually displacement in x-direction) is constrained, while the remaining 5 DOF’s are free. The next line defines a local coordinate system, in this case a cylindrical coordinate system with axis pointing from point [0.,0.,0.] to point [0.,0.,1.]. The last line

To facilitate property selection, a tag can be added.

nset1 = P.nodeProp(tag='loadcase 1',set=[2,3,4],cload=P1).nr
P.nodeProp(tag='loadcase 2',set=Nset(nset1),cload=P2)

The last two lines show how you can avoid duplication of sets in mulitple records. The same set of nodes should receive different concentrated load values for different load cases. The load case is stored in a tag, but duplicating the set definition could become wasteful if the sets are large. Instead of specifying the node numbers of the set directly, we can pass a string setting a set name. Of course, the application will need to know how to interprete the set names. Therefore the property module provides a unified way to attach a unique set name to each set defined in a property record. The name of a node property record set can be obtained with the function Nset(nr), where nr is the record number. In the example above, that value is first recorded in nset1 and then used in the last line to guarantee the use of the same set as in the property above.

Element properties

The elemProp() method creates element properties, which will have their kind attribute set to ‘e’. When selecting records using the getProp() method, add the kind=’e’ argument to get element properties.

Like node properties, element property records have a number of specialize fields. Currently, the following ones are recognized by the Abaqus input file generator.

eltype
This is the single most important element property. It sets the element type that will be used during the analysis. Notice that a Formex object also may have an eltype attribute; that one however is only used to describe the type of the geometric elements involved. The element type discussed here however may also define some other characteristics of the element, like the number and type of degrees of freedom to be used in the analysis or the integration rules to be used. What element types are available is dependent on the analysis package to be used. Currently, does not do any checks on the element type, so the simulation program’s own element designation may be used.
section
The section properties of the element. This should be an ElemSection instance, grouping material properties (like Young’s modulus) and geometrical properties (like plate thickness or beam section).
dload
A distributed load acting on the element. The value is an ElemLoad instance. Currently, this can include a label specifying the type of distributed loading, a value for the loading, and an optional amplitude curve for specifying the variation of a time dependent loading.

Property data classes

The data collected in property records can be very diverse. At times it can become quite difficult to keep these data consistent and compatible with other modules for further processing. The property module contains some data classes to help you in constructing appropriate data records for Finite Element models. The FeAbq module can currently interprete the following data types.

CoordSystem defines a local coordinate system for a node. Its constructor takes two arguments:

  • a string defining the type of coordinate system, either ‘Rectangular’, ‘Cylindrical’ or ‘Spherical’ (the first character suffices), and
  • a list of 6 coordinates, specifying two points A and B. With ‘R’, A is on the new x-axis and B is on the new ‘y axis. With ‘C’ and ‘S’, AB is the axis of the cylindrical/spherical coordinates.

Thus, CoordSystem('C',[0.,0.,0.,0.,0.,1.]) defines a cylindrical coordinate system with the global z as axis.

ElemLoad is a distributed load on an element. Its constructor takes two arguments:

  • a label defining the type of loading,
  • a value for the loading,
  • optionally, the name of an amplitude curve.

E.g., ElemLoad(‘PZ’,2.5) defines a distributed load of value 2.5 in the direction of the z-axis.

ElemSection can be used to set the material and section properties on the elements. It can hold:

  • a section,
  • a material,
  • an optional orientation,
  • an optional connector behavior,
  • a sectiontype (deprecated). The sectiontype should preferably be set togehter with the other section parameters.

An example:

>>> steel = {
    'name': 'steel',
    'young_modulus': 207000,
    'poisson_ratio': 0.3,
    'density': 0.1,
    }
>>> thin_plate = {
    'name': 'thin_plate',
    'sectiontype': 'solid',
    'thickness': 0.01,
    'material': 'steel',
    }
>>> P.elemProp(eltype='CPS3',section=ElemSection(section=thin_plate,material=steel))

First, a material is defined. Then a thin plate section is created, referring to that material. The last line creates a property record that will attribute this element section and an element type ‘CPS3’ to all elements.

Exporting to finite element programs

pyformex-0.8.6/pyformex/doc/html/geometry.html0000644000211500021150000002071511705104246021342 0ustar benebene00000000000000 Modeling Geometry — pyFormex v0.8.6 documentation

Table Of Contents

Previous topic

The Graphical User Interface

Next topic

The Canvas

[FSF Associate Member]

Valid XHTML 1.0 Transitional

Modeling Geometry

Warning

This document still needs to be written!

Abstract

This chapter explains the different geometrical models in pyFormex, how and when to use them, how to convert between them, how to import and export them in various formats.

Introduction

Everything is geometry

The Formex model

The Mesh model

Analytical models

pyformex-0.8.6/pyformex/doc/pyformex.10000644000211500021150000001036011677155636017624 0ustar benebene00000000000000.\" Man page generated from reStructeredText. . .TH PYFORMEX 1 "2011-12-29" "0.1" "text and X11 processing" .SH NAME pyformex \- generate and transform 3D geometry using Python scripts . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .\" TODO: authors and author with name . .SH SYNOPSIS .INDENT 0.0 .INDENT 3.5 .sp pyformex [options] [ [ file [args] ] ...] .UNINDENT .UNINDENT .SH DESCRIPTION .sp This manual page documents briefly the pyformex command. .sp pyFormex is a program for generating, transforming and manipulating large geometrical models of 3D structures by sequences of mathematical operations. Thanks to a powerful (Python based) scripting language, pyFormex is very well suited for the automated design of spatial frame structures. It provides a wide range of operations on surface meshes, like STL type triangulated surfaces. There are provisions to import medical scan images. pyFormex can also be used as a pre\- and post\-processor for Finite Element analysis programs. Finally, it might be used just for creating some nice graphics. .sp Using pyFormex, the topology of the elements and the final geometrical form can be decoupled. Often, topology is created first and then mapped onto the geometry. Through the scripting language, the user can define any sequence of transformations, built from provided or user defined functions. This way, building parametric models becomes a natural thing. .sp While pyFormex is still under development, it already provides a fairly stable scripting language and an OpenGL GUI environment for displaying and manipulating the generated structures. .SH OPTIONS .sp The pyformex command follows the usual GNU command line syntax, with long options starting with two dashes (\(aq\-\(aq). The non\-option arguments are filenames of existing pyFormex scripts, possibly followed by arguments, that will be executed by the pyFormex engine. If a script file uses arguments, it is responsible for removing the arguments from the list. .sp A summary of the most important options is included below. A complete overview is can be found from the \(aqpyformex \-h\(aq command. For a complete description of the use and operation of pyFormex, see the online documentation at \fI\%http://pyformex.org/doc\fP or the documentation included with the distribution. .INDENT 0.0 .TP .B \-\-gui . start the GUI (default if no file argument is given) .TP .B \-\-nogui . do not load the GUI (default if a file argument is given) .TP .B \-\-interactive, \-i . Go into interactive mode after processing the command line parameters. This is implied by the \-\-gui option. .TP .BI \-\-config\fB= . Read configuration settings from , if it exists. .TP .B \-\-nodefaultconfig . Skip the default site and user config files. This option can only be used in conjunction with the \-\-config option. .TP .B \-\-redirect . Redirect standard output to the message board (ignored with \-\-nogui) .TP .B \-\-debug . display debugging info to standard output .TP .B \-\-whereami . Show where the pyformex package is installed and exit .TP .B \-\-detect . Show detected helper software and exit .TP .B \-\-version . Show the program\(aqs version number and exit. .TP .B \-\-help, \-h . Show the help message and exit. .UNINDENT .SH SEE ALSO .sp \fI\%http://pyformex.org/doc/\fP .SH AUTHORS .sp pyFormex is developed by Benedict Verhegghe with the help of the other pyFormex project members: Gianluca De Santis, Francesco Iannaccone, Peter Mortier, Tomas Praet, Sofie Van Cauter, Wenxuan Zhou. .SH AUTHOR Benedict Verhegghe . This manual page was written for the Debian project (and may be used by others). .SH COPYRIGHT GPL v3 or higher .\" Generated by docutils manpage writer. .\" . pyformex-0.8.6/pyformex/doc/README0000777000211500021150000000000011703064312020030 2../../READMEustar benebene00000000000000pyformex-0.8.6/pyformex/doc/ReleaseNotes0000777000211500021150000000000011703064312023120 2../../ReleaseNotesustar benebene00000000000000pyformex-0.8.6/pyformex/doc/COPYING0000777000211500021150000000000011703064312020356 2../../COPYINGustar benebene00000000000000pyformex-0.8.6/pyformex/gui/0000755000211500021150000000000011705105304015663 5ustar benebene00000000000000pyformex-0.8.6/pyformex/gui/colors.py0000644000211500021150000001150711705104656017553 0ustar benebene00000000000000# $Id: colors.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Definition of some RGB colors and color conversion functions""" from PyQt4 import QtCore,QtGui import numpy # BV: THIS DETECTION SHOULD GO ELSEWHERE import pyformex as pf try: QtGui.QColor.setAllowX11ColorNames(True) pf.X11 = True except: print "WARNING: THIS IS NOT AN X11 WINDOW SYSTEM!" print "SOME THINGS MAY NOT WORK PROPERLY!" pf.X11 = False def GLColor(color): """Convert a color to an OpenGL RGB color. The output is a tuple of three RGB float values ranging from 0.0 to 1.0. The input can be any of the following: - a QColor - a string specifying the Xwindow name of the color - a hex string '#RGB' with 1 to 4 hexadecimal digits per color - a tuple or list of 3 integer values in the range 0..255 - a tuple or list of 3 float values in the range 0.0..1.0 Any other input may give unpredictable results. """ col = color # as of Qt4.5, QtGui.Qcolor no longer raises an error if given # erroneous input. Therefore, we check it ourselves # str, QString or QtCore.Globalcolor: convert to QColor if ( type(col) is str or type(col) is QtCore.QString or isinstance(col,QtCore.Qt.GlobalColor) ): try: col = QtGui.QColor(col) except: pass # QColor: convert to (r,g,b) tuple (0..255) if isinstance(col,QtGui.QColor): col = (col.red(),col.green(),col.blue()) # Convert to a list and check length try: col = tuple(col) if len(col) == 3: if type(col[0]) == int: # convert int values to float col = [ c/255. for c in col ] col = map(float,col) # SUCCESS ! return tuple(col) except: pass # No success: rais an error raise ValueError,"GLColor: unexpected input of type %s: %s" % (type(color),color) def colorName(color): """Return a string designation for the color. color can be anything that is accepted by GLColor. In most cases If color can not be converted, None is returned. """ try: return str(QtGui.QColor.fromRgbF(*(GLColor(color))).name()) except: return None def RGBcolor(color): """Return an RGB (0-255) tuple for an OpenGL color""" col = array(color)*255 return col.round().astype(Int) def WEBcolor(color): """Return an RGB hex string for an OpenGL color""" col = RGBcolor(color) return "#%02x%02x%02x" % tuple(col) def createColorDict(): for c in QtGui.QColor.colorNames(): col = QtGui.QColor print("Color %s = %s" % (c,colorName(c))) def closestColorName(color): """Return the closest color name.""" pass def RGBA(rgb,alpha=1.0): """Adds an alpha channel to an RGB color""" return GLColor(rgb)+(alpha,) black = (0.0, 0.0, 0.0) red = (1.0, 0.0, 0.0) green = (0.0, 1.0, 0.0) blue = (0.0, 0.0, 1.0) cyan = (0.0, 1.0, 1.0) magenta = (1.0, 0.0, 1.0) yellow = (1.0, 1.0, 0.0) white = (1.0, 1.0, 1.0) pyformex_pink = (1.0,0.2,0.4) def GREY(val,alpha=1.0): """Returns a grey OpenGL color of given intensity (0..1)""" return (val,val,val,1.0) def grey(i): return (i,i,i) lightlightgrey = grey(0.9) lightgrey = grey(0.8) mediumgrey = grey(0.7) darkgrey = grey(0.5) if __name__ == "__main__": print(GLColor(QtGui.QColor('red'))) print(GLColor(QtGui.QColor('indianred'))) print(GLColor('red')) print(GLColor(red)) print(GLColor([200,200,255])) print(GLColor([1.,1.,1.])) print(GLColor(lightgrey)) print(GLColor('#ffddff')) print(colorName(red)) print(colorName('red')) print(colorName('#ffddff')) print(colorName([1.,1.,1.])) createColorDict() pyformex-0.8.6/pyformex/gui/canvas.py0000644000211500021150000011252511705104656017527 0ustar benebene00000000000000# $Id: canvas.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """This implements an OpenGL drawing widget for painting 3D scenes.""" import pyformex as pf from coords import tand from numpy import * from OpenGL import GL,GLU from formex import length from drawable import saneColor import colors import camera import actors import decors import marks import utils from mydict import Dict def gl_pickbuffer(): "Return a list of the 2nd numbers in the openGL pick buffer." buf = GL.glRenderMode(GL.GL_RENDER) return asarray([ r[2] for r in buf ]) fill_modes = [ GL.GL_FRONT_AND_BACK, GL.GL_FRONT, GL.GL_BACK ] fill_mode = GL.GL_FRONT_AND_BACK def glFillMode(mode): global fill_mode if mode in fill_modes: fill_mode = mode def glFrontFill(): glFillMode(GL.GL_FRONT) def glBackFill(): glFillMode(GL.GL_BACK) def glBothFill(): glFillMode(GL.GL_FRONT_AND_BACK) def glFill(): GL.glPolygonMode(fill_mode,GL.GL_FILL) def glLine(): GL.glPolygonMode(GL.GL_FRONT_AND_BACK,GL.GL_LINE) def glLineSmooth(onoff): if onoff is True: GL.glEnable(GL.GL_LINE_SMOOTH) GL.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST) elif onoff is False: GL.glDisable(GL.GL_LINE_SMOOTH) def glLineStipple(factor,pattern): """Set the line stipple pattern. When drawing lines, OpenGl can use a stipple pattern. The stipple is defined by two values: a pattern (on/off) of maximum 16 bits, used on the pixel level, and a multiplier factor for each bit. If factor <= 0, the stippling is disabled. """ if factor > 0: GL.glLineStipple(factor,pattern) GL.glEnable(GL.GL_LINE_STIPPLE) else: GL.glDisable(GL.GL_LINE_STIPPLE) def glSmooth(): """Enable smooth shading""" GL.glShadeModel(GL.GL_SMOOTH) def glFlat(): """Disable smooth shading""" GL.glShadeModel(GL.GL_FLAT) def onOff(onoff): """Convert On/Off strings to a boolean""" if type(onoff) is str: return (onoff.lower() == 'on') else: if onoff: return True else: return False def glEnable(facility,onoff): """Enable/Disable an OpenGL facility, depending on onoff value facility is an OpenGL facility. onoff can be True or False to enable, resp. disable the facility, or None to leave it unchanged. """ pf.debug("%s: %s" % (facility,onoff)) if onOff(onoff): pf.debug("ENABLE") GL.glEnable(facility) else: pf.debug("DISABLE") GL.glDisable(facility) def glCulling(onoff=True): glEnable(GL.GL_CULL_FACE,onoff) def glNoCulling(): glCulling(False) def glLighting(onoff): glEnable(GL.GL_LIGHTING,onoff) def glPolygonFillMode(mode): if type(mode) is str: mode = mode.lower() if mode == 'Front and Back': glBothFill() elif mode == 'Front': glFrontFill() elif mode == 'Back': glBackFill() def glPolygonMode(mode): if type(mode) is str: mode = mode.lower() if mode == 'fill': glFill() elif mode == 'line': glLine() def glShadeModel(model): if type(model) is str: model = model.lower() if model == 'smooth': glSmooth() elif model == 'flat': glFlat() def glSettings(settings): pf.debug("GL SETTINGS: %s" % settings) glCulling(settings.get('Culling',None)) glLighting(settings.get('Lighting',None)) glShadeModel(settings.get('Shading',None)) glLineSmooth(onOff(settings.get('Line Smoothing',None))) glPolygonFillMode(settings.get('Polygon Fill',None)) glPolygonMode(settings.get('Polygon Mode',None)) pf.canvas.update() class ActorList(list): def __init__(self,canvas): self.canvas = canvas list.__init__(self) def add(self,actor): """Add an actor to an actorlist.""" self.append(actor) def delete(self,actor): """Remove an actor from an actorlist.""" if actor in self: self.remove(actor) def redraw(self): """Redraw all actors in the list. This redraws the specified actors (recreating their display list). This could e.g. be used after changing an actor's properties. """ for actor in self: actor.redraw() ############### OpenGL Lighting ################################# class Material(object): def __init__(self,name,ambient=0.2,diffuse=0.2,specular=0.9,emission=0.1,shininess=2.0): self.name = str(name) self.ambient = float(ambient) self.diffuse = float(diffuse) self.specular = float(specular) self.emission = float(emission) self.shininess = float(shininess) def setValues(self,**kargs): #print "setValues",kargs for k in kargs: #print k,kargs[k] if hasattr(self,k): #print getattr(self,k) setattr(self,k,float(kargs[k])) #print getattr(self,k) def activate(self): GL.glMaterialfv(fill_mode,GL.GL_AMBIENT,colors.GREY(self.ambient)) GL.glMaterialfv(fill_mode,GL.GL_DIFFUSE,colors.GREY(self.diffuse)) GL.glMaterialfv(fill_mode,GL.GL_SPECULAR,colors.GREY(self.specular)) GL.glMaterialfv(fill_mode,GL.GL_EMISSION,colors.GREY(self.emission)) GL.glMaterialfv(fill_mode,GL.GL_SHININESS,self.shininess) def dict(self): """Return the material light parameters as a dict""" return dict([(k,getattr(self,k)) for k in ['ambient','diffuse','specular','emission','shininess']]) def __str__(self): return """MATERIAL: %s ambient: %s diffuse: %s specular: %s emission: %s shininess: %s """ % (self.name,self.ambient,self.diffuse,self.specular,self.emission,self.shininess) def getMaterials(): mats = pf.refcfg['material'] mats.update(pf.prefcfg['material']) mats.update(pf.cfg['material']) return mats def createMaterials(): mats = getMaterials() matdb = {} for m in mats: matdb[m] = Material(m,**mats[m]) return matdb class Light(object): def __init__(self,nr,**kargs): self.light = GL.GL_LIGHT0 + (nr % GL.GL_MAX_LIGHTS) self.set(ambient=0.5,diffuse=0.5,specular=0.5,position=[0.,0.,1.,0.],enabled=False) def set(self,**kargs): for k in kargs: self.set_value(k,kargs[k]) def set_value(self,key,value): if key in [ 'ambient','diffuse','specular' ]: value = colors.GREY(value) setattr(self,key,value) def enable(self): GL.glEnable(self.light) GL.glLightfv(self.light,GL.GL_AMBIENT,self.ambient) GL.glLightfv(self.light,GL.GL_DIFFUSE,self.diffuse) GL.glLightfv(self.light,GL.GL_SPECULAR,self.specular) GL.glLightfv(self.light,GL.GL_POSITION,self.position) def disable(self): GL.glDisable(self.light) def __str__(self): return """LIGHT %s: ambient color: %s diffuse color: %s specular color: %s position: %s """ % (self.light-GL.GL_LIGHT0,self.ambient,self.diffuse,self.specular,self.position) class LightProfile(object): light_model = { 'ambient': GL.GL_AMBIENT, 'diffuse': GL.GL_DIFFUSE, 'ambient and diffuse': GL.GL_AMBIENT_AND_DIFFUSE, 'emission': GL.GL_EMISSION, 'specular': GL.GL_SPECULAR, } def __init__(self,model,ambient,lights): self.model = self.light_model[model] self.ambient = ambient self.lights = lights def activate(self): #GL.glEnable(GL.GL_LIGHTING) GL.glEnable(GL.GL_COLOR_MATERIAL) GL.glColorMaterial(fill_mode,self.model) GL.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT,colors.GREY(self.ambient)) GL.glLightModeli(GL.GL_LIGHT_MODEL_TWO_SIDE, 1) GL.glLightModeli(GL.GL_LIGHT_MODEL_LOCAL_VIEWER, 0) GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPushMatrix() GL.glLoadIdentity() for light in self.lights: light.enable() GL.glPopMatrix() ################################################################## # # The Canvas Settings # class CanvasSettings(Dict): """A collection of settings for an OpenGL Canvas. The canvas settings are a collection of settings and default values affecting the rendering in an individual viewport. There are two type of settings: - mode settings are set during the initialization of the canvas and can/should not be changed during the drawing of actors and decorations; - default settings can be used as default values but may be changed during the drawing of actors/decorations: they are reset before each individual draw instruction. Currently the following mode settings are defined: - bgmode: the viewport background mode. Should be one of 'solid', 'vertical gradient', 'horizontal gradient' - bgcolor: the viewport background color (left/top color for graded modes) - bgcolor2: right/bottom color for a grade background - slcolor: the highlight color - rendermode: the rendering mode - shading: True is smooth, False is flat - lighting: True or False The list of default settings includes: - fgcolor: the default drawing color - bkcolor: the default backface color - colormap: the default color map to be used if color is an index - bklormap: the default color map to be used if bkcolor is an index - pointsize: the default size for drawing points - marksize: the default size for drawing markers - linewidth: the default width for drawing lines Any of these values can be set in the constructor using a keyword argument. All items that are not set, will get their value from the configuration file(s). """ def __init__(self,**kargs): """Create a new set of CanvasSettings.""" Dict.__init__(self) self.reset(kargs) def reset(self,d={}): """Reset the CanvasSettings to its defaults. The default values are taken from the configuration files. An optional dictionary may be specified to override (some of) these defaults. """ self.update(pf.refcfg['canvas']) self.update(pf.prefcfg['canvas']) self.update(pf.cfg['canvas']) if d: self.update(d) def update(self,d,strict=True): """Update current values with the specified settings Returns the sanitized update values. """ ok = self.checkDict(d,strict) Dict.update(self,ok) @classmethod def checkDict(clas,dict,strict=True): """Transform a dict to acceptable settings.""" ok = {} for k,v in dict.items(): try: if k == 'bgmode': if not v in Canvas.bgmodes: raise elif k in [ 'bgcolor', 'bgcolor2', 'fgcolor', 'bkcolor', 'slcolor']: if v is not None: v = saneColor(v) elif k in ['colormap','bkcolormap']: if v is not None: v = map(saneColor,v) elif k in ['smoothshading', 'lighting', 'culling']: v = bool(v) elif k in ['linewidth', 'pointsize', 'marksize']: v = float(v) elif k == 'linestipple': v = map(int,v) elif k == 'transparency': v = max(min(float(v),1.0),0.0) elif k == 'rendermode': if not v in Canvas.rendermodes: raise elif k == 'marktype': pass else: raise ok[k] = v except: if strict: raise ValueError,"Invalid key/value for CanvasSettings: %s = %s" % (k,v) return ok def __str__(self): return utils.formatDict(self) def setMode(self): """Activate the mode canvas settings in the GL machine.""" GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) GL.glClearColor(*colors.RGBA(self.bgcolor)) def setDefault(self): """Activate the default canvas settings in the GL machine.""" GL.glColor3fv(self.fgcolor) GL.glLineWidth(self.linewidth) glLineStipple(*self.linestipple) GL.glPointSize(self.pointsize) ################################################################## # # The Canvas # def print_camera(self): print self.report() class Canvas(object): """A canvas for OpenGL rendering. The Canvas is a class holding all global data of an OpenGL scene rendering. This includes colors, line types, rendering mode. It also keeps lists of all the actors and decorations in the scene. It always has a Camera object holding import viewing parameters. And finally it stores the lighting information. It does not however contain the viewport size and position. """ rendermodes = ['wireframe','flat','flatwire','smooth','smoothwire', 'smooth_avg'] bgmodes = [ 'solid', 'vertical gradient', 'horizontal gradient' ] def __init__(self,settings={}): """Initialize an empty canvas with default settings.""" self.actors = ActorList(self) self.highlights = ActorList(self) self.annotations = ActorList(self) self.decorations = ActorList(self) self.triade = None self.background = None self.bbox = None self.resetLighting() self.setBbox() self.settings = CanvasSettings(**settings) self.mode2D = False self.rendermode = pf.cfg['render/mode'] self.lighting = pf.cfg['render/lighting'] if self.lighting not in [True,False]: self.lighting = self.rendermode.startswith('smooth') self.polygonfill = False self.avgnormals = False self.alphablend = False self.camera = None self.view_angles = camera.view_angles self.cursor = None self.focus = False pf.debug("Canvas Setting:\n%s"% self.settings) def Size(self): return self.width(),self.height() def do_lighting(self,onoff): """Toggle lights on/off.""" if onoff: self.lightprof.activate() self.material.activate() GL.glEnable(GL.GL_LIGHTING) else: GL.glDisable(GL.GL_LIGHTING) def has_lighting(self): """Return the status of the lighting.""" return GL.glIsEnabled(GL.GL_LIGHTING) def resetDefaults(self,dict={}): """Return all the settings to their default values.""" self.settings.reset(dict) self.resetLighting() ## self.resetLights() def setAmbient(self,ambient): """Set the global ambient lighting for the canvas""" self.lightprof.ambient = float(ambient) def setMaterial(self,matname): """Set the default material light properties for the canvas""" self.material = pf.GUI.materials[matname] def resetLighting(self): """Change the light parameters""" self.lightmodel = pf.cfg['render/lightmodel'] self.setMaterial(pf.cfg['render/material']) self.lightset = pf.cfg['render/lights'] lights = [ Light(int(light[-1:]),**pf.cfg['light/%s' % light]) for light in self.lightset ] self.lightprof = LightProfile(self.lightmodel,pf.cfg['render/ambient'],lights) def setRenderMode(self,mode,lighting=None): """Set the rendering mode. This sets or changes the rendermode and lighting attributes. If lighting is not specified, it is set depending on the rendermode. If the canvas has not been initialized, this merely sets the attributes self.rendermode and self.lighting. If the canvas was already initialized (it has a camera), and one of the specified settings is fdifferent from the existing, the new mode is set, the canvas is re-initialized according to the newly set mode, and everything is redrawn with the new mode. """ if mode not in Canvas.rendermodes: raise ValueError,"Invalid render mode %s" % mode if lighting not in [True,False]: lighting = mode.startswith('smooth') if mode != self.rendermode or lighting != self.lighting: self.rendermode = mode self.lighting = lighting #print "GLINIT %s %s" % (self.rendermode,self.lighting) self.glinit() #print "REDRAWALL %s %s" % (self.rendermode,self.lighting) #self.redrawAll() #print "SWITCH TO NRE RENDERMODE COMPLETED" else: #print "KEEP MODE %s %s" % (self.rendermode,self.lighting) pass def setToggle(self,attr,onoff): if onoff not in [True,False]: onoff = not getattr(self,attr) setattr(self,attr,onoff) try: func = getattr(self,'do_'+attr)#.capitalize()) func(onoff) except: pass ## def setTransparency(self,onoff): ## self.alphablend = onoff def setLighting(self,onoff): self.setToggle('lighting',onoff) ## def setLighting(self,onoff): ## self.lighting = onoff ## print "canvas.setLighting %s" % onoff ## self.glLight(self.lighting) ## def setAveragedNormals(self,onoff): ## self.avgnormals = onoff ## self.do_avgnormals(onoff) def do_avgnormals(self,onoff): change = (self.rendermode == 'smooth' and self.avgnormals) or \ (self.rendermode == 'smooth_avg' and not self.avgnormals) if change: if self.avgnormals: self.rendermode = 'smooth_avg' else: self.rendermode = 'smooth' self.actors.redraw() self.display() def setLineWidth(self,lw): """Set the linewidth for line rendering.""" self.settings.linewidth = float(lw) def setLineStipple(self,repeat,pattern): """Set the linestipple for line rendering.""" self.settings.update({'linestipple':(repeat,pattern)}) def setPointSize(self,sz): """Set the size for point drawing.""" self.settings.pointsize = float(sz) def setBgColor(self,color1,color2=None,mode='solid'): """Set the background color. If one color is specified, a solid background is set. If two colors are specified, a graded background is set and an object is created to display the background. """ # # THIS SHOULD USE self.settings.update # self.settings.bgmode = mode self.settings.bgcolor = colors.GLColor(color1) if mode == 'solid' or color2 is None: pf.debug("Clearing twocolor background") self.settings.bgmode = 'solid' self.background = None else: self.settings.bgcolor2 = colors.GLColor(color2) self.createBackground() glSmooth() glFill() self.clear() self.redrawAll() def createBackground(self): """Create the background object.""" x1,y1 = 0,0 x2,y2 = self.Size() color4 = [self.settings.bgcolor2,self.settings.bgcolor2,self.settings.bgcolor,self.settings.bgcolor] if self.settings.bgmode == 'horizontal gradient': color4 = color4[-1:] + color4[:-1] self.background = decors.Rectangle(x1,y1,x2,y2,color=color4) def setFgColor(self,color): """Set the default foreground color.""" self.settings.fgcolor = colors.GLColor(color) def setSlColor(self,color): """Set the highlight color.""" self.settings.slcolor = colors.GLColor(color) def setTriade(self,on=None,pos='lb',siz=100): """Toggle the display of the global axes on or off. If on is True, a triade of global axes is displayed, if False it is removed. The default (None) toggles between on and off. """ if on is None: on = self.triade is None pf.debug("SETTING TRIADE %s" % on) if self.triade: self.removeAnnotation(self.triade) self.triade = None if on: self.triade = decors.Triade(pos,siz) self.addAnnotation(self.triade) def initCamera(self): self.makeCurrent() # we need correct OpenGL context for camera self.camera = camera.Camera() if pf.options.testcamera: self.camera.modelview_callback = print_camera self.camera.projection_callback = print_camera def glinit(self): """Initialize the rendering machine. The rendering machine is initialized according to: - self.rendermode: one of - self.lighting """ self.setBgColor(self.settings.bgcolor,self.settings.bgcolor2,self.settings.bgmode) self.clear() GL.glClearDepth(1.0) # Enables Clearing Of The Depth Buffer GL.glEnable(GL.GL_DEPTH_TEST) # Enables Depth Testing #GL.glEnable(GL.GL_CULL_FACE) if self.rendermode == 'wireframe': if self.background: glSmooth() glFill() else: glLine() elif self.rendermode.startswith('flat'): if self.background: glSmooth() else: glFlat() glFill() elif self.rendermode.startswith('smooth'): glSmooth() glFill() else: raise RuntimeError,"Unknown rendering mode" self.setLighting(self.lighting) if self.rendermode.endswith('wire'): GL.glEnable(GL.GL_POLYGON_OFFSET_FILL) GL.glPolygonOffset(1.0,1.0) else: GL.glDisable(GL.GL_POLYGON_OFFSET_FILL) def glupdate(self): """Flush all OpenGL commands, making sure the display is updated.""" GL.glFlush() def clear(self): """Clear the canvas to the background color.""" self.settings.setMode() self.setDefaults() def setDefaults(self): """Activate the canvas settings in the GL machine.""" self.settings.setDefault() self.do_lighting(self.lighting) GL.glDepthFunc(GL.GL_LESS) def setSize (self,w,h): if h == 0: # prevent divide by zero h = 1 GL.glViewport(0, 0, w, h) self.aspect = float(w)/h self.camera.setLens(aspect=self.aspect) if self.background: # recreate the background to match the current size self.createBackground() self.display() def display(self): """(Re)display all the actors in the scene. This should e.g. be used when actors are added to the scene, or after changing camera position/orientation or lens. """ #pf.debugt("UPDATING CURRENT OPENGL CANVAS") #print "DISPLAY" self.makeCurrent() self.clear() # decorations are drawn in 2D mode self.begin_2D_drawing() if self.background: #pf.debug("Displaying background") self.background.draw(mode='smooth') if len(self.decorations) > 0: for actor in self.decorations: self.setDefaults() ## if hasattr(actor,'zoom'): ## self.zoom_2D(actor.zoom) actor.draw(canvas=self) ## if hasattr(actor,'zoom'): ## self.zoom_2D() # draw the focus rectangle if more than one viewport if len(pf.GUI.viewports.all) > 1 and pf.cfg['gui/showfocus']: if self.hasFocus(): self.draw_focus_rectangle(2) elif self.focus: self.draw_focus_rectangle(1) self.end_2D_drawing() # start 3D drawing self.camera.set3DMatrices() # draw the highlighted actors if self.highlights: for actor in self.highlights: self.setDefaults() actor.draw(canvas=self) # draw the scene actors sorted_actors = [ a for a in self.actors if not a.ontop ] + [ a for a in self.actors if a.ontop ] if self.alphablend: opaque = [ a for a in sorted_actors if not a.trans ] transp = [ a for a in sorted_actors if a.trans ] for actor in opaque: self.setDefaults() actor.draw(canvas=self) GL.glEnable (GL.GL_BLEND) GL.glDepthMask (GL.GL_FALSE) GL.glBlendFunc (GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) for actor in transp: self.setDefaults() actor.draw(canvas=self) GL.glDepthMask (GL.GL_TRUE) GL.glDisable (GL.GL_BLEND) else: for actor in sorted_actors: self.setDefaults() actor.draw(canvas=self) # annotations are decorations drawn in 3D space for actor in self.annotations: self.setDefaults() actor.draw(canvas=self) # make sure canvas is updated GL.glFlush() def zoom_2D(self,zoom=None): if zoom is None: zoom = (0,self.width(),0,self.height()) GLU.gluOrtho2D(*zoom) def begin_2D_drawing(self): """Set up the canvas for 2D drawing on top of 3D canvas. The 2D drawing operation should be ended by calling end_2D_drawing. It is assumed that you will not try to change/refresh the normal 3D drawing cycle during this operation. """ #pf.debug("Start 2D drawing") if self.mode2D: #pf.debug("WARNING: ALREADY IN 2D MODE") return GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPushMatrix() GL.glLoadIdentity() GL.glMatrixMode(GL.GL_PROJECTION) GL.glPushMatrix() GL.glLoadIdentity() self.zoom_2D() GL.glDisable(GL.GL_DEPTH_TEST) self.do_lighting(False) self.mode2D = True def end_2D_drawing(self): """Cancel the 2D drawing mode initiated by begin_2D_drawing.""" #pf.debug("End 2D drawing") if self.mode2D: GL.glEnable(GL.GL_DEPTH_TEST) GL.glMatrixMode(GL.GL_PROJECTION) GL.glPopMatrix() GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPopMatrix() self.do_lighting(self.lighting) self.mode2D = False def setBbox(self,bbox=None): """Set the bounding box of the scene you want to be visible.""" # TEST: use last actor #pf.debug("BBOX WAS: %s" % self.bbox) if bbox is None: if len(self.actors) > 0: bbox = self.actors[-1].bbox() else: bbox = [[-1.,-1.,-1.],[1.,1.,1.]] bbox = asarray(bbox) try: self.bbox = nan_to_num(bbox) except: # if bbox.any() == nan: pf.message("Invalid Bbox: %s" % bbox) #pf.debug("BBOX BECOMES: %s" % self.bbox) def addActor(self,actor): """Add a 3D actor to the 3D scene.""" self.actors.add(actor) def removeActor(self,actor): """Remove a 3D actor from the 3D scene.""" self.actors.delete(actor) #self.highlights.delete(actor) def addHighlight(self,actor): """Add a 3D actor highlight to the 3D scene.""" self.highlights.add(actor) def removeHighlight(self,actor): """Remove a 3D actor highlight from the 3D scene.""" self.highlights.delete(actor) def addAnnotation(self,actor): """Add an annotation to the 3D scene.""" self.annotations.add(actor) def removeAnnotation(self,actor): """Remove an annotation from the 3D scene.""" if actor == self.triade: pf.debug("REMOVING TRIADE") self.triade = None self.annotations.delete(actor) def addDecoration(self,actor): """Add a 2D decoration to the canvas.""" self.decorations.add(actor) def removeDecoration(self,actor): """Remove a 2D decoration from the canvas.""" self.decorations.delete(actor) def remove(self,itemlist): """Remove a list of any actor/highlights/annotation/decoration items. This will remove the items from any of the canvas lists in which the item appears. itemlist can also be a single item instead of a list. """ if not type(itemlist) in (list,tuple): itemlist = [ itemlist ] for item in itemlist: self.actors.delete(item) self.highlights.delete(item) self.annotations.delete(item) self.decorations.delete(item) def removeActors(self,actorlist=None): """Remove all actors in actorlist (default = all) from the scene.""" if actorlist == None: actorlist = self.actors[:] for actor in actorlist: self.removeActor(actor) self.setBbox() def removeHighlights(self,actorlist=None): """Remove all highlights in actorlist (default = all) from the scene.""" if actorlist == None: actorlist = self.highlights[:] for actor in actorlist: self.removeHighlight(actor) def removeAnnotations(self,actorlist=None): """Remove all annotations in actorlist (default = all) from the scene.""" if actorlist == None: actorlist = self.annotations[:] for actor in actorlist: self.removeAnnotation(actor) def removeDecorations(self,actorlist=None): """Remove all decorations in actorlist (default = all) from the scene.""" if actorlist == None: actorlist = self.decorations[:] for actor in actorlist: self.removeDecoration(actor) def removeAll(self): """Remove all actors and decorations""" self.removeActors() self.removeHighlights() self.removeAnnotations() self.removeDecorations() def redrawAll(self): """Redraw all actors in the scene.""" #print "REDRAWALL" self.actors.redraw() self.highlights.redraw() self.annotations.redraw() self.decorations.redraw() self.display() def setCamera(self,bbox=None,angles=None): """Sets the camera looking under angles at bbox. This function sets the camera angles and adjusts the zooming. The camera distance remains unchanged. If a bbox is specified, the camera will be zoomed to make the whole bbox visible. If no bbox is specified, the current scene bbox will be used. If no current bbox has been set, it will be calculated as the bbox of the whole scene. If no camera angles are given, the camera orientation is kept. angles can be a set of 3 angles, or a string """ self.makeCurrent() # go to a distance to have a good view with a 45 degree angle lens if bbox is not None: pf.debug("SETTING BBOX: %s" % self.bbox) self.setBbox(bbox) pf.debug("USING BBOX: %s" % self.bbox) X0,X1 = self.bbox center = 0.5*(X0+X1) # calculating the bounding circle: this is rather conservative self.camera.setCenter(*center) if type(angles) is str: angles = self.view_angles.get(angles) if angles is not None: try: self.camera.setAngles(angles) except: raise ValueError,'Invalid view angles specified' # Currently, we keep the default fovy/aspect # and change the camera distance to focus fovy = self.camera.fovy #pf.debug("FOVY: %s" % fovy) self.camera.setLens(fovy,self.aspect) # Default correction is sqrt(3) correction = float(pf.cfg.get('gui/autozoomfactor',1.732)) tf = tand(fovy/2.) import simple,coords bbix = simple.regularGrid(X0,X1,[1,1,1]) bbix = dot(bbix,self.camera.rot[:3,:3]) bbox = coords.Coords(bbix).bbox() dx,dy,dz = bbox[1] - bbox[0] vsize = max(dx/self.aspect,dy) offset = dz dist = (vsize/tf + offset) / correction if dist == nan or dist == inf: pf.debug("DIST: %s" % dist) return if dist <= 0.0: dist = 1.0 self.camera.setDist(dist) self.camera.setClip(0.01*dist,100.*dist) self.camera.resetArea() def project(self,x,y,z,locked=False): "Map the object coordinates (x,y,z) to window coordinates.""" locked=False if locked: model,proj,view = self.projection_matrices else: self.makeCurrent() self.camera.loadProjection() model = GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX) proj = GL.glGetDoublev(GL.GL_PROJECTION_MATRIX) view = GL.glGetIntegerv(GL.GL_VIEWPORT) winx,winy,winz = GLU.gluProject(x,y,z,model,proj,view) return winx,winy,winz return self.camera.project(x,y,z) def unProject(self,x,y,z,locked=False): "Map the window coordinates (x,y,z) to object coordinates.""" locked=False if locked: model,proj,view = self.projection_matrices else: self.makeCurrent() self.camera.loadProjection() model = GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX) proj = GL.glGetDoublev(GL.GL_PROJECTION_MATRIX) view = GL.glGetIntegerv(GL.GL_VIEWPORT) objx, objy, objz = GLU.gluUnProject(x,y,z,model,proj,view) return (objx,objy,objz) return self.camera.unProject(x,y,z) def zoom(self,f,dolly=True): """Dolly zooming. Zooms in with a factor `f` by moving the camera closer to the scene. This does noet change the camera's FOV setting. It will change the perspective view though. """ if dolly: self.camera.dolly(f) def zoomRectangle(self,x0,y0,x1,y1): """Rectangle zooming. Zooms in/out by changing the area and position of the visible part of the lens. Unlike zoom(), this does not change the perspective view. `x0,y0,x1,y1` are pixel coordinates of the lower left and upper right corners of the area of the lens that will be mapped on the canvas viewport. Specifying values that lead to smaller width/height will zoom in. """ w,h = float(self.width()),float(self.height()) self.camera.setArea(x0/w,y0/h,x1/w,y1/h) def zoomCentered(self,w,h,x=None,y=None): """Rectangle zooming with specified center. This is like zoomRectangle, but the zoom rectangle is specified by its center and size, which may be more appropriate when using off-center zooming. """ self.zoomRectangle(x-w/2,y-h/2,x+w/2,y+w/2) def zoomAll(self): """Rectangle zoom to make full scene visible. """ self.camera.resetArea() def saveBuffer(self): """Save the current OpenGL buffer""" self.save_buffer = GL.glGetIntegerv(GL.GL_DRAW_BUFFER) def showBuffer(self): """Show the saved buffer""" pass def draw_focus_rectangle(self,width=2): """Draw the focus rectangle. The specified width is HALF of the line width""" lw = width w,h = self.width(),self.height() self._focus = decors.Grid(lw,lw,w-lw,h-lw,color=colors.pyformex_pink,linewidth=2*lw) self._focus.draw() def draw_cursor(self,x,y): """draw the cursor""" if self.cursor: self.removeDecoration(self.cursor) w,h = pf.cfg.get('pick/size',(20,20)) col = pf.cfg.get('pick/color','yellow') self.cursor = decors.Grid(x-w/2,y-h/2,x+w/2,y+h/2,color=col,linewidth=1) self.addDecoration(self.cursor) def draw_rectangle(self,x,y): if self.cursor: self.removeDecoration(self.cursor) col = pf.cfg.get('pick/color','yellow') self.cursor = decors.Grid(self.statex,self.statey,x,y,color=col,linewidth=1) self.addDecoration(self.cursor) ### End pyformex-0.8.6/pyformex/gui/imagearray.py0000644000211500021150000001123311705104656020367 0ustar benebene00000000000000# $Id: imagearray.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Convert bitmap images into numpy arrays. This module contains functions to convert bitmap images into numpy arrays and vice versa. Most of this code was borrowed from the PyQwt mailing list """ from PyQt4.QtGui import QImage, QColor import numpy _bgra_rec = numpy.dtype({'b': (numpy.uint8, 0), 'g': (numpy.uint8, 1), 'r': (numpy.uint8, 2), 'a': (numpy.uint8, 3)}) def qimage2numpy(qimage): if qimage.format() == QImage.Format_Indexed8: # The colortable method does not work yet: # work around is to convert to full color qimage = qimage.convertToFormat(QImage.Format_ARGB32) if qimage.format() in (QImage.Format_ARGB32_Premultiplied, QImage.Format_ARGB32, QImage.Format_RGB32): dtype = _bgra_rec buf = qimage.bits().asstring(qimage.numBytes()) ar = numpy.frombuffer(buf, dtype) h,w = qimage.height(),qimage.width() return ar.reshape(h,w),None elif qimage.format() == QImage.Format_Indexed8: ncolors = qimage.numColors() print("Number of colors: %s" % ncolors) colortable = qimage.colorTable() print(colortable) colortable = numpy.array(colortable) print(colortable.dtype) print(colortable.size) dtype = numpy.uint8 buf = qimage.bits().asstring(qimage.numBytes()) ar = numpy.frombuffer(buf, dtype) h,w = qimage.height(),qimage.width() if w*h != ar.size: print("!! Size of image (%s) does not match dimensions: %s x %s = %s" % (ar.size,w,h,w*h)) ar = ar[:w*h] print(ar.shape) print(ar.dtype) print(ar.shape) print(ar) return ar.reshape(h,w),colortable else: raise ValueError("qimage2numpy only supports 32bit and 8bit images") def numpy2qimage(array): if numpy.ndim(array) == 2: return gray2qimage(array) elif numpy.ndim(array) == 3: return rgb2qimage(array) raise ValueError("can only convert 2D or 3D arrays") def gray2qimage(gray): """Convert the 2D numpy array `gray` into a 8-bit QImage with a gray colormap. The first dimension represents the vertical image axis.""" if len(gray.shape) != 2: raise ValueError("gray2QImage can only convert 2D arrays") gray = numpy.require(gray, numpy.uint8, 'C') h, w = gray.shape result = QImage(gray.data, w, h, QImage.Format_Indexed8) result.ndarray = gray for i in range(256): result.setColor(i, QColor(i, i, i).rgb()) return result def rgb2qimage(rgb): """Convert the 3D numpy array `rgb` into a 32-bit QImage. `rgb` must have three dimensions with the vertical, horizontal and RGB image axes.""" if len(rgb.shape) != 3: raise ValueError("rgb2QImage can expects the first (or last) dimension to contain exactly three (R,G,B) channels") if rgb.shape[2] != 3: raise ValueError("rgb2QImage can only convert 3D arrays") h, w, channels = rgb.shape # Qt expects 32bit BGRA data for color images: bgra = numpy.empty((h, w, 4), numpy.uint8, 'C') bgra[...,0] = rgb[...,2] bgra[...,1] = rgb[...,1] bgra[...,2] = rgb[...,0] bgra[...,3].fill(255) result = QImage(bgra.data, w, h, QImage.Format_RGB32) result.ndarray = bgra return result # End pyformex-0.8.6/pyformex/gui/toolbar.py0000644000211500021150000002741311705104656017717 0ustar benebene00000000000000# $Id: toolbar.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Toolbars for the pyFormex GUI. This module defines the functions for creating the pyFormex window toolbars. """ import pyformex as pf import os from PyQt4 import QtCore, QtGui import widgets import draw import utils ################### Script action toolbar ########### def addActionButtons(toolbar): """Add the script action buttons to the toolbar.""" action = {} buttons = [ ( "Play", "next", draw.play, False ), ( "Step", "nextstop", draw.step, False ), ( "Continue", "ff", draw.fforward, False ), # ( "Stop", "stop", draw.stopatbreakpt, False ), # ( "Stop", "stop", draw.exit, False ), ( "Stop", "stop", draw.raiseExit, False ), ] for b in buttons: icon = QtGui.QIcon(QtGui.QPixmap(utils.findIcon(b[1]))) a = toolbar.addAction(icon,b[0],b[2]) a.setEnabled(b[3]) action[b[0]] = a return action ################### General Button Functions ########### def addButton(toolbar,tooltip,icon,func,repeat=False,toggle=False,checked=False,icon0=None): """Add a button to a toolbar. - `toolbar`: the toolbar where the button will be added - `tooltip`: the text to appears as tooltip - `icon`: name of the icon to be displayed on the button, - `func`: function to be called when the button is pressed, - `repeat`: if True, the `func` will repeatedly be called if button is held down. - `toggle`: if True, the button is a toggle and stays in depressed state until pressed again. - `checked`: initial state for a toggle buton. - `icon1`: for a toggle button, icon to display when button is not checked. """ iconset = QtGui.QIcon() icon_on = QtGui.QPixmap(utils.findIcon(icon)) iconset.addPixmap(icon_on,QtGui.QIcon.Normal,QtGui.QIcon.On) if toggle and icon0: icon_off = QtGui.QPixmap(utils.findIcon(icon0)) iconset.addPixmap(icon_off,QtGui.QIcon.Normal,QtGui.QIcon.Off) a = toolbar.addAction(iconset,tooltip,func) b = toolbar.widgetForAction(a) if repeat: b.setAutoRepeat(True) b.setAutoRepeatDelay(500) QtCore.QObject.connect(b,QtCore.SIGNAL("clicked()"),a,QtCore.SLOT("trigger()")) if toggle: b.setCheckable(True) b.connect(b,QtCore.SIGNAL("clicked()"),QtCore.SLOT("toggle()")) b.setChecked(checked) b.setToolTip(tooltip) return b def removeButton(toolbar,button): """Remove a button from a toolbar.""" toolbar.removeAction(button) ################# Camera action toolbar ############### def addCameraButtons(toolbar): """Add the camera buttons to a toolbar.""" # The buttons have the following fields: # 0 : tooltip # 1 : icon # 2 : function # optional: # 3 : REPEAT (default True) import cameraMenu buttons = [ [ "Rotate left", "rotleft", cameraMenu.rotLeft ], [ "Rotate right", "rotright", cameraMenu.rotRight ], [ "Rotate up", "rotup", cameraMenu.rotUp ], [ "Rotate down", "rotdown", cameraMenu.rotDown ], [ "Twist left", "twistleft", cameraMenu.twistLeft ], [ "Twist right", "twistright", cameraMenu.twistRight ], [ "Translate left", "left", cameraMenu.panLeft ], [ "Translate right", "right", cameraMenu.panRight ], [ "Translate down", "down", cameraMenu.panDown ], [ "Translate up", "up", cameraMenu.panUp ], [ "Zoom Out", "zoomout", cameraMenu.dollyOut ], [ "Zoom In", "zoomin", cameraMenu.dollyIn ], [ "Zoom Rectangle", "zoomrect", cameraMenu.zoomRectangle, False ], [ "Zoom All", "zoomall", cameraMenu.zoomAll, False ], ] for but in buttons: icon = QtGui.QIcon(QtGui.QPixmap(utils.findIcon(but[1]))) a = toolbar.addAction(icon,but[0],but[2]) b = toolbar.children()[-1] # Get the QToolButton for the last action if len(but) < 4 or but[3]: b.setAutoRepeat(True) b.setAutoRepeatDelay(500) QtCore.QObject.connect(b,QtCore.SIGNAL("released()"),a,QtCore.SLOT("trigger()")) if len(but) >= 5: b.setCheckable(but[4]) b.connect(b,QtCore.SIGNAL("released()"),QtCore.SLOT("toggle()")) b.setToolTip(but[0]) ####################################################################### # Viewport Toggle buttons # ########################### class Toggle(object): def __init__(self,state=False): self.state = state def toggle(self,onoff=None): if onoff is None: onoff = not self.state self.state = onoff class ViewportToggleButton(object): def __init__(self,toolbar,tooltip,icon,func,attr,checked=False,icon0=None): self.button = addButton(toolbar,tooltip,icon,func,toggle=True,icon0=icon0) self.attr = attr def updateButton(self): """Update the button to current viewport state.""" vp = pf.GUI.viewports.current if vp == pf.canvas: self.setChecked(getattr(vp,self.attr)) pf.GUI.processEvents() def toggle(self,attr,state=None): """Update the corresponding viewport attribute. This does not update the button state. """ vp = pf.GUI.viewports.current vp.setToggle(attr,state) vp.update() pf.GUI.processEvents() def toggleButton(attr,state=None): """Update the corresponding viewport attribute. This does not update the button state. """ vp = pf.GUI.viewports.current vp.setToggle(attr,state) vp.update() pf.GUI.processEvents() def updateButton(button,attr): """Update the button to correct state.""" vp = pf.GUI.viewports.current pf.debug("VP %s / %s" % (vp,pf.canvas)) if vp == pf.canvas: button.setChecked(getattr(vp,attr)) pf.GUI.processEvents() ################# Transparency Button ############### transparency_button = None # the toggle transparency button def addTransparencyButton(toolbar): global transparency_button transparency_button = addButton(toolbar,'Toggle Transparent Mode', 'transparent',toggleTransparency, toggle=True) def toggleTransparency(state=None): toggleButton('alphablend',state) def updateTransparencyButton(): """Update the transparency button to correct state.""" updateButton(transparency_button,'alphablend') ################# Lights Button ############### light_button = None def addLightButton(toolbar): global light_button light_button = addButton(toolbar,'Toggle Lights', 'lamp-on',toggleLight,icon0='lamp', toggle=True,checked=True) def toggleLight(state=None): toggleButton('lighting',state) def updateLightButton(): """Update the light button to correct state.""" updateButton(light_button,'lighting') ################# Normals Button ############### normals_button = None def addNormalsButton(toolbar): global normals_button normals_button = addButton(toolbar,'Toggle Normals', 'normals-avg',toggleNormals,icon0='normals-ind', toggle=True,checked=False) def toggleNormals(state=None): toggleButton('avgnormals',state) def updateNormalsButton(state=True): """Update the normals button to correct state.""" updateButton(normals_button,'avgnormals') ################# Perspective Button ############### perspective_button = None # the toggle perspective button def togglePerspective(mode=None): # Called by the button, not by user vp = pf.GUI.viewports.current if mode is None: mode = not vp.camera.perspective vp.camera.setPerspective(mode) vp.display() vp.update() pf.GUI.processEvents() def addPerspectiveButton(toolbar): global perspective_button perspective_button = addButton(toolbar,'Toggle Perspective/Projective Mode', 'perspect',togglePerspective, toggle=True,icon0='project',checked=True) def updatePerspectiveButton(): """Update the normals button to correct state.""" #updateButton(perspective_button,'avgnormals') vp = pf.GUI.viewports.current if vp == pf.canvas: perspective_button.setChecked(vp.camera.perspective) pf.GUI.processEvents() def setPerspective(): togglePerspective(True) updatePerspectiveButton() def setProjection(): togglePerspective(False) updatePerspectiveButton() ################# Shrink Button ############### # # The shrink button currently does not redraw, it only sets the default # shrink factor and clears the viewport # shrink_button = None # the toggle shrink button def toggleShrink(): # Called by the button mode = draw.DrawOptions.get('shrink',None) if mode is None: mode = 0.8 else: mode = None draw.shrink(mode) def addShrinkButton(toolbar): global shrink_button shrink_button = addButton(toolbar,'Toggle Shrink Mode', 'shrink',toggleShrink, toggle=True) def setShrink(mode): draw.shrink(mode) if shrink_button: shrink_button.setChecked(mode != None) ################# Timeout Button ############### timeout_button = None # the timeout toggle button def toggleTimeout(onoff=None): if onoff is None: onoff = widgets.input_timeout < 0 if onoff: timeout = pf.cfg.get('gui/timeoutvalue',-1) else: timeout = -1 widgets.setInputTimeout(timeout) onoff = widgets.input_timeout > 0 if onoff: # THIS SUSPENDS ALL WAITING! WE SHOULD IMPLEMENT A TIMEOUT! # BY FORCING ALL INDEFINITE PAUSES TO A WAIT TIME EQUAL TO # WIDGET INPUT TIMEOUT pf.debug("FREEING the draw lock") pf.GUI.drawlock.free() else: pf.debug("ALLOWING the draw lock") pf.GUI.drawlock.allow() return onoff def addTimeoutButton(toolbar): """Add or remove the timeout button,depending on cfg.""" global timeout_button if pf.cfg['gui/timeoutbutton']: if timeout_button is None: timeout_button = addButton(toolbar,'Toggle Timeout','clock',toggleTimeout,toggle=True,checked=False) else: if timeout_button is not None: removeButton(toolbar,timeout_button) timeout_button = None def timeout(onoff=None): """Programmatically toggle the timeout button""" if timeout_button is not None: timeout_button.setChecked(toggleTimeout(onoff)) # End pyformex-0.8.6/pyformex/gui/guifunc.py0000644000211500021150000000534111705104656017711 0ustar benebene00000000000000# $Id: guifunc.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """GUI support functions. This module defines a collection of functions which are the equivalent of functions defined in the draw module, but are executed in the viewport with the current GUI focus, instead of the script viewport. """ import pyformex as pf import draw ######## decorator function ############# def viewport_function(func): """Perform a function on the current GUI viewport. This is a decorator function executing a function on the current GUI viewport instead of on the current script viewport. """ draw_func = getattr(draw,func.__name__) def newf(*args,**kargs): """Performs the draw.func on the current GUI viewport""" #print "SAVED script canvas %s" % pf.canvas save = pf.canvas pf.canvas = pf.GUI.viewports.current #print "SET script canvas %s" % pf.canvas draw_func(*args,**kargs) pf.canvas = save #print "RESTORED script canvas %s" % pf.canvas newf.__name__ = func.__name__ newf.__doc__ = draw_func.__doc__ return newf @viewport_function def renderMode(*args,**kargs): pass @viewport_function def zoomAll(*args,**kargs): pass def inGUIVP(func,*args,**kargs): """Execute a draw function in the current GUI viewport.""" draw_func = getattr(draw,func.__name__) #print "inGUI SAVED script canvas %s" % pf.canvas save = pf.canvas pf.canvas = pf.GUI.viewports.current #print "inGUI SET script canvas %s" % pf.canvas draw_func(*args,**kargs) pf.canvas = save #print "inGUI RESTORED script canvas %s" % pf.canvas # End pyformex-0.8.6/pyformex/gui/widgets.py0000644000211500021150000031222311705104656017717 0ustar benebene00000000000000# $Id: widgets.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A collection of custom widgets used in the pyFormex GUI The widgets in this module were primarily created in function of the pyFormex GUI. The user can apply them to change the GUI or to add interactive widgets to his scripts. Of course he can also use all the Qt widgets directly. """ import os,types from PyQt4 import QtCore, QtGui import pyformex as pf import colors import odict,mydict,olist import utils import warnings # timeout value for all widgets providing timeout feature # (currently: InputDialog, MessageBox) input_timeout = -1 # default timeout value : -1 means no timeout def setInputTimeout(timeout): global input_timeout input_timeout = timeout # result values for dialogs ACCEPTED = QtGui.QDialog.Accepted REJECTED = QtGui.QDialog.Rejected TIMEOUT = -1 # the return value if a widget timed out # slots Accept = QtCore.SLOT("accept()") Reject = QtCore.SLOT("reject()") # QT List selection mode selection_mode = { None: QtGui.QAbstractItemView.NoSelection, 'single': QtGui.QAbstractItemView.SingleSelection, 'multi': QtGui.QAbstractItemView.MultiSelection, 'contiguous': QtGui.QAbstractItemView.ContiguousSelection, 'extended': QtGui.QAbstractItemView.ExtendedSelection, 'checked': QtGui.QAbstractItemView.SingleSelection, } # icons def standardIcon(label): try: icon = ['noicon','info','warning','error','question'].index(label) return QtGui.QMessageBox.standardIcon(icon) except: return label def maxSize(): """Return the maximum widget size. The maximum widget size is the (available) screen size. This may be smaller than the physical screen size (e.g. excluding docking panels). """ rect = pf.app.desktop().availableGeometry() maxh,maxw = rect.width(),rect.height() return maxh,maxw def addTimeOut(widget,timeout=None,timeoutfunc=None): """Add a timeout to a widget. - `timeoutfunc` is a callable. If None it will be set to the widget's `timeout` method if one exists. - `timeout` is a float value. If None, it will be set to to the global `input_timeout`. If timeout is positive, a timer will be installed into the widget which will call the `timeoutfunc` after `timeout` seconds have elapsed. The `timeoutfunc` can be any callable, but usually will emit a signal to make the widget accept or reject the input. The timeoutfunc will not be called is if the widget is destructed before the timer has finished. """ if timeout is None: timeout = input_timeout if timeoutfunc is None and hasattr(widget,'timeout'): timeoutfunc = widget.timeout try: timeout = float(timeout) if timeout >= 0.0: pf.debug("ADDING TIMEOUT %s,%s" % (timeout,timeoutfunc)) timer = QtCore.QTimer() if type(timeoutfunc) is str: timer.connect(timer,QtCore.SIGNAL("timeout()"),widget,QtCore.SLOT(timeoutfunc)) else: timer.connect(timer,QtCore.SIGNAL("timeout()"),timeoutfunc) timer.setSingleShot(True) timeout = int(1000*timeout) timer.start(timeout) widget.timer = timer # make sure this timer stays alive pf.debug("TIMER STARTED") except: raise ValueError,"Could not start the timeout timer" def setExpanding(w): freePol = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.MinimumExpanding) w.setSizePolicy(freePol) w.adjustSize() ##################################################################### ########### General Input Dialog #################################### ##################################################################### class InputItem(QtGui.QWidget): """A single input item. This is the base class for widgets holding a single input item. A single input item is any item that is treated as a unit and refered to by a single name. This base class is rarely used directly. Most of the components of an InputDialog are subclasses of hereof, each specialized in some form of input data or representation. There is e.g. an InputInteger class to input an integer number and an InputString for the input of a string. The base class groups the functionality that is common to the different input widgets. The InputItem widget holds a horizontal layout box (QHBoxLayout) to group its its components. In most cases there are just two components: a label with the name of the field, and the actual input field. Other components, such as buttons or sliders, may be added. This is often done in subclasses. The constructor has one required argument: `name`. Other (optional) positional parameters are passed to the QtGui.QWidget constructor. The remaining keyword parameters are options that somehow change the default behavior of the InputItem class. Parameters: - `name`: the name used to identify the item. It should be unique for all InputItems in the same InputDialog. It will be used as a key in the dictionary that returns all the input values in the dialog. It will also be used as the label to display in front of the input field, in case no `text` value was specified. - `text`: if specified, this text will be displayed in the label in front of the input field. This allows for showing descriptive texts for the input fields in the dialog, while keeping short and simple names for the items in the programming. `text` can be set to an empty string to suppress the creation of a label in front of the input field. This is useful if the input field widget itself already provides a label (see e.g. InputBool). `text` can also be a QtGui.QPixmap, allowing for icons to be used as labels. - `buttons`: a list of (label,function) tuples. For each tuple a button will be added after the input field. The button displays the text and when pressed, the specified function will be executed. The function takes no arguments. - `data`: any extra data that you want to be stored into the widget. These data are not displayed, but can be useful in the functioning of the widget. - `enabled`: boolean. If False, the InputItem will not be enabled, meaning that the user can not enter any values there. Disabled fields are usually displayed in a greyed-out fashion. - `readonly`: boolean. If True, the data are read-only and can not be changed by the user. Unlike disabled items, they are displayed in a normal fashion. - `tooltip`: A descriptive text which is only shown when the user pauses the cursor for some time on the widget. It can be used to give more comprehensive explanation to first time users. Subclasses should have an ``__init__()`` method which first constructs a proper widget for the input field, and stores it in the attribute ``self.input``. Then the baseclass should be properly initialized, passing any optional parameters:: self.input = SomeInputWidget() InputItem.__init__(self,name,*args,**kargs) Subclasses should also override the following default methods of the InputItem base class: - text(): if the subclass calls the superclass __init__() method with a value ``text=''``. This method should return the value of the displayed text. - value(): if the value of the input field is not given by ``self.input.text()``, i.e. in most cases. This method should return the value of the input field. - setValue(val): always, unless the field is readonly. This method should change the value of the input widget to the specified value. Subclasses are allowed to NOT have a ``self.input`` attribute, IFF they redefine both the value() and the setValue() methods. Subclasses can set validators on the input, like:: self.input.setValidator(QtGui.QIntValidator(self.input)) Subclasses can define a show() method e.g. to select the data in the input field on display of the dialog. """ def __init__(self,name,*args,**kargs): """Create a widget with a horizontal box layout""" QtGui.QWidget.__init__(self,*args) layout = QtGui.QHBoxLayout() #layout.setSpacing(0) layout.setMargin(0) self.setLayout(layout) self.key = str(name) if 'text' in kargs: text = kargs['text'] else: text = self.key if text: self.label = QtGui.QLabel() text = standardIcon(text) if isinstance(text,QtGui.QPixmap): self.label.setPixmap(text) else: self.label.setText(text) if 'b' in kargs.get('stretch',''): layout.addStretch() layout.addWidget(self.label) if 'a' in kargs.get('stretch',''): print 'test' layout.addStretch() if 'data' in kargs: self.data = kargs['data'] if 'enabled' in kargs: self.setEnabled(kargs['enabled']) if 'readonly' in kargs: try: self.input.setReadOnly(kargs['readonly']) except: print "Can not set readonly: %s,%s" % (name,kargs) if 'width' in kargs: try: print 'SETTING WIDTH',self.input self.input.setMinimumWidth(kargs['width']) except: pass if 'tooltip' in kargs: self.setToolTip(kargs['tooltip']) ## if hasattr(self,'label'): ## self.label.setToolTip(kargs['tooltip']) ## try: ## self.input.setToolTip(kargs['tooltip']) ## except: ## pass if 'buttons' in kargs and kargs['buttons']: #print kargs self.buttons = dialogButtons(self,kargs['buttons']) layout.addItem(self.buttons) def name(self): """Return the name of the InputItem.""" return self.key def text(self): """Return the displayed text of the InputItem.""" if hasattr(self,'label'): return str(self.label.text()) else: return self.key def value(self): """Return the widget's value.""" return str(self.input.text()) def setValue(self,val): """Change the widget's value.""" self.input.setText(str(val)) class InputInfo(InputItem): """An unchangeable input field with a label in front. It is just like an InputString, but the text can not be edited. The value should be a simple string without newlines. There are no specific options. """ def __init__(self,name,value,*args,**kargs): """Creates the input item.""" self.input = QtGui.QLineEdit(str(value)) self.input.setReadOnly(True) InputItem.__init__(self,name,*args,**kargs) self._value_ = value if self._value_ is not None: self.layout().insertWidget(1,self.input) def value(self): """Return the widget's value.""" return self._value_ class InputLabel(InputItem): """An unchangeable information field. The value is displayed as a string, but may contain more complex texts. By default, the text format will be guessed to be either plain text, ReStructuredText ot html. Specify plain=True to display in plain text. """ def __init__(self,name,value,*args,**kargs): """Create the input item.""" self._plain = kargs.get('plain',False) self.input = QtGui.QLabel() maxw,maxh = maxSize() self.input.setMaximumSize(0.6*maxw,0.6*maxh) self.input.setMinimumSize(0.2*maxw,0.2*maxh) setExpanding(self.input) InputItem.__init__(self,name,*args,**kargs) self.setValue(value) self.layout().insertWidget(1,self.input) # self.setSize() def setValue(self,val): """Change the widget's value.""" val = str(val) if self._plain: self.input.setText(val) self.input.setWordWrap(False) else: updateText(self.input,val) self.input.setWordWrap(True) ## def layoutMinimumWidth(self): ## # self.input.activate() ## return self.totalMinimumSize().width() ## def setSize(self): ## maxw,maxh = maxSize() ## maxw -= 40 ## self.input.setWordWrap(False) # makes the label return min size ## width = self.layoutMinimumWidth() ## #print "min size: %s" % width ## self.input.setWordWrap(True) class InputString(InputItem): """A string input field with a label in front. If the type of value is not a string, the input string will be eval'ed before returning. Options: - `max`: the maximum number of characters in the string. """ def __init__(self,name,value,max=None,*args,**kargs): """Creates the input item.""" self.input = QtGui.QLineEdit(str(value)) InputItem.__init__(self,name,*args,**kargs) if max>0: self.input.setMaxLength(max) self._is_string_ = type(value) == str self.layout().insertWidget(1,self.input) def show(self): """Select all text on first display.""" InputItem.show(self,*args) self.input.selectAll() def value(self): """Return the widget's value.""" s = str(self.input.text()) if self._is_string_: return s else: return eval(s) class InputText(InputItem): """A scrollable text input field with a label in front. By default, the text format will be guessed to be either plain text, ReStructuredText ot html. Specify plain=True to display in plain text. If the type of value is not a string, the input text will be eval'ed before returning. """ def __init__(self,name,value,*args,**kargs): """Creates the input item.""" self._is_string_ = type(value) == str self._plain = kargs.get('plain',False) self.input = QtGui.QTextEdit() # maxw,maxh = maxSize() # self.input.setMaximumSize(0.6*maxw,0.6*maxh) # self.input.setMinimumSize(0.2*maxw,0.2*maxh) setExpanding(self.input) InputItem.__init__(self,name,*args,**kargs) self.setValue(value) self.layout().insertWidget(1,self.input) if 'font' in kargs: try: self.setFont(QtGui.QFont(kargs['font'])) except: pass if 'size' in kargs: self.size = kargs['size'] def sizeHint(self): if not hasattr(self,'size'): size = QtGui.QTextEdit.sizeHint(self.input) else: width,height = self.size docsize = self.input.document().size().toSize() #print "docsize = %s" % docsize font = self.input.font() if width < 0: #print "Pixelsize = %s" % font.pixelSize() #print "Pointsize = %s" % font.pointSize() width = max(80 * font.pixelSize(), 50* font.pointSize()) #width = docsize.width() + (self.input.width() - self.input.viewport().width()) if height < 0: height = docsize.height() + (self.input.height() - self.input.viewport().height()) height = max(height, 0.75*width) size = QtCore.QSize(width,height) #print "newsize = %s" % size return size def show(self): """Select all text on first display.""" InputItem.show(self,*args) self.input.selectAll() def value(self): """Return the widget's value.""" s = str(self.input.toPlainText()) if self._is_string_: return s else: return eval(s) def setValue(self,val): """Change the widget's value.""" val = str(val) if self._plain: self.input.setPlainText(val) ## self.input.setLineWrapMode(QtGui.QTextEdit.FixedColumnWidth) ## self.input.setLineWrapColumnOrWidth(200) else: updateText(self.input,val) ## self.input.setLineWrapMode(QtGui.QTextEdit.FixedPixelWidth) ## self.input.setLineWrapColumnOrWidth(600) self.input.adjustSize() class InputBool(InputItem): """A boolean input item. Creates a new checkbox for the input of a boolean value. Displays the name next to a checkbox, which will initially be set if value evaluates to True. (Does not use the label) The value is either True or False,depending on the setting of the checkbox. """ def __init__(self,name,value,*args,**kargs): """Creates the input item.""" if 'text' in kargs: text = kargs['text'] else: text = str(name) kargs['text'] = '' # Force no label self.input = QtGui.QCheckBox(text) InputItem.__init__(self,name,*args,**kargs) self.setValue(value) self.layout().insertWidget(1,self.input) def text(self): """Return the displayed text.""" return str(self.input.text()) def value(self): """Return the widget's value.""" return self.input.checkState() == QtCore.Qt.Checked def setValue(self,val): """Change the widget's value.""" if val: self.input.setCheckState(QtCore.Qt.Checked) else: self.input.setCheckState(QtCore.Qt.Unchecked) class MyListWidget(QtGui.QListWidget): def __init__(self): QtGui.QListWidget.__init__(self) def allItems(self): return [ self.item(i) for i in range(self.count()) ] def fixSize(self): w = 0 h = 10 # margin for i in self.allItems(): r = self.visualItemRect(i) h += r.height() w = max(w,r.width()) #print "Need total height of %s" % h self.setFixedSize(w,h) class InputList(InputItem): """A list selection InputItem. A list selection is a widget allowing the selection of zero, one or more items from a list. choices is a list/tuple of possible values. default is the initial/default list of selected items. Values in default that are not in the choices list, are ignored. If default is None or an empty list, nothing is selected initially. By default, the user can select multiple items and the return value is a list of all currently slected items. If single is True, only a single item can be selected. If check is True, all items have a checkbox and only the checked items are returned. This option sets single==False. """ def __init__(self,name,default=[],choices=[],sort=False,single=False,check=False,fast_sel=False,*args,**kargs): """Create the listwidget.""" if len(choices) == 0: raise ValueError,"List input expected choices!" self._choices_ = [ str(s) for s in choices ] self.input = MyListWidget() if fast_sel: but = [('Select All',self.setAll),('Deselect All',self.setNone)] if 'buttons' in kargs and kargs['buttons']: kargs['buttons'].extend(but) else: kargs['buttons'] = but InputItem.__init__(self,name,*args,**kargs) self.input.addItems(self._choices_) if sort: self.input.sortItems() mode = 'extended' self._check_ = check if check: mode = None single = False if single: mode = 'single' self.input.setSelectionMode(selection_mode[mode]) self.setValue(default) #self.input.setSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Minimum) self.input.fixSize() self.input.updateGeometry() self.layout().insertWidget(1,self.input) self.updateGeometry() #self.input.setSizeHint(QtCore.QSize(self.input.width(),10)) def setSelected(self,selected,flag=True): """Mark the specified items as selected or not.""" for s in selected: for i in self.input.findItems(s,QtCore.Qt.MatchExactly): i.setSelected(flag) def setChecked(self,selected,flag=True): """Mark the specified items as checked or not.""" if flag: qtflag = QtCore.Qt.Checked else: qtflag = QtCore.Qt.Unchecked for s in selected: for i in self.input.findItems(s,QtCore.Qt.MatchExactly): i.setCheckState(qtflag) def getSelected(self): res = [i.text() for i in self.input.selectedItems()] return map(str,res) def getChecked(self): res = [ i.text() for i in self.input.allItems() if i.checkState()==QtCore.Qt.Checked ] return map(str,res) def value(self): """Return the widget's value.""" if self._check_: f = self.getChecked else: f = self.getSelected return f() def setValue(self,val): """Change the widget's value.""" if self._check_: f = self.setChecked else: f = self.setSelected f(val,True) f(olist.difference(self._choices_,val),False) def setAll(self): """Mark all items as selected/checked.""" self.setValue(self._choices_) def setNone(self): """Mark all items as not selected/checked.""" self.setValue([]) class InputCombo(InputItem): """A combobox InputItem. A combobox is a widget allowing the selection of an item from a drop down list. choices is a list/tuple of possible values. default is the initial/default choice. If default is not in the choices list, it is prepended. If default is None, the first item of choices is taken as the default. The choices are presented to the user as a combobox, which will initially be set to the default value. An optional `onselect` function may be specified, which will be called whenever the current selection changes. """ def __init__(self,name,default,choices=[],onselect=None,func=None,*args,**kargs): """Create the combobox.""" if len(choices) == 0: raise ValueError,"Selection expected choices!" if default is None: default = choices[0] elif default not in choices: choices[0:0] = [ default ] self._choices_ = [ str(s) for s in choices ] self.input = QtGui.QComboBox() InputItem.__init__(self,name,*args,**kargs) self.input.addItems(self._choices_) if callable(onselect): self.connect(self.input,QtCore.SIGNAL("currentIndexChanged(const QString &)"),onselect) if callable(func): self.connect(self.input,QtCore.SIGNAL("activated(int)"),func) self.setValue(default) self.layout().insertWidget(1,self.input) def value(self): """Return the widget's value.""" return str(self.input.currentText()) def setValue(self,val): """Change the widget's value.""" val = str(val) if val in self._choices_: self.input.setCurrentIndex(self._choices_.index(val)) def setIndex(self,i): self.input.setCurrentIndex(i) class InputRadio(InputItem): """A radiobuttons InputItem. Radio buttons are a set of buttons used to select a value from a list. choices is a list/tuple of possible values. default is the initial/default choice. If default is not in the choices list, it is prepended. If default is None, the first item of choices is taken as the default. The choices are presented to the user as a hbox with radio buttons, of which the default will initially be pressed. If direction == 'v', the options are in a vbox. """ def __init__(self,name,default,choices=[],direction='h',*args,**kargs): """Creates the radiobuttons.""" if default is None: default = choices[0] elif default not in choices: choices[0:0] = [ default ] self.input = QtGui.QGroupBox() InputItem.__init__(self,name,*args,**kargs) if direction == 'v': self.hbox = QtGui.QVBoxLayout() self.hbox.setContentsMargins(0,10,0,10) else: self.hbox = QtGui.QHBoxLayout() self.hbox.setContentsMargins(10,0,10,0) self.rb = [] self.hbox.addStretch(1) for v in choices: rb = QtGui.QRadioButton(v) self.hbox.addWidget(rb) self.rb.append(rb) self.rb[choices.index(default)].setChecked(True) self.input.setLayout(self.hbox) self.layout().insertWidget(1,self.input) def value(self): """Return the widget's value.""" for rb in self.rb: if rb.isChecked(): return str(rb.text()) return '' def setValue(self,val): """Change the widget's value.""" val = str(val) for rb in self.rb: if rb.text() == val: rb.setChecked(True) break class InputPush(InputItem): """A pushbuttons InputItem. Creates pushbuttons for the selection of a value from a list. choices is a list/tuple of possible values. default is the initial/default choice. If default is not in the choices list, it is prepended. If default is None, the first item of choices is taken as the default. The choices are presented to the user as a hbox with radio buttons, of which the default will initially be selected. If direction == 'v', the options are in a vbox. """ def __init__(self,name,default=None,choices=[],direction='h',*args,**kargs): """Create the pushbuttons""" if default is None: default = choices[0] elif default not in choices: choices[0:0] = [ default ] self.input = QtGui.QGroupBox() InputItem.__init__(self,name,*args,**kargs) self.input.setFlat(True) if direction == 'v': self.hbox = QtGui.QVBoxLayout() self.hbox.setContentsMargins(0,10,0,10) else: self.hbox = QtGui.QHBoxLayout() self.hbox.setContentsMargins(10,5,10,5) self.hbox.setSpacing(0) self.hbox.setMargin(0) self.rb = [] for v in choices: rb = QtGui.QPushButton(v) self.hbox.addWidget(rb) self.rb.append(rb) self.rb[choices.index(default)].setDown(True) self.input.setLayout(self.hbox) self.layout().insertWidget(1,self.input) def setText(self,text,index=0): """Change the text on button index.""" self.rb[index].setText(text) def setIcon(self,icon,index=0): """Change the icon on button index.""" self.rb[index].setIcon(icon) def value(self): """Return the widget's value.""" for rb in self.rb: # if rb.isChecked(): if rb.isDown(): return str(rb.text()) return '' def setValue(self,val): """Change the widget's value.""" val = str(val) for rb in self.rb: rb.setChecked(rb.text() == val) rb.setDown(rb.text() == val) class InputInteger(InputItem): """An integer input item. Options: - `min`, `max`: range of the scale (integer) """ def __init__(self,name,value,*args,**kargs): """Creates a new integer input field with a label in front.""" self.input = QtGui.QLineEdit(str(value)) InputItem.__init__(self,name,*args,**kargs) self.validator = QtGui.QIntValidator(self) if kargs.has_key('min'): self.validator.setBottom(int(kargs['min'])) if kargs.has_key('max'): self.validator.setTop(int(kargs['max'])) self.input.setValidator(self.validator) self.layout().insertWidget(1,self.input) def show(self): """Select all text on first display.""" InputItem.show(self) self.input.selectAll() def value(self): """Return the widget's value.""" return int(self.input.text()) def setValue(self,val): """Change the widget's value.""" val = int(val) self.input.setText(str(val)) class InputFloat(InputItem): """An float input item.""" def __init__(self,name,value,*args,**kargs): """Creates a new float input field with a label in front.""" self.input = QtGui.QLineEdit(str(value)) InputItem.__init__(self,name,*args,**kargs) self.validator = QtGui.QDoubleValidator(self) if kargs.has_key('min'): self.validator.setBottom(float(kargs['min'])) if kargs.has_key('max'): self.validator.setTop(float(kargs['max'])) if kargs.has_key('dec'): self.validator.setDecimals(int(kargs['dec'])) self.input.setValidator(self.validator) self.layout().insertWidget(1,self.input) def show(self): """Select all text on first display.""" InputItem.show(self) self.input.selectAll() def value(self): """Return the widget's value.""" return float(self.input.text()) def setValue(self,val): """Change the widget's value.""" val = float(val) self.input.setText(str(val)) ## class InputFloatTable(InputItem): ## """A table of floats input item.""" ## def __init__(self,name,value,*args,**kargs): ## """Creates a new float table input field.""" ## if value is None: ## ncols = kargs.get('ncols',1) ## nrows = kargs.get('nrows',1) ## value = zeros(nrows,ncols) ## else: ## nrows,ncols = value.shape ## chead = kargs.get('chead',None) ## rhead = kargs.get('rhead',None) ## self.input = ArrayTable(value,rhead=rhead,chead=chead) ## InputItem.__init__(self,name,*args,**kargs) ## self.layout().insertWidget(1,self.input) ## def show(self): ## """Select all text on first display.""" ## InputItem.show(self) ## self.input.selectAll() ## def value(self): ## """Return the widget's value.""" ## return float(self.input.text()) ## def setValue(self,val): ## """Change the widget's value.""" ## val = float(val) ## self.input.setText(str(val)) class InputSlider(InputInteger): """An integer input item using a slider. Options: - `min`, `max`: range of the scale (integer) - `ticks`: step for the tick marks (default range length / 10) - `func`: an optional function to be called whenever the value is changed. The function takes a float/integer argument. """ def __init__(self,name,value,*args,**kargs): """Creates a new integer input slider.""" InputInteger.__init__(self,name,value,*args,**kargs) self.slider = QtGui.QSlider(QtCore.Qt.Horizontal) self.slider.setTickPosition(QtGui.QSlider.TicksBelow) vmin = kargs.get('min',0) vmax = kargs.get('max',100) ticks = kargs.get('ticks',(vmax-vmin)/10) self.slider.setTickInterval(ticks) self.slider.setMinimum(vmin) self.slider.setMaximum(vmax) self.slider.setValue(value) self.slider.setSingleStep(1) #self.slider.setPageStep(5) self.slider.setTracking(1) self.connect(self.slider,QtCore.SIGNAL("valueChanged(int)"),self.set_value) if kargs.has_key('func'): self.connect(self.slider,QtCore.SIGNAL("valueChanged(int)"),kargs['func']) self.layout().addWidget(self.slider) def set_value(self,val): val = int(val) self.input.setText(str(val)) class InputFSlider(InputFloat): """A float input item using a slider. Options: - `min`, `max`: range of the scale (integer) - `scale`: scale factor to compute the float value - `ticks`: step for the tick marks (default range length / 10) - `func`: an optional function to be called whenever the value is changed. The function receives the input field as argument. With this argument, the fields attirbutes like name, value, text, can be retrieved. """ def __init__(self,name,value,*args,**kargs): """Creates a new integer input slider.""" InputFloat.__init__(self,name,value,*args,**kargs) self.slider = QtGui.QSlider(QtCore.Qt.Horizontal) self.slider.setTickPosition(QtGui.QSlider.TicksBelow) self.scale = kargs.get('scale',1.0) self.func = kargs.get('func',None) vmin = kargs.get('min',0) vmax = kargs.get('max',100) ticks = kargs.get('ticks',(vmax-vmin)/10) self.slider.setTickInterval(ticks) self.slider.setMinimum(vmin) self.slider.setMaximum(vmax) self.slider.setValue(value/self.scale) self.slider.setSingleStep(1) #self.slider.setPageStep(5) self.slider.setTracking(1) self.connect(self.slider,QtCore.SIGNAL("valueChanged(int)"),self.set_value) self.layout().addWidget(self.slider) def set_value(self,val): print val val = float(val) value = val*self.scale print value self.input.setText(str(value)) if self.func: self.func(self) class InputPoint(InputItem): """A 3D point/vector input item.""" def __init__(self,name,value,*args,**kargs): """Creates a new point input field with a label in front.""" self.input = CoordsBox() InputItem.__init__(self,name,*args,**kargs) self.layout().insertWidget(1,self.input) self.setValue(value) def value(self): """Return the widget's value.""" return self.input.getValues() def setValue(self,val): """Change the widget's value.""" self.input.setValues(val) class InputIVector(InputItem): """A vector of int values.""" def __init__(self,name,value,*args,**kargs): """Creates a new ivector input field with a label in front.""" self.ndim = len(value) if 'fields' in kargs: fields = kargs['fields'] else: fields = [ str(i) for i in range(self.ndim) ] self.input = QtGui.QWidget(*args) InputItem.__init__(self,name,*args,**kargs) #self.layout().insertWidget(1,self.input) #layout = QtGui.QHBoxLayout(self) #self.input.setLayout(layout) layout = self.layout() self.fields = [] for fld,val in zip(fields,value): f = InputInteger(fld,val) self.fields.append(f) layout.addWidget(f) def value(self): """Return the widget's value.""" return [ f.value() for f in self.fields ] def setValue(self,val): """Change the widget's value.""" for f,v in zip(self.fields,val): f.setValue(v) class InputButton(InputItem): """A button input item. The button input field is a button displaying the current value. Clicking on the button executes a function responsible for changing the value. Extra parameters: - `func`: the function to call when the button is clicked. The current input value is passed as an argument. The function should return the value to be set, or None if it is to be unchanged. If no function is specified, the value can not be changed. """ def __init__(self,name,value,*args,**kargs): """Create a new button input field.""" value = str(value) self.input = QtGui.QPushButton(value) self.func = kargs.get('func',None) InputItem.__init__(self,name,*args,**kargs) self.setValue(value) if self.func: self.connect(self.input,QtCore.SIGNAL("clicked()"),self.doFunc) self.layout().insertWidget(1,self.input) def doFunc(self): """Set the value by calling the button's func""" val = self.func(self.value()) if val: self.setValue(val) class InputColor(InputItem): """A color input item. Creates a new color input field with a label in front. The color input field is a button displaying the current color. Clicking on the button opens a color dialog, and the returned value is set in the button. """ def __init__(self,name,value,*args,**kargs): """Create the color input item.""" color = colors.colorName(value) self.input = QtGui.QPushButton(color) InputItem.__init__(self,name,*args,**kargs) self.setValue(color) self.connect(self.input,QtCore.SIGNAL("clicked()"),self.setColor) self.layout().insertWidget(1,self.input) def setColor(self): color = getColor(self.input.text()) if color: self.setValue(color) def setValue(self,value): """Change the widget's value.""" rgb = QtGui.QColor(value).getRgb() self.input.setStyleSheet("* { background-color: rgb(%s,%s,%s) }" % rgb[:3]) self.input.setText(str(value)) class InputFont(InputItem): """An input item to select a font.""" def __init__(self,name,value,*args,**kargs): """Creates a new font input field.""" if value is None: value = pf.app.font().toString() self.input = QtGui.QPushButton(value) InputItem.__init__(self,name,*args,**kargs) self.setValue(value) self.connect(self.input,QtCore.SIGNAL("clicked()"),self.setFont) self.layout().insertWidget(1,self.input) def setFont(self): font = selectFont() if font: self.setValue(font.toString()) #pf.GUI.setFont(font) class InputWidget(InputItem): """An input item containing any other widget. The widget should have: - a results attribute that is set to a dict with the resulting input values when the widget's acceptData() is called. - an acceptData() method, that sets the widgets results dict. - a setValue(dict) method that sets the widgets values to those specified in the dict. The return value of this item is an ODict. """ def __init__(self,name,value,*args,**kargs): """Creates a new InputWidget.""" kargs['text'] = '' # Force no label self.input = value InputItem.__init__(self,name,*args,**kargs) self.layout().insertWidget(1,self.input) def text(self): """Return the displayed text.""" return '' ## def value(self): ## """Return the widget's value.""" ## self.item.acceptData() ## return self.results def setValue(self,val): """Change the widget's value.""" if val: self.input.setValue(val) class InputForm(QtGui.QVBoxLayout): """An input form. The input form is a layout box in which the items are layed out vertically. The layout can also contain any number of tab widgets in which items can be layed out using tab pages. """ def __init__(self): QtGui.QVBoxLayout.__init__(self) self.tabs = [] # list of tab widgets in this form self.last = None # last added itemtype class InputGroup(QtGui.QGroupBox): """A boxed group of InputItems.""" def __init__(self,name,*args,**kargs): QtGui.QGroupBox.__init__(self,*args) self.key = name self.input = self self.tab = None self.form = InputForm() self.setLayout(self.form) self.setTitle(kargs.get('text',name)) if 'checked' in kargs: self.setCheckable(True) self.setChecked(kargs['checked']) if 'enabled' in kargs: self.setEnabled(kargs['enabled']) def name(self): return self.key def value(self): """Return the widget's value.""" if self.isCheckable(): return self.isChecked() else: return None def setValue(self,val): """Change the widget's value.""" if self.isCheckable(): self.setChecked(val) class InputTab(QtGui.QWidget): """A tab page in an input form.""" def __init__(self,name,tab,*args,**kargs): QtGui.QWidget.__init__(self,*args) self.key = name self.form = InputForm() self.setLayout(self.form) tab.addTab(self,kargs.get('text',name)) def name(self): return self.key def defaultItemType(item): """Guess the InputItem type from the value""" if 'choices' in item: itemtype = 'select' else: itemtype = type(item['value']) if itemtype is None: itemtype = str return itemtype def simpleInputItem(name,value=None,itemtype=None,**kargs): """A convenience function to create an InputItem dictionary""" kargs['name'] = name if value is not None: kargs['value'] = value if itemtype is not None: kargs['itemtype'] = itemtype return kargs def groupInputItem(name,items=[],**kargs): """A convenience function to create an InputItem dictionary""" kargs['name'] = name kargs['items'] = items kargs['itemtype'] = 'group' return kargs def tabInputItem(name,items=[],**kargs): """A convenience function to create an InputItem dictionary""" kargs['name'] = name kargs['items'] = items kargs['itemtype'] = 'tab' return kargs def compatInputItem(name,value,itemtype=None,kargs={}): """A convenience function to create an InputItem dictionary This function accepts InputItem data in the old format:: ( name, value, [ itemtype, [ optionsdict ] ] ) and turns them into a dictionary as required by the new InputItem format. """ # Create a new dict item! # We cannot change kargs directly like in simpleInputItem, # that would permanently change the value of the empty dict! item = {} if isinstance(itemtype,dict): # in case the itemtype was missing kargs = itemtype itemtype = None item.update(kargs) item['name'] = name item['value'] = value item['itemtype'] = itemtype return item def convertInputItem(data): """Convert InputItem data to a proper dict. This function tries to convert some old style or sloppy InputItem data to a proper InputItem data dict. The conversion does the following: - if `data` is a dict, it is considered proper data and returned as is. - if `data` is a tuple or a list, first conversion with simpleInputItem is tried, then conversion with compatInputItem, using the data items as arguments. - if neither succeeds, an error is raised. """ if isinstance(data,dict): return data elif type(data) in [list,tuple]: try: return simpleInputItem(*data) except: try: return compatInputItem(*data) except: pass pass raise ValueError,"Invalid inputItem data: %s" % str(data) # define a function to have the same enabling name as for InputItem def enableItem(self,*args): try: ok = any([ src.value() == val for src,val in self.enabled_by ]) self.setEnabled(ok) except: warnings.warn("Error in a dialog item enabler. This should not happen!") pass InputItem.enableItem = enableItem QtGui.QGroupBox.enableItem = enableItem QtGui.QTabWidget.enableItem = enableItem ## def nameGroupBox(self): ## return self.title() ## QtGui.QGroupBox.name = nameGroupBox ## QtGui.QTabWidget.disable = disableGroup default_dialog_flags = QtCore.Qt.WindowMaximizeButtonHint | QtCore.Qt.CustomizeWindowHint class InputDialog(QtGui.QDialog): """A dialog widget to interactively set the value of one or more items. Overview The pyFormex user has full access to the Qt4 framework on which the GUI was built. Therefore he can built input dialogs as complex and powerful as he can imagine. However, directly dealing with the Qt4 libraries requires some skills and, for simple input widgets, more effort than needed. The InputDialog class presents a unified system for quick and easy creation of common dialog types. The provided dialog can become quite sophisticated with tabbed pages, groupboxes and custom widgets. Both modal and modeless (non-modal) dialogs can be created. Items Each basic input item is a dictionary, where the fields have the following meaning: - name: the name of the field, - value: the initial or default value of the field, - itemtype: the type of values the field can accept, - options: a dict with options for the field. - text: if specified, the text value will be displayed instead of the name. The name value will remain the key in the return dict. Use this field to display a more descriptive text for the user, while using a short name for handling the value in your script. - buttons: - tooltip: - min: - max: - scale: - func: Other arguments - caption: the window title to be shown in the window decoration - actions: a list of action buttons to be added at the bottom of the input form. By default, a Cancel and Ok button will be added, to either reject or accept the input values. - default: the default action - parent: the parent widget (by default, this is the pyFormex main window) - autoprefix: if True, the names of items inside tabs and group boxes will get prefixed with the tab and group names, separated with a '/'. - flat: if True, the results are returned in a single (flat) dictionary, with keys being the specified or autoprefixed ones. If False, the results will be structured: the value of a tab or a group is a dictionary with the results of its fields. The default value is equal to the value of autoprefix. - flags: - modal: - `enablers`: a list of tuples (key,value,key1,...) where the first two items indicate the key and value of the enabler, and the next items are keys of fields that are enabled when the field key has the specified value. Currentley, key should be a field of type boolean, [radio], combo or group. Also, any input field should only have one enabler! """ def __init__(self,items,caption=None,parent=None,flags=None,actions=None,default=None,store=None,prefix='',autoprefix=False,flat=None,modal=None,enablers=[]): """Create a dialog asking the user for the value of items.""" if parent is None: parent = pf.GUI QtGui.QDialog.__init__(self,parent) if caption is None: caption = 'pyFormex-dialog' else: caption = str(caption) self.setObjectName(caption) self.setWindowTitle(caption) if modal is not None: self.setModal(modal) self.fields = [] self.groups = {} self.results = odict.ODict() self._pos = None self.store = store self.autoname = utils.NameSequence('input') self.prefix = prefix self.autoprefix = autoprefix if flat is None: self.flat = self.autoprefix else: self.flat = flat # create the form with the input fields self.tab = None # tabwidget for all the tabs in this form self.form = InputForm() self.add_items(items,self.form,self.prefix) # add the action buttons but = dialogButtons(self,actions,default) self.form.addLayout(but) self.setLayout(self.form) self.connect(self,QtCore.SIGNAL("accepted()"),self.acceptData) # add the enablers init_signals = [] for en in enablers: #print "Enabler %s " % str(en) src = self[en[0]] if src: val = en[1] for t in en[2:]: tgt = self[t] #print "%s" % (tgt) if tgt: try: tgt.enabled_by.append((src,val)) except: tgt.enabled_by = [(src,val)] signal = None if isinstance(src,InputBool): signal = QtCore.SIGNAL("stateChanged(int)") elif isinstance(src,InputRadio): utils.warn('radio_enabler') # BV: this does not work signal = QtCore.SIGNAL("buttonClicked(int)") elif isinstance(src,InputCombo): signal = QtCore.SIGNAL("currentIndexChanged(int)") elif isinstance(src,InputGroup): signal = QtCore.SIGNAL("clicked(bool)") else: raise ValueError,"Can not enable from a %s input field" % type(src.input) if signal: init_signals.append((src.input,signal)) src.connect(src.input,signal,tgt.enableItem) # emit the signal to adjust initial state for src,signal in init_signals: src.emit(signal,0) def add_items(self,items,form,prefix=''): """Add input items to form. items is a list of input item data layout is the widget layout where the input widgets will be added """ for item in items: if isinstance(item,list) or isinstance(item,tuple): warnings.warn("warn_deprecated_inputitem") try: item = compatInputItem(*item) except: pass if isinstance(item,dict): itemtype = item.get('itemtype',None) if itemtype == 'tab': self.add_tab(form,prefix=prefix,**item) elif itemtype == 'group': self.add_group(form,prefix=prefix,**item) else: self.add_input(form,prefix=prefix,**item) form.last = itemtype elif isinstance(item,QtGui.QWidget): # this allows including widgets which are not # input fields form.addWidget(item) form.last = None else: raise ValueError,"Invalid input item (type %s). Expected a dict or a QWidget." % type(item) def add_tab(self,form,prefix,name,items,**extra): """Add a Tab page of input items.""" if form.last == 'tab': # Add to previous tab widget tab = form.tabs[-1] else: # Create a new tab widget tab = QtGui.QTabWidget() form.addWidget(tab) form.tabs.append(tab) w = InputTab(prefix+name,tab,**extra) if self.autoprefix: prefix += name+'/' self.add_items(items,w.form,prefix=prefix) w.form.addStretch() # makes items in tab align to top def add_group(self,form,prefix,name,items,**extra): """Add a group of input items.""" w = InputGroup(prefix+name,**extra) form.addWidget(w) if w.isCheckable: self.fields.append(w) if self.autoprefix: prefix += name+'/' self.add_items(items,w.form,prefix=prefix) def add_input(self,form,prefix,**item): """Add a single input item to the form.""" #print item item['name'] = prefix + item.get('name',self.autoname.next()) if not 'value' in item: # no value: try to find one if 'choices' in item: item['value'] = item['choices'][0] # DO NOT USE A TEST if self.store: HERE # THAT DOES NOT SEEM TO WORK: ALWAYS RETURNS FALSE try: item['value'] = self.store[item['name']] except: pass # we should have a value now, or we can't continue! if not 'value' in item: raise ValueError,"No value specified for item '%s'" % item['name'] if not 'itemtype' in item or item['itemtype'] is None: item['itemtype'] = defaultItemType(item) itemtype = item['itemtype'] if type(itemtype) is str: if itemtype.endswith('radio') or itemtype.endswith('push'): if itemtype[0] in 'hv': item['direction'] = itemtype[0] item['itemtype'] = itemtype[1:] else: # default horizontal item['direction'] = 'h' if itemtype == 'slider': value = item['value'] if type(value) == int: pass elif type(value) == float: item['itemtype'] = 'fslider' else: raise ValueError,"Invalid value type for slider: %s" % value item['parent'] = self field = inputAny(**item) self.fields.append(field) form.addWidget(field) def __getitem__(self,name): """Return the input item with specified name.""" items = [ f for f in self.fields if f.name() == name ] if len(items) > 0: return items[0] else: raise ValueError,"No input field named: %s" % name #return self.groups.get(name,None) def timeout(self): """Hide the dialog and set the result code to TIMEOUT""" pf.debug("TIMEOUT") self.acceptData(TIMEOUT) def timedOut(self): """Returns True if the result code was set to TIMEOUT""" return self.result() == TIMEOUT def show(self,timeout=None,timeoutfunc=None,modal=False): """Show the dialog. For a non-modal dialog, the user has to call this function to display the dialog. For a modal dialog, this is implicitely executed by getResult(). If a timeout is given, start the timeout timer. """ # Set the keyboard focus to the first input field #self.fields[0].input.setFocus() self.status = None self.setModal(modal) if not modal: #print "DELETE ON CLOSE" self.setAttribute(QtCore.Qt.WA_DeleteOnClose) #self.adjustSize() self.setMaximumHeight(1000) #print self.maximumHeight() QtGui.QDialog.show(self) addTimeOut(self,timeout,timeoutfunc) ## def close(self): ## """Close and delete the dialog. ## """ ## QtGui.QDialog.close(self) ## print self.parent() ## print self.parent().children() ## self.parent().removeChild(self) def acceptData(self,result=ACCEPTED): """Update the dialog's return value from the field values. This function is connected to the 'accepted()' signal. Modal dialogs should normally not need to call it. In non-modal dialogs however, you can call it to update the results without having to raise the accepted() signal (which would close the dialog). """ self.results = odict.ODict() self.results.update([ (fld.name(),fld.value()) for fld in self.fields ]) ## if self.report_pos: ## self.results.update({'__pos__':self.pos()}) if result == TIMEOUT: self.done(result) else: self.setResult(result) def updateData(self,d): """Update a dialog from the data in given dictionary. d is a dictionary where the keys are field names in the dialog. The values will be set in the corresponding input items. """ for f in self.fields: n = f.name() if n in d: f.setValue(d[n]) def getResults(self,timeout=None): """ Get the results from the input dialog. This fuction is used to present a modal dialog to the user (i.e. a dialog that must be ended before the user can continue with the program. The dialog is shown and user interaction is processed. The user ends the interaction either by accepting the data (e.g. by pressing the OK button or the ENTER key) or by rejecting them (CANCEL button or ESC key). On accept, a dictionary with all the fields and their values is returned. On reject, an empty dictionary is returned. If a timeout (in seconds) is given, a timer will be started and if no user input is detected during this period, the input dialog returns with the default values set. A value 0 will timeout immediately, a negative value will never timeout. The default is to use the global variable input_timeout. The result() method can be used to find out how the dialog was ended. Its value will be one of ACCEPTED, REJECTED ot TIMEOUT. """ self.results = odict.ODict() self.setResult(0) if self._pos is not None: self.restoreGeometry(self._pos) self.show(timeout,modal=True) self.exec_() #self.activateWindow() #self.raise_() pf.app.processEvents() self._pos = self.saveGeometry() return self.results # for compatibility, should be deprecated getResult = getResults class ScrollDialog(InputDialog): def __init__(self,*args,**kargs): # This is experimental !!! InputDialog.__init__(self,*args,**kargs) self.scroll = QtGui.QScrollArea() self.scroll.setWidget(self) #self.viewport().setBackgroundRole(QtGui.QPalette.Dark) self.scroll.viewport().setAutoFillBackground(True) #self.scroll.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding,QtGui.QSizePolicy.Maximum) #self.scroll.resize(pf.GUI.width()/2,pf.GUI.height()) #self.scroll.setWidgetResizable(True) def show(self): self.scroll.show() def close(self): self.scroll.close() # Create a dict with itemtype <-> InputItem mapping def getInputItemDict(base=InputItem): sub = base.__subclasses__() if not sub: return {} d = dict([ (k.__name__[5:].lower(),k) for k in sub ]) for k in sub: d.update(getInputItemDict(k)) return d InputItems = getInputItemDict() # some itemtypes are not strings but Python type objects. # also add some name mismatches InputItems.update({ None: InputItem, bool: InputBool, int: InputInteger, float: InputFloat, str: InputString, 'select': InputCombo, }) keys = InputItems.keys() keys.sort() def inputAny(name,value,itemtype,**options): """Create an InputItem of any type, depending on the arguments. Arguments: only name, value and itemtype are required - name: name of the item, also the key for the return value - value: initial value, - itemtype: one of the available itemtypes """ #print name,value,itemtype,options try: f = InputItems[itemtype] except: f = InputString # default convert to string return f(name,value,**options) def inputAnyOld(item,parent=None): """_Create an InputItem with the old data style. This translates the data from the legacy InputItem data to the new style required by InputAny. Returns the InputItem constrctured with the data. """ name,value = item[:2] if type(item[-1]) == dict: # we have options options = item[-1] item = item[:-1] else: options = {} if len(item) > 2 and type(item[2]) == str: itemtype = item[2] else: # No item specified: guess from value or from available options if 'choices' in options: itemtype = 'select' else: itemtype = type(value) if itemtype == int: if len(item) > 3 and type(item[3] != dict): options['min'] = int(item[3]) if len(item) > 4: options['max'] = int(item[4]) elif itemtype == float: if len(item) > 3 and type(item[3] != dict): options['min'] = int(item[3]) if len(item) > 4: options['max'] = int(item[4]) if len(item) > 5: options['dec'] = int(item[5]) elif itemtype == 'select' : if len(item) > 3: options['choices'] = item[3] elif itemtype in ['radio','hradio','vradio']: if len(item) > 3: options['choices'] = item[3] options['direction'] = itemtype[0] elif itemtype in ['push','hpush','vpush']: if len(item) > 3: options['choices'] = item[3] options['direction'] = itemtype[0] if parent is not None: options['parent'] = parent return inputAny(name,value,itemtype,**options) def updateDialogItems(data,newdata): """Update the input data fields with new data values - data: a list of dialog items, as required by an InputDialog. - newdata: a dictionary with new values for (some of) the items. The data items with a name occurring as a key in newdata will have their value replaced with the corresponding value in newdata, unless this value is None. The user should make sure to set only values of the proper type! """ if newdata: # check for old format if type(data) is dict: return updateOldDialogItems(data,newdata) for d in data: if not isinstance(d,dict): return updateOldDialogItems(data,newdata) # new format for d in data: if d.get('itemtype',None) in [ 'group', 'tab' ]: updateDialogItems(d['items'],newdata) else: newval = newdata.get(d['name'],None) if newval is not None: d['value'] = newval def updateOldDialogItems(data,newdata): """_Update the input data fields with new data values.""" warnings.warn("warn_widgets_updatedialogitems") if newdata: if type(data) is dict: for d in data: updateOldDialogItems(data[d],newdata) else: for d in data: v = newdata.get(d[0],None) if v is not None: d[1] = v ###################### File Selection Dialog ######################### class FileSelection(QtGui.QFileDialog): """A file selection dialog. You can specify a default path/filename that will be suggested initially. If a pattern is specified, only matching files will be shown. A pattern can be something like ``Images (*.png *.jpg)`` or a list of such strings. Default mode is to accept any filename. You can specify exist=True to accept only existing files. Or set exist=True and multi=True to accept multiple files. If dir==True, a single existing directory is asked. """ def __init__(self,path='.',pattern='*.*',exist=False,multi=False,dir=False): """The constructor shows the widget.""" QtGui.QFileDialog.__init__(self) if os.path.isfile(path): self.setDirectory(os.path.dirname(path)) self.selectFile(path) else: self.setDirectory(path) if type(pattern) == str: self.setFilter(pattern) else: # should be a list of patterns self.setFilters(pattern) if dir: mode = QtGui.QFileDialog.Directory caption = "Select a directory" elif exist: if multi: mode = QtGui.QFileDialog.ExistingFiles caption = "Select existing files" else: mode = QtGui.QFileDialog.ExistingFile caption = "Open existing file" else: mode = QtGui.QFileDialog.AnyFile caption = "Save file as" self.setFileMode(mode) self.setWindowTitle(caption) if exist: self.setLabelText(QtGui.QFileDialog.Accept,'Open') else: self.setLabelText(QtGui.QFileDialog.Accept,'Save') ## if self.sidebar: ## urls = self.sidebarUrls() ## for f in self.sidebar: ## urls.append(QtCore.QUrl.fromLocalFile(f)) ## self.setSidebarUrls(urls) ## for p in self.sidebarUrls(): ## pf.message(p.toString()) timeout = "accept()" def show(self,timeout=None,timeoutfunc=None,modal=False): self.setModal(modal) QtGui.QFileDialog.show(self) addTimeOut(self,timeout,timeoutfunc) def getFilename(self,timeout=None): """Ask for a filename by user interaction. Return the filename selected by the user. If the user hits CANCEL or ESC, None is returned. """ self.show(timeout,modal=True) self.exec_() if self.result() == QtGui.QDialog.Accepted: files = map(str,self.selectedFiles()) if self.fileMode() == QtGui.QFileDialog.ExistingFiles: return files else: return files[0] else: return None class ProjectSelection(FileSelection): """A file selection dialog specialized for opening projects.""" def __init__(self,path=None,pattern=None,exist=False,compression=4, access=None,default=None,convert=True): """Create the dialog.""" if path is None: path = pf.cfg['workdir'] if pattern is None: pattern = map(utils.fileDescription, ['pyf']) FileSelection.__init__(self,path,pattern,exist) grid = self.layout() nr,nc = grid.rowCount(),grid.columnCount() if access is None: access = [ 'rw', 'r' ] if exist else [ 'wr', 'rw', 'w', 'r' ] self.acc = InputRadio("Access Mode",default,choices=access) self.acc.setToolTip("wr=read if exist; rw=must exist; w=overwrite; r=readonly") grid.addWidget(self.acc,nr,0,1,-1) nr += 1 if exist: self.cvt = InputBool("Automatically convert file to latest format",convert) self.cvt.setToolTip("It is recommended to automatically convert your project files to the latest format, to avoid future compatibility problems. The only reason to not convert is if you still need to read your files with olde versions of pyFormex. The conversion will not be performed if pyFormex can not correctly read your file.") grid.addWidget(self.cvt,nr,0,1,-1) nr += 1 if not exist: self.cpr = InputSlider("Compression level (0-9)",compression,min=0,max=9,ticks=1) self.cpr.setToolTip("Higher compression levels result in smaller files, but higher load and save times.") grid.addWidget(self.cpr,nr,0,1,-1) nr += 1 def getResult(self): self.exec_() if self.result() == QtGui.QDialog.Accepted: opt = mydict.Dict() opt.fn = str(self.selectedFiles()[0]) opt.acc = self.acc.value() opt.cpr = opt.cvt = None if hasattr(self,'cpr'): opt.cpr = self.cpr.value() if hasattr(self,'cvt'): opt.cvt = self.cvt.value() return opt else: return {} class SaveImageDialog(FileSelection): """A dialog for saving to an image file. The dialog contains the normal file selection widget plus some extra fields to set the Save Image parameters: - `Whole Window`: If checked, the whole pyFormex main window will be saved. If unchecked, only the current OpenGL viewport is saved. - `Crop Root`: If checked, the window will be cropped from the root window. This mode is required if you want to include the window decorations. """ default_size = None def __init__(self,path=None,pattern=None,exist=False,multi=False): """Create the dialog.""" if path is None: path = pf.cfg['workdir'] if pattern is None: pattern = map(utils.fileDescription, ['img','icon','all']) FileSelection.__init__(self,path,pattern,exist) grid = self.layout() nr,nc = grid.rowCount(),grid.columnCount() try: w,h = SaveImageDialog.default_size except: w,h = pf.canvas.getSize() import image formats = ['From Extension'] + image.imageFormats() self.fmt = InputCombo("Format:",None,choices=formats) self.qua = InputInteger("Quality:",-1) self.siz = InputIVector("Size:",[w,h],fields=['W','H']) self.win = QtGui.QCheckBox("Whole Window") self.roo = QtGui.QCheckBox("Crop Root") self.bor = QtGui.QCheckBox("Add Border") self.mul = QtGui.QCheckBox("Multi mode") self.hot = QtGui.QCheckBox("Activate '%s' hotkey" % pf.cfg['keys/save']) self.aut = QtGui.QCheckBox('Autosave mode') self.mul.setChecked(multi) self.hot.setChecked(multi) self.win.setToolTip("If checked, the whole window is saved;\nelse, only the Canvas is saved.") self.roo.setToolTip("If checked, the window will be cropped from the root window.\nThis mode is required if you want to include the window decorations.") self.bor.setToolTip("If checked when the whole window is saved,\nthe window decorations will be included as well.") self.mul.setToolTip("If checked, multiple images can be saved\nwith autogenerated names.") self.hot.setToolTip("If checked, a new image can be saved\nby hitting the 'S' key when focus is in the Canvas.") self.aut.setToolTip("If checked, a new image will saved\non each draw() operation") grid.addWidget(self.fmt,nr,0,1,2) grid.addWidget(self.qua,nr,2) nr += 1 grid.addWidget(self.siz,nr,0,1,2) nr += 1 grid.addWidget(self.win,nr,0) grid.addWidget(self.roo,nr,1) grid.addWidget(self.bor,nr,2) nr += 1 grid.addWidget(self.mul,nr,0) grid.addWidget(self.hot,nr,1) grid.addWidget(self.aut,nr,2) def getResult(self): self.exec_() if self.result() == QtGui.QDialog.Accepted: opt = mydict.Dict() opt.fm = self.fmt.value() opt.qu = SaveImageDialog.default_size = self.qua.value() opt.sz = self.siz.value() opt.fn = str(self.selectedFiles()[0]) opt.wi = self.win.isChecked() opt.rc = self.roo.isChecked() opt.bo = self.bor.isChecked() opt.mu = self.mul.isChecked() opt.hk = self.hot.isChecked() opt.au = self.aut.isChecked() return opt else: return {} def selectFont(): """Ask the user to select a font. A font selection dialog widget is displayed and the user is requested to select a font. Returns a font if the user exited the dialog with the :guilabel:`OK` button. Returns None if the user clicked :guilabel:`CANCEL`. """ font,ok = QtGui.QFontDialog.getFont() if ok: return font else: return None class ListSelection(InputDialog): """A dialog for selecting one or more items from a list. This is a convenient class which constructs an input dialog with a single input item: an InputList. It allows the user to select one or more items from a list. The constructor supports all arguments of the InputDialog and the InputList classes. The return value is the value of the InputList, not the result of the InputDialog. """ def __init__(self,choices,caption='ListSelection',default=[],single=False,check=False,sort=False,*args,**kargs): """Create the SelectionList dialog.""" InputDialog.__init__(self,caption=caption,items = [ dict(name='input',value=default,itemtype='list',choices=choices, text='',single=single,check=check,sort=sort,*args,**kargs), ],) def setValue(self,selected): """Mark the specified items as selected.""" self['input'].setValue(selected) def value(self): """Return the selected items.""" return self['input'].value() def getResult(self): """Show the modal dialog and return the list of selected values. If the user cancels the selection operation, the return value is None. Else, the result is always a list, possibly empty or with a single value. """ self.exec_() if self.result() == QtGui.QDialog.Accepted: return self.value() else: return None class Selection(ListSelection): def __init__(self,slist=[],title='Selection Dialog',mode=None,sort=False,selected=[]): """Create the SelectionList dialog.""" import warnings warnings.deprecation("widgets.Selection is deprecated. Please use widgets.ListSelection.") ListSelection.__init__(self,caption=title,choices=slist,default=selected,single=mode=='single',sort=sort) # BV Uncommented, because I could only find 1 use: in osteo_menu, to define # an InputList, which can probably be replaced by the default InputList ## class ModelessSelection(QtGui.QDialog): ## """A modeless dialog for selecting one or more items from a list.""" ## def __init__(self,slist=[],title='Selection Dialog',mode=None,sort=False,func=None,width=None,height=None): ## """Create the SelectionList dialog.""" ## QtGui.QDialog.__init__(self) ## self.setWindowTitle(title) ## # Selection List ## self.listw = QtGui.QListWidget() ## if width is not None: ## self.listw.setMaximumWidth(width) ## if height is not None: ## self.listw.setMaximumHeight(height) ## self.listw.addItems(slist) ## if sort: ## self.listw.sortItems() ## self.listw.setSelectionMode(selection_mode[mode]) ## grid = QtGui.QGridLayout() ## grid.addWidget(self.listw,0,0,1,1) ## self.setLayout(grid) ## if func: ## self.connect(self.listw,QtCore.SIGNAL("itemClicked(QListWidgetItem *)"),func) ## def setSelected(self,selected,bool): ## """Mark the specified items as selected.""" ## for s in selected: ## for i in self.listw.findItems(s,QtCore.Qt.MatchExactly): ## i.setSelected(True) ## i.setCheckState(QtCore.Qt.Checked) ## def getResult(self): ## """Return the list of selected values. ## If the user cancels the selection operation, the return value is None. ## Else, the result is always a list, possibly empty or with a single ## value. ## """ ## res = [i.text() for i in self.listw.selectedItems()] ## return map(str,res) # BV uncommented, because I can not find any place where it is used, # and probably it would be better to use a generic docked widget and # an InputList ## class DockedSelection(QtGui.QDockWidget): ## """A docked selection widget. ## A widget that is docked in the main window and contains a modeless ## dialog for selecting items. ## """ ## def __init__(self,slist=[],title='Selection Dialog',mode=None,sort=False,func=None): ## QtGui.QDockWidget.__init__(self) ## self.setWidget(ModelessSelection(slist,title,mode,sort,func)) ## def setSelected(self,selected,bool): ## self.widget().setSelected(selected,bool) ## def getResult(self): ## res = self.widget().getResult() ## return res # !! The QtGui.QColorDialog can not be instantiated or subclassed. # !! The color selection dialog is created by the static getColor # !! function. def getColor(col=None,caption=None): """Create a color selection dialog and return the selected color. col is the initial selection. If a valid color is selected, its string name is returned, usually as a hex #RRGGBB string. If the dialog is canceled, None is returned. """ if type(col) == tuple: col = QtGui.QColor.fromRgb(*col) else: col = QtGui.QColor(col) dia = QtGui.QColorDialog #myButton = QtGui.QPushButton('MY') #dia.layout() col = dia.getColor(col) if col.isValid(): return str(col.name()) else: return None # Can be replaced with InputDialog?? class GenericDialog(QtGui.QDialog): """A generic dialog widget. The dialog is formed by a number of widgets stacked in a vertical box layout. At the bottom is a horizontal button box with possible actions. - `widgets`: a list of widgets to include in the dialog - `title`: the window title for the dialog - `parent`: the parent widget. If None, it is set to pf.GUI. - `actions`: the actions to include in the bottom button box. By default, an 'OK' button is displayed to close the dialog. Can be set to None to avoid creation of a button box. - `default`: the default action, 'OK' by default. """ def __init__(self,widgets,title=None,parent=None,actions=[('OK',)],default='OK'): """Create the Dialog""" if parent is None: parent = pf.GUI QtGui.QDialog.__init__(self,parent) if title is None: title = 'pyFormex Dialog' self.setWindowTitle(str(title)) self.form = QtGui.QVBoxLayout() self.add(widgets) if actions is not None: but = dialogButtons(self,actions,default) self.form.addLayout(but) self.setLayout(self.form) def add(self,widgets,pos=-1): if type(widgets) is not list: widgets = [widgets] for w in widgets: if pos >= 0: ind = pos else: ind = pos+self.form.count() self.form.insertWidget(ind,w) ########################### Table widgets ########################### _EDITROLE = QtCore.Qt.EditRole class TableModel(QtCore.QAbstractTableModel): """A table model that represent data as a two-dimensional array of items. data is any tabular data organized in a fixed number of rows and colums. This means that an item at row i and column j can be addressed as data[i][j]. Optional lists of column and row headers can be specified. """ def __init__(self,data,chead=None,rhead=None,edit=True): QtCore.QAbstractTableModel.__init__(self) self.arraydata = data self.headerdata = {QtCore.Qt.Horizontal:chead,QtCore.Qt.Vertical:rhead} self.makeEditable(edit) def makeEditable(self,edit=True): self._flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable if edit: self._flags |= QtCore.Qt.ItemIsEditable self.edit = edit def rowCount(self,parent=None): return len(self.arraydata) def columnCount(self,parent=None): return len(self.arraydata[0]) def data(self,index,role): if index.isValid() and role == QtCore.Qt.DisplayRole: return QtCore.QVariant(self.arraydata[index.row()][index.column()]) return QtCore.QVariant() def headerData(self,col,orientation,role): if orientation in self.headerdata and self.headerdata[orientation] and role == QtCore.Qt.DisplayRole: return QtCore.QVariant(self.headerdata[orientation][col]) return QtCore.QVariant() def insertRows(self,row=None,count=None): if row is None: row = self.rowCount() if count is None: count = 1 last = row+count-1 newdata = [ [ None ] * self.columnCount() ] * count self.beginInsertRows(QtCore.QModelIndex(),row,last) self.arraydata[row:row] = newdata self.endInsertRows() return True def removeRows(self,row=None,count=None): if row is None: row = self.rowCount() if count is None: count = 1 last = row+count-1 self.beginRemoveRows(QtCore.QModelIndex(),row,last) self.arraydata[row:row+count] = [] self.endRemoveRows() return True def flags(self,index): """Return the TableModel flags.""" return self._flags def setData(self,index,value,role=_EDITROLE): if self.edit and role == QtCore.Qt.EditRole: print "Setting items at %s to %s" % (str(index),str(value)) try: r,c = [index.row(),index.column()] print "Setting value at %s,%s to %s" %(r,c,value) value = eval(str(svalue.toString())) print "Setting value at %s,%s to %s" %(r,c,value) self.arraydata[index.row()][index.column()] = value print "Succesfully changed data" self.emit(QtCore.SIGNAL(self.dataChanged),index,index) print "Signaled success" return True except: print "Could not set the value" return False else: print "CAN NOT EDIT" return False from numpy import ndarray,asarray class ArrayModel(TableModel): """A TableModel specialized for represents 2D array data. - `data`: a numpy array with two dimensions. - `chead`, `rhead`: column and row headers. The default will show column and row numbers. - `edit`: if True (default), the data can be edited. Set to False to make the data readonly. """ def __init__(self,data,chead=None,rhead=None,edit=True): data = asarray(data) if rhead is None: rhead=range(data.shape[0]) if chead is None: chead=range(data.shape[1]) TableModel.__init__(self,data,rhead=rhead,chead=chead,edit=edit) def setData(self,index,value,role=_EDITROLE): if self.edit and role == QtCore.Qt.EditRole: print "Setting items at %s to %s" % (str(index),str(value)) try: r,c = [index.row(),index.column()] print "Setting value at %s,%s to %s" %(r,c,value) if self.arraydata.dtype.kind == 'f': value,ok = value.toDouble() elif self.arraydata.dtype.kind == 'i': value,ok = value.toInt() else: print "Editing of other than float or int arrays is not implemented yet!" ok = False if not ok: raise ValueError print "Setting value at %s,%s to %s" %(r,c,value) self.arraydata[index.row()][index.column()] = value self.emit(QtCore.SIGNAL(self.dataChanged),index,index) return True except: print "Could not set the value" return False else: print "CAN NOT EDIT" return False class Table(QtGui.QTableView): """A widget to show/edit a two-dimensional array of items. - `data`: a 2-D array of items, with `nrow` rows and `ncol` columns. If `data` is an ndarray instance, the Table will use an ArrayModel, else a TableModel. The difference is important when editing the table. Also, an ArrayModel has default row and column headers, while a TableModel doesn't. - `chead`: an optional list of `ncol` column headers. - `rhead`: an optional list of `nrow` row headers. - `label`: currently unused (intended to display an optional label in the upper left corner if both `chead` and `rhead` are specified. """ def __init__(self,data,chead=None,rhead=None,label=None,edit=True,parent=None): """Initialize the Table widget.""" QtGui.QTableView.__init__(self,parent) if isinstance(data,ndarray): self.tm = ArrayModel(data,chead,rhead,edit=edit) else: self.tm = TableModel(data,chead,rhead,edit=edit) self.setModel(self.tm) self.horizontalHeader().setVisible(chead is not None) self.verticalHeader().setVisible(rhead is not None) self.resizeColumnsToContents() self.setCornerButtonEnabled class Tabs(QtGui.QTabWidget): """Present a list of widgets as a single tabbed widget. - `items`: a list of (header,widget) tuples. - `caption`: - `parent`: """ def __init__(self,items,parent=None): """Create the TabWidget.""" QtGui.QTabWidget.__init__(self,parent) for header,widget in items: self.addTab(widget,header) class TableDialog(GenericDialog): """A dialog widget to show/edit a two-dimensional array of items. A convenience class representing a Table within a dialog. """ def __init__(self,data,chead=None,rhead=None,title=None,parent=None,actions=[('OK',)],default='OK'): """Create the Table dialog. - data is a 2-D array of items, with nrow rows and ncol columns. - chead is an optional list of ncol column headers. - rhead is an optional list of nrow row headers. """ GenericDialog.__init__(self, [Table(data,chead=chead,rhead=rhead,parent=self)], title=title, parent=parent, actions=actions,default=default) ##################################################################### ########### Text Display Widgets #################################### ##################################################################### def updateText(widget,text,format=''): """Update the text of a text display widget. - `widget`: a widget that has the :meth:`setText`, :meth:`setPlainText` and :meth:`setHtml` methods to set the widget's text. Examples are :class:`QMessageBox` and :class:`QTextEdit`. - `text`: a multiline string with the text to be displayed. - `format`: the format of the text. If empty, autorecognition will be tried. Currently available formats are: ``plain``, ``html`` and ``rest`` (reStructuredText). This function allows to display other text formats besides the plain text and html supported by the widget. Any format other than ``plain`` or ``html`` will be converted to one of these before sending it to the widget. Currently, we convert the following formats: - ``rest`` (reStructuredText): If the :mod:docutils is available, `rest` text is converted to `html`, otherwise it will be displayed as plain text. A text is autorecognized as reStructuredText if its first line starts with '..'. Note: If you add a '..' line to your text to have it autorecognized as reST, be sure to have it followed with a blank line, or your first paragraph could be turned into comments. """ # autorecognition if format not in ['plain','html','rest']: if type(text) is str and text.startswith('..'): format = 'rest' # conversion if format == 'rest' and pf.options.rst2html: html = utils.rst2html(text) if html[:10] == text[:10]: #print "CONVERSION TO HTML FAILED" text += "\n\nNote: This reStructuredText is displayed as plain text because it could not be converted to html. If you install python-docutils, you will see this text (and other pyFormex messages) in a much nicer layout!\n" else: text = html # We leave the format undefined, because we are not sure # that the conversion function (docutils) is available # and always produces good results format = '' if format == 'plain': widget.setPlainText(text) elif format == 'html': widget.setHtml(text) else: # As a last rescue, try QT4's autorecognition widget.setText(text) class MessageBox(QtGui.QMessageBox): """A message box is a widget displaying a short text for the user. The message box displays a text, an optional icon depending on the level and a number of push buttons. - `text`: the text to be shown. This can be either plain text or html or reStructuredText. - `format`: the text format: either 'plain', 'html' or 'rest'. Any other value will try automatic recognition. Recognition of plain text and html is automatic. A text is autorecognized to be reStructuredText if its first line starts with '..' and is followed by a blank line. - `level`: defines the icon that will be shown together with the text. If one of 'question', 'info', 'warning' or 'error', a matching icon will be shown to hint the user about the type of message. Any other value will suppress the icon. - `actions`: a list of strings. For each string a pushbutton will be created which can be used to exit the dialog and remove the message. By default there is a single button labeled 'OK'. When the MessageBox is displayed with the :meth:`getResult()` method, a modal dialog is created, i.e. the user will have to click a button or hit the ESC key before he can continue. If you want a modeless dialog, allowing the user to continue while the message stays open, use the :meth:`show()` mehod to display it. """ def __init__(self,text,format='',level='info',actions=['OK'],default=None,timeout=None,modal=None,parent=None,check=None): if parent is None: parent = pf.GUI QtGui.QMessageBox.__init__(self,parent) if modal is not None: self.setModal(modal) if default is None: default = actions[-1] updateText(self,text,format) self.setIcon(['noicon','info','warning','error','question'].index(level)) for a in actions: b = self.addButton(a,QtGui.QMessageBox.AcceptRole) if a == default: self.setDefaultButton(b) addTimeOut(self,timeout,"accept()") self.checks = [] if check: self.checks.append(self.addCheck(check)) def addCheck(self,text): """Add a check field at the bottom of the layout.""" grid = self.layout() nr,nc = grid.rowCount(),grid.columnCount() check = QtGui.QCheckBox(text) grid.addWidget(check,nr,1) return check def show(self,modal=False): self.setModal(modal) QtGui.QMessageBox.show(self) def getResult(self): """Display the message box and wait for user to click a button. This will show the message box as a modal dialog, so that the user has to click a button (or hit the ESC key) before he can continue. Returns the text of the button that was clicked or an empty string if ESC was hit. """ self.show(modal=True) self.exec_() b = self.clickedButton() if not b: # b == 0 or b is None b = self.defaultButton() if b: res = str(b.text()) else: res = '' if self.checks: return res,[c.isChecked() for c in self.checks] else: return res def updateText(self,text,format=''): updateText(self._t,text,format) class WarningBox(QtGui.QMessageBox): """A message box is a widget displaying a short text for the user. The message box displays a text, an optional icon depending on the level and a number of push buttons. - `text`: the text to be shown. This can be either plain text or html or reStructuredText. - `format`: the text format: either 'plain', 'html' or 'rest'. Any other value will try automatic recognition. Recognition of plain text and html is automatic. A text is autorecognized to be reStructuredText if its first line starts with '..' and is followed by a blank line. - `level`: defines the icon that will be shown together with the text. If one of 'question', 'info', 'warning' or 'error', a matching icon will be shown to hint the user about the type of message. Any other value will suppress the icon. - `actions`: a list of strings. For each string a pushbutton will be created which can be used to exit the dialog and remove the message. By default there is a single button labeled 'OK'. When the MessageBox is displayed with the :meth:`getResult()` method, a modal dialog is created, i.e. the user will have to click a button or hit the ESC key before he can continue. If you want a modeless dialog, allowing the user to continue while the message stays open, use the :meth:`show()` mehod to display it. """ class TextBox(QtGui.QDialog): """Display a text and wait for user response. Possible choices are 'OK' and 'CANCEL'. The function returns True if the OK button was clicked or 'ENTER' was pressed, False if the 'CANCEL' button was pressed or ESC was pressed. """ def __init__(self,text,format=None,actions=['OK',None],modal=None,parent=None,caption=None,mono=False,timeout=None,flags=None): if parent is None: parent = pf.GUI QtGui.QDialog.__init__(self,parent) if flags is not None: self.setWindowFlags(flags) if caption is None: caption = 'pyFormex-dialog' self.setWindowTitle('pyFormex Text Display') if modal is not None: self.setModal(modal) self._t = QtGui.QTextEdit() self._t.setReadOnly(True) updateText(self._t,text,format) self._b = ButtonBox('',actions,parent=self,stretch=[1,1]) l = QtGui.QVBoxLayout() l.addWidget(self._t) l.addWidget(self._b) self.setLayout(l) self.resize(800,400) if mono: font = QtGui.QFont("DejaVu Sans Mono") # font.setStyle(QtGui.QFont.StyleNormal) self.setFont(font) addTimeOut(self,timeout,"accept()") def getResult(self): return self.exec_() == QtGui.QDialog.Accepted def updateText(self,text,format=''): updateText(self._t,text,format) ## ############################# Input box ########################### ## class InputBox(QtGui.QWidget): ## """A widget accepting a line of input. ## """ ## def __init__(self,*args): ## QtGui.QWidget.__init__(self,*args) ## layout = InputString('Input:','') ## self.setLayout(layout) ############################# Button box ########################### # # THESE TWO SHOULD BE MERGED # def dialogButtons(dialog,actions=None,default=None): """Create a set of dialog buttons dia is a dialog widget actions is a list of tuples (name,) or (name,function). If a function is specified, it will be executed on pressing the button. If no function is specified, and name is one of 'ok' or 'cancel' (case is ignored), the button will be bound to the dialog's 'accept' or 'reject' slot. If actions==None (default), it will be set to the default ``[('Cancel',),('OK',)]``. Specify actions=[] if you want an empty dialogDuttons. default is the name of the action to set as the default. If no default is given, it is set to the LAST button. Returns a horizontal box layout with the buttons. """ if actions is None: actions = [('Cancel',),('OK',)] if actions and default is None: default = actions[-1][0].lower() but = QtGui.QHBoxLayout() spacer = QtGui.QSpacerItem(0,0,QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum ) but.addItem(spacer) for a in actions: name = a[0] b = QtGui.QPushButton(name,dialog) n = name.lower() if len(a) > 1: slot = (a[1],) elif n == 'ok': slot = (dialog,Accept) elif n == 'cancel': slot = (dialog,Reject) else: slot = (dialog,Reject) dialog.connect(b,QtCore.SIGNAL("clicked()"),*slot) if n == default.lower(): b.setDefault(True) but.addWidget(b) return but class ButtonBox(InputPush): """A box with action buttons. - `name`: a label to be displayed in front of the button box. An empty string will suppress it. - `actions`: a list of (button label, button function) tuples. The button function can be a normal callable function, or one of the values widgets.ACCEPTED or widgets.REJECTED. In the latter case, `parent` should be specified. - `parent`: the parent dialog holding this button box. It should be specified if one of the buttons actions is widgets.ACCEPTED or widgets.REJECTED. """ def __init__(self,name,actions=[],parent=None,stretch=[-1,-1]): InputPush.__init__(self,name,None,[a[0] for a in actions]) s = self.layout() for i in [0,-1]: if stretch[i] >= 0: s.insertStretch(i,stretch[i]) s.setSpacing(0) s.setMargin(0) for r,f in zip(self.rb,[a[1] for a in actions]): if f is None: f = Accept if callable(f): self.connect(r,QtCore.SIGNAL("clicked()"),f) else: self.connect(r,QtCore.SIGNAL("clicked()"),parent,f) self.buttons = s def __str__(self): s = '' for a in ['rect','minimumHeight']: s += "%s = %s\n" % (a,getattr(self,a)()) return s ############################# Coords box ########################### # BV: this should be merged into InputPoint class CoordsBox(QtGui.QWidget): """A widget displaying the coordinates of a point. """ def __init__(self,ndim=3,readonly=False,*args): QtGui.QWidget.__init__(self,*args) layout = QtGui.QHBoxLayout(self) self.validator = QtGui.QDoubleValidator(self) self.values = [] for name in ['x','y','z'][:ndim]: lbl = QtGui.QLabel(name) val = QtGui.QLineEdit('0.0') val.setValidator(self.validator) val.setReadOnly(readonly) layout.addWidget(lbl) layout.addWidget(val) self.values.append(val) self.setLayout(layout) def getValues(self): """Return the current x,y,z values as a list of floats.""" return [ float(val.text()) for val in self.values ] def setValues(self,values): """Set the three values of the widget.""" for v,val in zip(self.values,map(float,values)): v.setText(str(val)) ############################# ImageView ########################### class ImageView(QtGui.QLabel): """A widget displaying an image. """ def __init__(self,image=None,maxheight=None,parent=None): """Create a new ImageView widget.""" QtGui.QLabel.__init__(self,parent) self.setBackgroundRole(QtGui.QPalette.Base) self.setSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Minimum) if maxheight: self.setMaximumHeight(maxheight) if image is not None: self.showImage(image) def showImage(self,image): """Show an image in the viewer. image: either a filename or an existing QImage instance. If a filename, it should be an image file that can be read by the QImage constructor. Most image formats are understood by QImage. The variable gui.image.image_formats_qtr provides a list. """ if isinstance(image,QtGui.QImage): filename = None else: filename = str(image) image = QtGui.QImage(filename) if image.isNull(): raise ValueError,"Cannot load image file %s" % filename #print("Size %sx%s" % (image.width(),image.height())) self.setPixmap(QtGui.QPixmap.fromImage(image)) self.filename = filename self.image = image self.zoom = 1.0 ############################# Deprecated Features ########################### class OldTableDialog(GenericDialog): """_A dialog widget to show two-dimensional arrays of items.""" def __init__(self,items,caption=None,parent=None,tab=False): """_Create the Table dialog. If tab = False, a dialog with one table is created and items should be a list [table_header,table_data]. If tab = True, a dialog with multiple pages is created and items should be a list of pages [page_header,table_header,table_data]. """ warnings.warn('warn_old_table_dialog') GenericDialog.__init__(self,[],title=caption,parent=parent) if tab: contents = Tabs( [ (item[0], Table(data=item[2],chead=item[1],parent=None)) for item in items ], parent=parent) else: contents = Table(data=items[1],chead=items[0],parent=None) self.add(contents) self.show() # End pyformex-0.8.6/pyformex/gui/draw.py0000644000211500021150000016573111705104656017220 0ustar benebene00000000000000## $Id: draw.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Create 3D graphical representations. The draw module provides the basic user interface to the OpenGL rendering capabilities of pyFormex. The full contents of this module is available to scripts running in the pyFormex GUI without the need to import it. """ import pyformex as pf import threading,os,sys,types,copy,commands,time import numpy import utils import messages import widgets Dialog = widgets.InputDialog _I = widgets.simpleInputItem _G = widgets.groupInputItem _T = widgets.tabInputItem import toolbar import actors import decors import marks import image import canvas import colors import coords from mesh import Mesh from plugins import trisurface,tools,fe from script import * from signals import * from formex import * #################### Interacting with the user ############################### def closeGui(): """Close the GUI. Calling this function from a script closes the GUI and terminates pyFormex. """ pf.debug("Closing the GUI: currently, this will also terminate pyformex.") pf.GUI.close() def closeDialog(name): """Close the named dialog. Closes the InputDialog with the given name. If multiple dialogs are open with the same name, all these dialogs are closed. This only works for dialogs owned by the pyFormex GUI. """ pf.GUI.closeDialog(name) def showMessage(text,actions=['OK'],level='info',modal=True,**kargs): """Show a short message widget and wait for user acknowledgement. There are three levels of messages: 'info', 'warning' and 'error'. They differ only in the icon that is shown next to the test. By default, the message widget has a single button with the text 'OK'. The dialog is closed if the user clicks a button. The return value is the button text. """ w = widgets.MessageBox(text,level=level,actions=actions,**kargs) if modal: return w.getResult() else: w.show() return None def showInfo(text,actions=['OK'],modal=True): """Show an informational message and wait for user acknowledgement.""" return showMessage(text,actions,'info',modal) def warning(text,actions=['OK']): """Show a warning message and wait for user acknowledgement.""" return showMessage(text,actions,'warning') def error(text,actions=['OK']): """Show an error message and wait for user acknowledgement.""" return showMessage(text,actions,'error') def ask(question,choices=None,**kargs): """Ask a question and present possible answers. Return answer if accepted or default if rejected. The remaining arguments are passed to the InputDialog getResult method. """ return showMessage(question,choices,'question',**kargs) def ack(question,**kargs): """Show a Yes/No question and return True/False depending on answer.""" return ask(question,['No','Yes'],**kargs) == 'Yes' def showText(text,type=None,actions=[('OK',None)],modal=True,mono=False): """Display a text and wait for user response. This opens a TextBox widget and displays the text in the widget. Scrollbars will be added if the text is too large to display at once. The text can be plain text format. Some rich text formats will be recognized and rendered appropriately. See widgets.TextBox. `mono=True` forces the use of a monospaced font. """ ## w = widgets.TextBox(text,type,actions,modal=modal,mono=mono) if mono: font = "DejaVu Sans Mono" else: font = None w = Dialog( items=[_I('text',text,itemtype='text',text='',font=font,size=(-1,-1))], modal=modal, actions=[('Ok',)] ) if modal: return w.getResult() else: w.show() return w.results def showFile(filename,mono=False,**kargs): """Display a text file. This will use the :func:`showText()` function to display a text read from a file. By default this uses a monospaced font. Other arguments may also be passed to ShowText. """ try: f = open(filename,'r') except IOError: return showText(f.read(),mono=mono,**kargs) f.close() def showDescription(filename=None): """Show the Description part of the docstring of a pyFormex script. If no file name is specified, the current script is used. If the script's docstring has no Description part, a default text is shown. """ from scriptMenu import getDocString,getDescription if pf.GUI.canPlay: scriptfile = pf.prefcfg['curfile'] doc = getDocString(scriptfile) des = getDescription(doc) if len(des.strip()) == 0: des = """.. NoDescription No help available ================= The maintainers of this script have not yet added a description for this example. You can study the source code, and if anything is unclear, ask for help on the pyFormex `Support tracker <%s>`_. """ % pf.cfg['help/support'] showText(des,modal=False) # widget and result status of the widget in askItems() function _dialog_widget = None _dialog_result = None def askItems(items,caption=None,timeout=None,**kargs): """Ask the value of some items to the user. Create an interactive widget to let the user set the value of some items. 'items' is a list of input items (basically [key,value] pairs). See the widgets.InputDialog class for complete description of the available input items. Two InputDialog classes are defined in gui.widgets. The OldInputDialog class is deprecated in favor of InputDialog, which has become the default as of pyFormex 0.8.3. The two classes differ in how the input is specified. In the new format, each input item is either a simpleInputItem, a groupInputItem or a tabInputItem. You can specify 'legacy=False' to indicate that you are using the new format, or 'legacy=True' if your data are in the old format. The default ('legacy = None'), will make this function try to detect the format and convert the input items to the proper new format. This conversion will work on most, but not all legacy formats that have been used in the past by pyFormex. Since the legacy format is scheduled to be withdrawn in future, users are encouraged to change their input to the new format. The remaining arguments are keyword arguments that are passed to the InputDialog.getResult method. A timeout (in seconds) can be specified to have the input dialog interrupted automatically. Return a dictionary with the results: for each input item there is a (key,value) pair. Returns an empty dictionary if the dialog was canceled. Sets the dialog timeout and accepted status in global variables. """ global _dialog_widget,_dialog_result if 'legacy' in kargs: warnings.warn("The use of the 'legacy' argument in askitems is deprecated.") # convert items, allows for sloppy style items = [ widgets.convertInputItem(i) for i in items ] w = widgets.InputDialog(items,caption,**kargs) _dialog_widget = w _dialog_result = None res = w.getResult(timeout) _dialog_widget = None _dialog_result = w.result() return res def currentDialog(): """Returns the current dialog widget. This returns the dialog widget created by the askItems() function, while the dialog is still active. If no askItems() has been called or if the user already closed the dialog, None is returned. """ return _dialog_widget def dialogAccepted(): """Returns True if the last askItems() dialog was accepted.""" return _dialog_result == widgets.ACCEPTED def dialogRejected(): """Returns True if the last askItems() dialog was rejected.""" return _dialog_result == widgets.REJECTED def dialogTimedOut(): """Returns True if the last askItems() dialog timed out.""" return _dialog_result == widgets.TIMEOUT def askFilename(cur=None,filter="All files (*.*)",exist=True,multi=False,change=True): """Ask for a file name or multiple file names using a file dialog. cur is a directory or filename. All the files matching the filter in that directory (or that file's directory) will be shown. If cur is a file, it will be selected as the current filename. Unless the user cancels the operation, or the change parameter was set to False, the parent directory of the selected file will become the new working directory. """ if cur is None: cur = pf.cfg['workdir'] if os.path.isdir(cur): fn = '' else: fn = os.path.basename(cur) cur = os.path.dirname(cur) w = widgets.FileSelection(cur,filter,exist,multi) if fn: w.selectFile(fn) fn = w.getFilename() if fn and change: if multi: chdir(fn[0]) else: chdir(fn) pf.GUI.update() pf.app.processEvents() return fn def askNewFilename(cur=None,filter="All files (*.*)"): """Ask a single new filename. This is a convenience function for calling askFilename with the arguments exist=False. """ return askFilename(cur=cur,filter=filter,exist=False,multi=False) def askDirname(path=None,change=True): """Interactively select a directory and change the current workdir. The user is asked to select a directory through the standard file dialog. Initially, the dialog shows all the subdirectories in the specified path, or by default in the current working directory. The selected directory becomes the new working directory, unless the user canceled the operation, or the change parameter was set to False. """ if path is None: path = pf.cfg['workdir'] if not os.path.isdir(path): path = os.path.dirname(path) fn = widgets.FileSelection(path,'*',dir=True).getFilename() if fn and change: chdir(fn) pf.GUI.update() #pf.canvas.update() pf.app.processEvents() return fn def askImageFile(fn=None): if not fn: fn = pf.cfg['pyformexdir'] filt = map(utils.fileDescription,['img','all']) return askFilename(fn,filter=filt,multi=False,exist=True) def checkWorkdir(): """Ask the user to change the current workdir if it is not writable. Returns True if the new workdir is writable. """ workdir = os.getcwd() ok = os.access(workdir,os.W_OK) if not ok: warning("Your current working directory (%s) is not writable. Change your working directory to a path where you have write permission." % workdir) askDirname() ok = os.access(os.getcwd(),os.W_OK) return ok logfile = None # the log file def printMessage(s): """Print a message on the message board. If a logfile was opened, the message is also written to the log file. """ if logfile is not None: logfile.write(str(s)+'\n') pf.GUI.board.write(str(s)) pf.GUI.update() pf.app.processEvents() # message is the preferred function to send text info to the user. # The default message handler is set here. message = printMessage ############################## drawing functions ######################## def flatten(objects,recurse=True): """Flatten a list of geometric objects. Each item in the list should be either: - a drawable object, - a string with the name of such an object, - a list of any of these three. This function will flatten the lists and replace the string items with the object they point to. The result is a single list of drawable objects. This function does not enforce the objects to be drawable. That should be done by the caller. """ r = [] for i in objects: if type(i) == str: i = named(i) if type(i) == list: if recurse: r.extend(flatten(i,True)) else: r.extend(i) else: r.append(i) return r def drawable(objects): """Filters the drawable objects from a list. The input is a list, usually of drawable objects. For each item in the list, the following is done: - if the item is drawable, it is kept as is, - if the item is not drawable but can be converted to a Formex, it is converted, - if it is neither drawable nor convertible to Formex, it is removed. The result is a list of drawable objects (since a Formex is drawable). """ def fltr(i): if hasattr(i,'actor'): return i elif hasattr(i,'toFormex'): return i.toFormex() else: return None r = [ fltr(i) for i in objects ] return [ i for i in r if i is not None ] def draw(F, view=None,bbox=None, color='prop',colormap=None,bkcolor=None,bkcolormap=None,alpha=None, mode=None,linewidth=None,linestipple=None,shrink=None,marksize=None, wait=True,clear=None,allviews=False, highlight=False,nolight=False,ontop=False, silent=True, **kargs): """Draw object(s) with specified settings and direct camera to it. The first argument is an object to be drawn. All other arguments are settings that influence how the object is being drawn. F is one of: - a drawable object (a geometry object like Formex, Mesh or TriSurface), - the name of a global pyFormex variable refering to such an object, - a list of any of these three items. The variables are replaced with their value and the lists are flattened, to create a single list of objects. This then filtered for the drawable objects, and the resulting list of drawables is drawn using the remaining arguments. The remaining arguments are drawing options. If None, they are filled in from the current viewport drawing options. view is either the name of a defined view or 'last' or None. Predefined views are 'front','back','top','bottom','left','right','iso'. With view=None the camera settings remain unchanged (but might be changed interactively through the user interface). This may make the drawn object out of view! With view='last', the camera angles will be set to the same camera angles as in the last draw operation, undoing any interactive changes. The initial default view is 'front' (looking in the -z direction). bbox specifies the 3D volume at which the camera will be aimed (using the angles set by view). The camera position wil be set so that the volume comes in view using the current lens (default 45 degrees). bbox is a list of two points or compatible (array with shape (2,3)). Setting the bbox to a volume not enclosing the object may make the object invisible on the canvas. The special value bbox='auto' will use the bounding box of the objects getting drawn (object.bbox()), thus ensuring that the camera will focus on these objects. The special value bbox=None will use the bounding box of the previous drawing operation, thus ensuring that the camera's target volume remains unchanged. color,colormap,linewidth,alpha,marksize are passed to the creation of the 3D actor. if color is None, it is drawn with the color specified on creation. if color == 'prop' and a colormap was installed, props define color. else, color should be an array of RGB values, either with shape (3,) for a single color, or (nelems,3) for differently colored elements shrink is a floating point shrink factor that will be applied to object before drawing it. If the Formex has properties and a color list is specified, then the the properties will be used as an index in the color list and each member will be drawn with the resulting color. If color is one color value, the whole Formex will be drawn with that color. Finally, if color=None is specified, the whole Formex is drawn in black. Each draw action activates a locking mechanism for the next draw action, which will only be allowed after drawdelay seconds have elapsed. This makes it easier to see subsequent images and is far more elegant that an explicit sleep() operation, because all script processing will continue up to the next drawing instruction. The value of drawdelay is set in the config, or 2 seconds by default. The user can disable the wait cycle for the next draw operation by specifying wait=False. Setting drawdelay=0 will disable the waiting mechanism for all subsequent draw statements (until set >0 again). """ if 'flat' in kargs: utils.warn('warn_flat_removed',DeprecationWarning,stacklevel=2) # For simplicity of the code, put objects to draw always in a list if type(F) is list: FL = F else: FL = [ F ] # Flatten the list, replacing named objects with their value FL = flatten(FL) ntot = len(FL) # Transform to list of drawable objects FL = drawable(FL) nres = len(FL) if nres < ntot and not silent: raise ValueError,"Data contains undrawable objects (%s/%s)" % (ntot-nres,ntot) # Fill in the remaining defaults if bbox is None: bbox = pf.canvas.options.get('bbox','auto') if shrink is None: shrink = pf.canvas.options.get('shrink',None) if marksize is None: marksize = pf.canvas.options.get('marksize',pf.cfg.get('marksize',5.0)) if alpha is None: alpha = pf.canvas.options.get('alpha',0.5) # Shrink the objects if requested if shrink is not None: FL = [ _shrink(F,shrink) for F in FL ] # Execute the drawlock wait before doing first canvas change pf.GUI.drawlock.wait() if clear is None: clear = pf.canvas.options.get('clear',False) if clear: clear_canvas() if view is not None and view != 'last': pf.debug("SETTING VIEW to %s" % view) setView(view) pf.GUI.setBusy() pf.app.processEvents() try: actors = [] # loop over the objects for F in FL: #print "DRAW %s" % type(F) # Create the colors if color == 'prop': try: Fcolor = F.prop except: Fcolor = colors.black elif color == 'random': # create random colors Fcolor = numpy.random.rand(F.nelems(),3) else: Fcolor = color # Create the actor actor = F.actor(color=Fcolor,colormap=colormap,bkcolor=bkcolor,bkcolormap=bkcolormap,alpha=alpha,mode=mode,linewidth=linewidth,linestipple=linestipple,marksize=marksize,nolight=nolight,ontop=ontop,**kargs) actors.append(actor) if actor is not None: # Show the actor if highlight: pf.canvas.addHighlight(actor) else: pf.canvas.addActor(actor) # Adjust the camera #print "adjusting camera" #print "VIEW = %s; BBOX = %s" % (view,bbox) if view is not None or bbox not in [None,'last']: if view == 'last': view = pf.canvas.options['view'] if bbox == 'auto': bbox = coords.bbox(FL) #print("SET CAMERA TO: bbox=%s, view=%s" % (bbox,view)) pf.canvas.setCamera(bbox,view) #setView(view) pf.canvas.update() pf.app.processEvents() #pf.debug("AUTOSAVE %s" % image.autoSaveOn()) if image.autoSaveOn(): image.saveNext() if wait: # make sure next drawing operation is retarded pf.GUI.drawlock.lock() finally: pf.GUI.setBusy(False) if type(F) is list or len(actors) != 1: return actors else: return actors[0] # Commented in 0.8.5 ## def olddraw(F, ## view=None,bbox=None, ## color='prop',colormap=None,bkcolor=None,bkcolormap=None,alpha=None, ## mode=None,linewidth=None,linestipple=None,shrink=None,marksize=None, ## wait=True,clear=None,allviews=False, ## highlight=False,nolight=False,ontop=False,**kargs): ## """_Old recursive draw function""" ## if 'flat' in kargs: ## import warnings ## warnings.warn('warn_flat_removed',DeprecationWarning,stacklevel=2) ## # Facility for drawing database objects by name ## if type(F) == str: ## F = named(F) ## if F is None: ## return None ## # We need to get the default for bbox before processing a list, ## # because bbox should be set only once for the whole list of objects ## if bbox is None: ## bbox = pf.canvas.options.get('bbox','auto') ## if type(F) == list: ## actor = [] ## nowait = False ## save_bbox = bbox ## for Fi in F: ## if Fi is F[-1]: ## nowait = wait ## actor.append(olddraw(Fi,view,bbox,color,colormap,bkcolor,bkcolormap,alpha,mode,linewidth,linestipple,shrink,marksize,wait=nowait,clear=clear,allviews=allviews,highlight=highlight,nolight=nolight,ontop=ontop,**kargs)) ## if Fi is F[0]: ## clear = False ## view = None ## bbox = 'last' ## bbox = save_bbox ## if bbox == 'auto': ## bbox = coords.bbox(actor) ## pf.canvas.setCamera(bbox,view) ## pf.canvas.update() ## return actor ## # We now should have a single object to draw ## # Check if it is something we can draw ## if not hasattr(F,'actor') and hasattr(F,'toFormex'): ## pf.debug("CONVERTING %s TO FORMEX TO ENABLE DRAWING" % type(F)) ## F = F.toFormex() ## if not hasattr(F,'actor'): ## # Don't know how to draw this object ## raise RuntimeError,"draw() can not draw objects of type %s" % type(F) ## # Fill in the remaining defaults ## if shrink is None: ## shrink = pf.canvas.options.get('shrink',None) ## if marksize is None: ## marksize = pf.canvas.options.get('marksize',pf.cfg.get('marksize',5.0)) ## if alpha is None: ## alpha = pf.canvas.options.get('alpha',0.5) ## # Create the colors ## if color == 'prop': ## if hasattr(F,'p'): ## color = F.prop ## elif hasattr(F,'prop'): ## color = F.prop ## else: ## color = colors.black ## elif color == 'random': ## # create random colors ## color = numpy.random.rand(F.nelems(),3) ## pf.GUI.drawlock.wait() ## if clear is None: ## clear = pf.canvas.options.get('clear',False) ## if clear: ## clear_canvas() ## if view is not None and view != 'last': ## pf.debug("SETTING VIEW to %s" % view) ## setView(view) ## pf.GUI.setBusy() ## pf.app.processEvents() ## if shrink is not None: ## F = _shrink(F,shrink) ## try: ## actor = F.actor(color=color,colormap=colormap,bkcolor=bkcolor,bkcolormap=bkcolormap,alpha=alpha,mode=mode,linewidth=linewidth,linestipple=linestipple,marksize=marksize,nolight=nolight,ontop=ontop,**kargs) ## if actor is None: ## return None ## if highlight: ## pf.canvas.addHighlight(actor) ## else: ## pf.canvas.addActor(actor) ## if view is not None or bbox not in [None,'last']: ## pf.debug("CHANGING VIEW to %s" % view) ## if view == 'last': ## view = pf.canvas.options['view'] ## if bbox == 'auto': ## bbox = F.bbox() ## pf.debug("SET CAMERA TO: bbox=%s, view=%s" % (bbox,view)) ## pf.canvas.setCamera(bbox,view) ## #setView(view) ## pf.canvas.update() ## pf.app.processEvents() ## #pf.debug("AUTOSAVE %s" % image.autoSaveOn()) ## if image.autoSaveOn(): ## image.saveNext() ## if wait: # make sure next drawing operation is retarded ## pf.GUI.drawlock.lock() ## finally: ## pf.GUI.setBusy(False) ## return actor ## if pf.options.olddraw: ## draw = olddraw def _setFocus(object,bbox,view): """Set focus after a draw operation""" if view is not None or bbox not in [None,'last']: if view == 'last': view = pf.canvas.options['view'] if bbox == 'auto': bbox = coords.bbox(object) pf.canvas.setCamera(bbox,view) pf.canvas.update() def focus(object): """Move the camera thus that object comes fully into view. object can be anything having a bbox() method or a list thereof. if no view is given, the default is used. The camera is moved with fixed axis directions to a place where the whole object can be viewed using a 45. degrees lens opening. This technique may change in future! """ pf.canvas.setCamera(bbox=coords.bbox(object)) pf.canvas.update() def setDrawOptions(kargs0={},**kargs): """Set default values for the draw options. Draw options are a set of options that hold default values for the draw() function arguments and for some canvas settings. The draw options can be specified either as a dictionary, or as keyword arguments. """ d = {} d.update(kargs0) d.update(kargs) pf.canvas.setOptions(d) def showDrawOptions(): pf.message("Current Drawing Options: %s" % pf.canvas.options) pf.message("Current Viewport Options: %s" % pf.canvas.settings) def askDrawOptions(d={}): """Interactively ask the Drawing options from the user. A dictionary may be specified to override the current defaults. """ setDrawOptions(d) res = askItems(pf.canvas.options.items()) setDrawOptions(res) def reset(): pf.canvas.resetDefaults() pf.canvas.resetOptions() pf.GUI.drawwait = pf.cfg['draw/wait'] clear() view('front') def resetAll(): wireframe() reset() def shrink(v): setDrawOptions({'shrink':v}) def _shrink(F,factor): """Return a shrinked object. A shrinked object is one where each element is shrinked with a factor around its own center. """ if not isinstance(F,Formex): F = F.toFormex() return F.shrink(factor) def drawVectors(P,v,size=None,nolight=True,**drawOptions): """Draw a set of vectors. If size==None, draws the vectors v at the points P. If size is specified, draws the vectors size*normalize(v) P, v and size are single points or sets of points. If sets, they should be of the same size. Other drawoptions can be specified and will be passed to the draw function. """ if size is None: Q = P + v else: Q = P + size*normalize(v) return draw(connect([P,Q]),nolight=nolight,**drawOptions) def drawPlane(P,N,size): from plugins.tools import Plane p = Plane(P,N,size) return draw(p,bbox='last') def drawMarks(X,M,color='black',leader='',ontop=True): """Draw a list of marks at points X. X is a Coords array. M is a list with the same length as X. The string representation of the marks are drawn at the corresponding 3D coordinate. """ _large_ = 20000 if len(M) > _large_: if not ack("You are trying to draw marks at %s points. This may take a long time, and the results will most likely not be readible anyway. If you insist on drawing these marks, anwer YES." % len(M)): return None M = marks.MarkList(X,M,color=color,leader=leader,ontop=ontop) pf.canvas.addAnnotation(M) pf.canvas.numbers = M pf.canvas.update() return M def drawFreeEdges(M,color='black'): """Draw the feature edges of a Mesh""" print "DRAW FREE EDGES" B = M.getFreeEdgesMesh() print B draw(B,color=color,nolight=True) def drawNumbers(F,numbers=None,color='black',trl=None,offset=0,leader='',ontop=True): """Draw numbers on all elements of F. numbers is an array with F.nelems() integer numbers. If no numbers are given, the range from 0 to nelems()-1 is used. Normally, the numbers are drawn at the centroids of the elements. A translation may be given to put the numbers out of the centroids, e.g. to put them in front of the objects to make them visible, or to allow to view a mark at the centroids. If an offset is specified, it is added to the shown numbers. """ try: X = F.centroids() except: return None if trl is not None: X = X.trl(trl) X = X.reshape(-1,3) if numbers is None: numbers = numpy.arange(X.shape[0]) return drawMarks(X,numbers+offset,color=color,leader=leader,ontop=ontop) def drawPropNumbers(F,**kargs): """Draw property numbers on all elements of F. This calls drawNumbers to draw the property numbers on the elements. All arguments of drawNumbers except `numbers` may be passed. If the object F thus not have property numbers, -1 values are drawn. """ if F.prop is None: nrs = [ -1 ] * self.nelems() else: nrs = F.prop drawNumbers(F,nrs,**kargs) def drawVertexNumbers(F,color='black',trl=None,ontop=False): """Draw (local) numbers on all vertices of F. Normally, the numbers are drawn at the location of the vertices. A translation may be given to put the numbers out of the location, e.g. to put them in front of the objects to make them visible, or to allow to view a mark at the vertices. """ FC = F.coords.reshape((-1,3)) if trl is not None: FC = FC.trl(trl) return drawMarks(FC,numpy.resize(numpy.arange(F.coords.shape[-2]),(FC.shape[0])),color=color,ontop=ontop) def drawText3D(P,text,color=None,font='sans',size=18,ontop=True): """Draw a text at a 3D point P.""" M = marks.TextMark(P,text,color=color,font=font,size=size,ontop=ontop) pf.canvas.addAnnotation(M) pf.canvas.update() return M def drawAxes(*args,**kargs): """Draw the axes of a CoordinateSystem. This draws an AxesActor corresponding to the specified Coordinatesystem. The arguments are the same as those of the AxesActor constructor. """ A = actors.AxesActor(*args,**kargs) drawActor(A) return A def drawImage(image,nx=-1,ny=-1,pixel='dot'): """Draw an image as a colored Formex Draws a raster image as a colored Formex. While there are other and better ways to display an image in pyFormex (such as using the imageView widget), this function allows for interactive handling the image using the OpenGL infrastructure. Parameters: - `image`: a QImage holding a raster image. An image can be loaded from most standard image files using the :func:`loadImage` function - `nx`,`ny`: resolution you want to use for the display - `pixel`: the Formex representing a single pixel. It should be either a single element Formex, or one of the strings 'dot' or 'quad'. If 'dot' a single point will be used, if 'quad' a unit square. The difference will be important when zooming in. The default is 'dot'. """ pf.GUI.setBusy() from gui.imagecolor import image2glcolor w,h = image.width(),image.height() if nx <= 0: nx = w if ny <= 0: ny = h if nx != w or ny != h: image = image.scaled(nx,ny) # Create the colors color,colortable = image2glcolor(image) # Create a 2D grid of nx*ny elements # !! THIS CAN PROBABLY BE DONE FASTER if isinstance(pixel,Formex) and pixel.nelems()==1: F = pixel elif pixel == 'quad': F = Formex('4:0123') else: F = Formex(origin()) F = F.replic2(nx,ny).centered() # Draw the grid using the image colors FA = draw(F,color=color,colormap=colortable,nolight=True) pf.GUI.setBusy(False) return FA def drawViewportAxes3D(pos,color=None): """Draw two viewport axes at a 3D position.""" M = marks.AxesMark(pos,color) annotate(M) return M def drawBbox(A): """Draw the bbox of the actor A.""" B = actors.BboxActor(A.bbox()) annotate(B) return B def drawActor(A): """Draw an actor and update the screen.""" pf.canvas.addActor(A) pf.canvas.update() def undraw(itemlist): """Remove an item or a number of items from the canvas. Use the return value from one of the draw... functions to remove the item that was drawn from the canvas. A single item or a list of items may be specified. """ pf.canvas.remove(itemlist) pf.canvas.update() pf.app.processEvents() def view(v,wait=True): """Show a named view, either a builtin or a user defined. This shows the current scene from another viewing angle. Switching views of a scene is much faster than redrawing a scene. Therefore this function is prefered over :func:`draw` when the actors in the scene remain unchanged and only the camera viewpoint changes. Just like :func:`draw`, this function obeys the drawing lock mechanism, and by default it will restart the lock to retard the next draing operation. """ pf.GUI.drawlock.wait() if v != 'last': angles = pf.canvas.view_angles.get(v) if not angles: warning("A view named '%s' has not been created yet" % v) return pf.canvas.setCamera(None,angles) setView(v) pf.canvas.update() if wait: pf.GUI.drawlock.lock() def setTriade(on=None,pos='lb',siz=100): """Toggle the display of the global axes on or off. If on is True, the axes triade is displayed, if False it is removed. The default (None) toggles between on and off. """ pf.canvas.setTriade(on,pos,siz) pf.canvas.update() pf.app.processEvents() def drawText(text,x,y,gravity='E',font='helvetica',size=14,color=None,zoom=None): """Show a text at position x,y using font.""" TA = decors.Text(text,x,y,gravity=gravity,font=font,size=size,color=color,zoom=zoom) decorate(TA) return TA def annotate(annot): """Draw an annotation.""" pf.canvas.addAnnotation(annot) pf.canvas.update() def unannotate(annot): pf.canvas.removeAnnotation(annot) pf.canvas.update() def decorate(decor): """Draw a decoration.""" pf.canvas.addDecoration(decor) pf.canvas.update() def undecorate(decor): pf.canvas.removeDecoration(decor) pf.canvas.update() def createView(name,angles,addtogui=False): """Create a new named view (or redefine an old). The angles are (longitude, latitude, twist). By default, the view is local to the script's viewport. If gui is True, it is also added to the GUI. """ pf.canvas.view_angles[name] = angles if addtogui: pf.GUI.createView(name,angles) def setView(name,angles=None): """Set the default view for future drawing operations. If no angles are specified, the name should be an existing view, or the predefined value 'last'. If angles are specified, this is equivalent to createView(name,angles) followed by setView(name). """ if name != 'last' and angles: createView(name,angles) setDrawOptions({'view':name}) def saveView(name,addtogui=False): pf.GUI.saveView(name) def frontView(): view("front") def backView(): view("back") def leftView(): view("left") def rightView(): view("right") def topView(): view("top"); def bottomView(): view("bottom") def isoView(): view("iso") def bgcolor(color,color2=None,mode='h'): """Change the background color (and redraw). If one color is given, the background is a solid color. If two colors are given, the background color will get a vertical gradient with color on top and color2 at the bottom. """ pf.canvas.setBgColor(color,color2,mode) pf.canvas.display() pf.canvas.update() def fgcolor(color): """Set the default foreground color.""" pf.canvas.setFgColor(color) def colormap(color=None): """Gets/Sets the current canvas color map""" return pf.canvas.settings.colormap def renderMode(mode,avg=False,light=None): #print "DRAW.RENDERMODE" #print "CANVAS %s" % pf.canvas #print "MODE %s" % pf.canvas.rendermode # ERROR The following redraws twice !!! pf.canvas.setRenderMode(mode,light) #print "NEW MODE %s" % pf.canvas.rendermode pf.canvas.update() toolbar.updateNormalsButton() toolbar.updateLightButton() pf.GUI.processEvents() #print "DONE DRAW>RENDERMODE" def wireframe(): renderMode("wireframe") def smooth(): renderMode("smooth") def smoothwire(): renderMode("smoothwire") def flat(): renderMode("flat") def flatwire(): renderMode("flatwire") def smooth_avg(): renderMode("smooth",True) ## def opacity(alpha): ## """Set the viewports transparency.""" ## pf.canvas.alpha = float(alpha) def lights(state=True): """Set the lights on or off""" pf.canvas.setLighting(state) pf.canvas.update() toolbar.updateLightButton() pf.GUI.processEvents() def transparent(state=True): """Set the transparency mode on or off.""" pf.canvas.setToggle('alphablend',state) pf.canvas.update() toolbar.updateTransparencyButton() pf.GUI.processEvents() def perspective(state=True): pf.canvas.camera.setPerspective(state) pf.canvas.update() toolbar.updatePerspectiveButton() pf.GUI.processEvents() def timeout(state=None): toolbar.timeout(state) def set_material_value(typ,val): """Set the value of one of the material lighting parameters typ is one of 'ambient','specular','emission','shininess' val is a value between 0.0 and 1.0 """ setattr(pf.canvas,typ,val) pf.canvas.setLighting(True) pf.canvas.update() pf.app.processEvents() def set_light(light,**args): light = int(light) pf.canvas.lights.set(light,**args) pf.canvas.setLighting(True) pf.canvas.update() pf.app.processEvents() def set_light_value(light,key,val): light = int(light) pf.canvas.lights.set_value(light,key,val) pf.canvas.setLighting(True) pf.canvas.update() pf.app.processEvents() def linewidth(wid): """Set the linewidth to be used in line drawings.""" pf.canvas.setLineWidth(wid) def linestipple(factor,pattern): """Set the linewidth to be used in line drawings.""" pf.canvas.setLineStipple(factor,pattern) def pointsize(siz): """Set the size to be used in point drawings.""" pf.canvas.setPointSize(siz) def canvasSize(width,height): """Resize the canvas to (width x height).""" pf.canvas.resize(width,height) def clear_canvas(): """Clear the canvas. This is a low level function not intended for the user. """ pf.canvas.removeAll() pf.canvas.clear() def clear(): """Clear the canvas. Removes everything from the current scene and displays an empty background. This function waits for the drawing lock to be released, but will not reset it. """ pf.GUI.drawlock.wait() clear_canvas() pf.canvas.update() def redraw(): pf.canvas.redrawAll() pf.canvas.update() def delay(s=None): """Get/Set the draw delay time. Returns the current setting of the draw wait time (in seconds). This drawing delay is obeyed by drawing and viewing operations. A parameter may be given to set the delay time to a new value. It should be convertable to a float. The function still returns the old setting. This may be practical to save that value to restore it later. """ saved = pf.GUI.drawwait if s is not None: pf.GUI.drawwait = float(s) return saved def wait(relock=True): """Wait until the drawing lock is released. This uses the drawing lock mechanism to pause. The drawing lock ensures that subsequent draws are retarded to give the user the time to view. This use of this function is prefered over that of :func:`pause` or :func:`sleep`, because it allows your script to continue the numerical computations while waiting to draw the next screen. This function can be used to retard other functions than ` """ pf.GUI.drawlock.wait() if relock: pf.GUI.drawlock.lock() def fforward(): """Releases the drawing lock mechanism indefinely. Releasing the drawing lock indefinely means that the lock will not be set again and your script will execute till the end. """ pf.GUI.drawlock.free() def step(): """Perform one step of a script. A step is a set of instructions until the next draw operation. If a script is running, this just releases the draw lock. Else, it starts the script in step mode. """ if pf.GUI.drawlock.locked: pf.GUI.drawlock.release() else: if ack(""" STEP MODE is currently only possible with specially designed, very well behaving scripts. If you're not sure what you are doing, you should cancel the operation now. Are you REALLY SURE you want to run this script in step mode? """): play(step=True) def pause(msg="Use the Step or Continue button to proceed",timeout=None): """Pause the execution until an external event occurs or timeout. When the pause statement is executed, execution of the pyformex script is suspended until some external event forces it to proceed again. Clicking the STEP or CONTINUE button will produce such an event. """ from gui.drawlock import repeat def _continue_(): return pf.GUI.drawlock.locked pf.debug("PAUSE ACTIVATED!") if msg: pf.message(msg) pf.GUI.drawlock.release() if pf.GUI.drawlock.allowed: pf.GUI.drawlock.locked = True if timeout is None: timeout = widgets.input_timeout repeat(_continue_,timeout) ################### EXPERIMENTAL STUFF: AVOID! ############### _wakeup_mode=0 sleeping = False timer = None def sleep(timeout=None): """Sleep until key/mouse press in the canvas or until timeout""" utils.warn('warn_avoid_sleep') # global sleeping,_wakeup_mode,timer if _wakeup_mode > 0 or timeout == 0: # don't bother return # prepare for getting wakeup event onSignal(WAKEUP,wakeup) # create a Timer to wakeup after timeout if timeout and timeout > 0: timer = threading.Timer(timeout,wakeup) timer.start() else: timer = None # go into sleep mode sleeping = True ## while sleeping, we have to process events ## (we could start another thread for this) while sleeping: pf.app.processEvents() # And we have to sleep in between, or we would be using to much # processor time idling. 0.1 is a good compromise to get some # responsitivity while not pegging the cpu time.sleep(0.01) # ignore further wakeup events offSignal(WAKEUP,wakeup) def wakeup(mode=0): """Wake up from the sleep function. This is the only way to exit the sleep() function. Default is to wake up from the current sleep. A mode > 0 forces wakeup for longer period. """ global timer,sleeping,_wakeup_mode if timer: timer.cancel() sleeping = False _wakeup_mode = mode ########################## print information ################################ def printbbox(): pf.message(pf.canvas.bbox) def printviewportsettings(): pf.GUI.viewports.printSettings() def reportCamera(): print(pf.canvas.camera.report()) #################### camera ################################## def zoom_factor(factor=None): if factor is None: factor = pf.cfg['gui/zoomfactor'] return float(factor) def pan_factor(factor=None): if factor is None: factor = pf.cfg['gui/panfactor'] return float(factor) def rot_factor(factor=None): if factor is None: factor = pf.cfg['gui/rotfactor'] return float(factor) def zoomIn(factor=None): pf.canvas.camera.zoomArea(1./zoom_factor(factor)) pf.canvas.update() def zoomOut(factor=None): pf.canvas.camera.zoomArea(zoom_factor(factor)) pf.canvas.update() def panRight(factor=None): pf.canvas.camera.transArea(-pan_factor(factor),0.) pf.canvas.update() def panLeft(factor=None): pf.canvas.camera.transArea(pan_factor(factor),0.) pf.canvas.update() def panUp(factor=None): pf.canvas.camera.transArea(0.,-pan_factor(factor)) pf.canvas.update() def panDown(factor=None): pf.canvas.camera.transArea(0.,pan_factor(factor)) pf.canvas.update() def rotRight(factor=None): pf.canvas.camera.rotate(rot_factor(factor),0,1,0) pf.canvas.update() def rotLeft(factor=None): pf.canvas.camera.rotate(-rot_factor(factor),0,1,0) pf.canvas.update() def rotUp(factor=None): pf.canvas.camera.rotate(-rot_factor(factor),1,0,0) pf.canvas.update() def rotDown(factor=None): pf.canvas.camera.rotate(rot_factor(factor),1,0,0) pf.canvas.update() def twistLeft(factor=None): pf.canvas.camera.rotate(rot_factor(factor),0,0,1) pf.canvas.update() def twistRight(factor=None): pf.canvas.camera.rotate(-rot_factor(factor),0,0,1) pf.canvas.update() def transLeft(factor=None): val = pan_factor(factor) * pf.canvas.camera.getDist() pf.canvas.camera.translate(-val,0,0,pf.cfg['draw/localaxes']) pf.canvas.update() def transRight(factor=None): val = pan_factor(factor) * pf.canvas.camera.getDist() pf.canvas.camera.translate(+val,0,0,pf.cfg['draw/localaxes']) pf.canvas.update() def transDown(factor=None): val = pan_factor(factor) * pf.canvas.camera.getDist() pf.canvas.camera.translate(0,-val,0,pf.cfg['draw/localaxes']) pf.canvas.update() def transUp(factor=None): val = pan_factor(factor) * pf.canvas.camera.getDist() pf.canvas.camera.translate(0,+val,0,pf.cfg['draw/localaxes']) pf.canvas.update() def dollyIn(factor=None): pf.canvas.camera.dolly(1./zoom_factor(factor)) pf.canvas.update() def dollyOut(factor=None): pf.canvas.camera.dolly(zoom_factor(factor)) pf.canvas.update() def lockCamera(): pf.canvas.camera.lock() def unlockCamera(): pf.canvas.camera.lock(False) def zoomRectangle(): """Zoom a rectangle selected by the user.""" pf.canvas.start_rectangle_zoom() pf.canvas.update() def zoomBbox(bb): """Zoom thus that the specified bbox becomes visible.""" pf.canvas.setBbox(bb) pf.canvas.setCamera() pf.canvas.update() def zoomAll(): """Zoom thus that all actors become visible.""" if pf.canvas.actors: zoomBbox(bbox(pf.canvas.actors)) # Can this be replaced with zoomIn/Out? def zoom(f): pf.canvas.zoom(f) pf.canvas.update() def flyAlong(path,upvector=[0.,1.,0.],sleeptime=None): """Fly through the current scene along the specified path. - `path`: a plex-2 or plex-3 Formex (or convertibel to such Formex) specifying the paths of camera eye and center (and upvector). - `upvector`: the direction of the vertical axis of the camera, in case of a 2-plex camera path. - `sleeptime`: a delay between subsequent images, to slow down the camera movement. This function moves the camera through the subsequent elements of the Formex. For each element the first point is used as the center of the camera and the second point as the eye (the center of the scene looked at). For a 3-plex Formex, the third point is used to define the upvector (i.e. the vertical axis of the image) of the camera. For a 2-plex Formex, the upvector is constant as specified in the arguments. """ try: if not isinstance(path,Formex): path = path.toFormex() if not path.nplex() in (2,3): raise ValueError except: raise ValueError,"The camera path should be (convertible to) a plex-2 or plex-3 Formex!" nplex = path.nplex() if sleeptime is None: sleeptime = pf.cfg['draw/flywait'] saved = delay(sleeptime) saved1 = pf.GUI.actions['Continue'].isEnabled() pf.GUI.actions['Continue'].setEnabled(True) for elem in path: center,eye = elem[:2] if nplex == 3: upv = elem[2] - center else: upv = upvector #pf.debug("Eye: %s; Center: %s; Upvector: %s" % (eye,center,upv)) pf.canvas.camera.lookAt(eye,center,upv) wait() pf.canvas.display() pf.canvas.update() image.saveNext() delay(saved) pf.GUI.actions['Continue'].setEnabled(saved1) pf.canvas.camera.setCenter(*center) pf.canvas.camera.setDist(length(center-eye)) pf.canvas.update() #################### viewports ################################## ### BEWARE FOR EFFECTS OF SPLITTING pf.canvas and pf.canvas if these ### are called from interactive functions! def viewport(n=None): """Select the current viewport. n is an integer number in the range of the number of viewports, or is one of the viewport objects in pyformex.GUI.viewports if n is None, selects the current GUI viewport for drawing """ if n is not None: pf.canvas.update() pf.GUI.viewports.setCurrent(n) pf.canvas = pf.GUI.viewports.current def layout(nvps=None,ncols=None,nrows=None,pos=None,rstretch=None,cstretch=None): """Set the viewports layout.""" pf.GUI.viewports.changeLayout(nvps,ncols,nrows,pos,rstretch,cstretch) viewport() def addViewport(): """Add a new viewport.""" pf.GUI.viewports.addView() viewport() def removeViewport(): """Remove the last viewport.""" n = len(pf.GUI.viewports.all) if n > 1: pf.GUI.viewports.removeView() viewport() def linkViewport(vp,tovp): """Link viewport vp to viewport tovp. Both vp and tovp should be numbers of viewports. """ pf.GUI.viewports.link(vp,tovp) viewport() #################### def updateGUI(): """Update the GUI.""" pf.GUI.update() pf.canvas.update() pf.app.processEvents() ######### Highlighting ############### def highlightActors(K): """Highlight a selection of actors on the canvas. K is Collection of actors as returned by the pick() method. colormap is a list of two colors, for the actors not in, resp. in the Collection K. """ pf.canvas.removeHighlights() for i in K.get(-1,[]): pf.message("%s/%s" % (i,len(pf.canvas.actors))) actor = pf.canvas.actors[i] FA = actors.GeomActor(actor,color=pf.canvas.settings.slcolor) pf.canvas.addHighlight(FA) pf.canvas.update() def highlightElements(K): """Highlight a selection of actor elements on the canvas. K is Collection of actor elements as returned by the pick() method. colormap is a list of two colors, for the elements not in, resp. in the Collection K. """ pf.canvas.removeHighlights() for i in K.keys(): pf.debug("Actor %s: Selection %s" % (i,K[i])) actor = pf.canvas.actors[i] FA = actors.GeomActor(actor.select(K[i]),color=pf.canvas.settings.slcolor,linewidth=3) pf.canvas.addHighlight(FA) pf.canvas.update() def highlightEdges(K): """Highlight a selection of actor edges on the canvas. K is Collection of TriSurface actor edges as returned by the pick() method. colormap is a list of two colors, for the edges not in, resp. in the Collection K. """ pf.canvas.removeHighlights() for i in K.keys(): pf.debug("Actor %s: Selection %s" % (i,K[i])) actor = pf.canvas.actors[i] FA = actors.GeomActor(Formex(actor.coords[actor.object.getEdges()[K[i]]]),color=pf.canvas.settings.slcolor,linewidth=3) pf.canvas.addHighlight(FA) pf.canvas.update() def highlightPoints(K): """Highlight a selection of actor elements on the canvas. K is Collection of actor elements as returned by the pick() method. """ pf.canvas.removeHighlights() for i in K.keys(): pf.debug("Actor %s: Selection %s" % (i,K[i])) actor = pf.canvas.actors[i] FA = actors.GeomActor(Formex(actor.vertices()[K[i]]),color=pf.canvas.settings.slcolor,marksize=10) pf.canvas.addHighlight(FA) pf.canvas.update() def highlightPartitions(K): """Highlight a selection of partitions on the canvas. K is a Collection of actor elements, where each actor element is connected to a collection of property numbers, as returned by the partitionCollection() method. """ pf.canvas.removeHighlights() for i in K.keys(): pf.debug("Actor %s: Partitions %s" % (i,K[i][0])) actor = pf.canvas.actors[i] for j in K[i][0].keys(): FA = actors.GeomActor(actor.select(K[i][0][j]),color=j*numpy.ones(len(K[i][0][j]),dtype=int)) pf.canvas.addHighlight(FA) pf.canvas.update() highlight_funcs = { 'actor': highlightActors, 'element': highlightElements, 'point': highlightPoints, 'edge': highlightEdges, } def removeHighlights(): """Remove the highlights from the current viewport""" pf.canvas.removeHighlights() pf.canvas.update() def pick(mode='actor',filter=None,oneshot=False,func=None): """Enter interactive picking mode and return selection. See viewport.py for more details. This function differs in that it provides default highlighting during the picking operation, a button to stop the selection operation Parameters: - `mode`: one of the pick modes - `filter`: one of the `selection_filters`. The default picking filter activated on entering the pick mode. All available filters are presented in a combobox. """ def _set_selection_filter(s): """Set the selection filter mode This function is used to change the selection filter from the selection InputCombo widget. s is one of the strings in selection_filters. """ s = str(s) if pf.canvas.selection_mode is not None and s in pf.canvas.selection_filters: pf.canvas.start_selection(None,s) if pf.canvas.selection_mode is not None: warning("You need to finish the previous picking operation first!") return if mode not in pf.canvas.getPickModes(): warning("Invalid picking mode: %s. Expected one of %s." % (mode,pf.canvas.getPickModes())) return pick_buttons = widgets.ButtonBox('Selection:',[('Cancel',pf.canvas.cancel_selection),('OK',pf.canvas.accept_selection)]) if mode == 'element': filters = pf.canvas.selection_filters else: filters = pf.canvas.selection_filters[:3] filter_combo = widgets.InputCombo('Filter:',None,choices=filters,onselect=_set_selection_filter) if filter is not None and filter in pf.canvas.selection_filters: filter_combo.setValue(filter) if func is None: func = highlight_funcs.get(mode,None) pf.message("Select %s %s" % (filter,mode)) pf.GUI.statusbar.addWidget(pick_buttons) pf.GUI.statusbar.addWidget(filter_combo) try: sel = pf.canvas.pick(mode,oneshot,func,filter) finally: # cleanup if pf.canvas.selection_mode is not None: pf.canvas.finish_selection() pf.GUI.statusbar.removeWidget(pick_buttons) pf.GUI.statusbar.removeWidget(filter_combo) return sel def pickActors(filter=None,oneshot=False,func=None): return pick('actor',filter,oneshot,func) def pickElements(filter=None,oneshot=False,func=None): return pick('element',filter,oneshot,func) def pickPoints(filter=None,oneshot=False,func=None): return pick('point',filter,oneshot,func) def pickEdges(filter=None,oneshot=False,func=None): return pick('edge',filter,oneshot,func) def pickNumbers(marks=None): if marks: pf.canvas.numbers = marks return pf.canvas.pickNumbers() def highlight(K,mode): """Highlight a Collection of actor/elements. K is usually the return value of a pick operation, but might also be set by the user. mode is one of the pick modes. """ func = highlight_funcs.get(mode,None) if func: func(K) LineDrawing = None edit_modes = ['undo', 'clear','close'] def set_edit_mode(s): """Set the drawing edit mode.""" s = str(s) if s in edit_modes: pf.canvas.edit_drawing(s) def drawLinesInter(mode ='line',single=False,func=None): """Enter interactive drawing mode and return the line drawing. See viewport.py for more details. This function differs in that it provides default displaying during the drawing operation and a button to stop the drawing operation. The drawing can be edited using the methods 'undo', 'clear' and 'close', which are presented in a combobox. """ if pf.canvas.drawing_mode is not None: warning("You need to finish the previous drawing operation first!") return if func == None: func = showLineDrawing drawing_buttons = widgets.ButtonBox('Drawing:',[('Cancel',pf.canvas.cancel_drawing),('OK',pf.canvas.accept_drawing)]) pf.GUI.statusbar.addWidget(drawing_buttons) edit_combo = widgets.InputCombo('Edit:',None,choices=edit_modes,onselect=set_edit_mode) pf.GUI.statusbar.addWidget(edit_combo) lines = pf.canvas.drawLinesInter(mode,single,func) pf.GUI.statusbar.removeWidget(drawing_buttons) pf.GUI.statusbar.removeWidget(edit_combo) return lines def showLineDrawing(L): """Show a line drawing. L is usually the return value of an interactive draw operation, but might also be set by the user. """ global LineDrawing if LineDrawing: undecorate(LineDrawing) LineDrawing = None if L.size != 0: LineDrawing = decors.LineDrawing(L,color='yellow',linewidth=3) decorate(LineDrawing) ################################ def setLocalAxes(mode=True): pf.cfg['draw/localaxes'] = mode def setGlobalAxes(mode=True): setLocalAxes(not mode) # deprecated alternative spellings from utils import deprecated,deprecation @deprecated(zoomAll) def zoomall(*args,**kargs): pass @deprecated(drawText) def drawtext(*args,**kargs): pass @deprecation("`drawNormals` is deprecated: use `drawVectors` instead.\nNotice that the argument order is different!") def drawNormals(v,P,size=None,**drawOptions): drawVectors(P,v,size=size,**drawOptions) ########################################################################### # Make _I, _G and _T be included when doing 'from gui.draw import *' # __all__ = [ n for n in globals().keys() if not n.startswith('_')] + ['_I','_G','_T'] #### End pyformex-0.8.6/pyformex/gui/viewport.py0000644000211500021150000016645511705104656020146 0ustar benebene00000000000000## $Id: viewport.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Interactive OpenGL Canvas embedded in a Qt4 widget. This module implements user interaction with the OpenGL canvas defined in module :mod:`canvas`. `QtCanvas` is a single interactive OpenGL canvas, while `MultiCanvas` implements a dynamic array of multiple canvases. """ import pyformex as pf from PyQt4 import QtCore, QtGui, QtOpenGL from OpenGL import GL from collection import Collection import canvas import decors import image import utils import toolbar # BV: UGLY! WE SHOULD GET RID OF THIS if pf.X11: from ctypes import cdll libGL = cdll.LoadLibrary("libGL.so.1") import math from coords import Coords from numpy import * #asarray,where,unique,intersect1d,setdiff1d,empty,append,delete # Some 2D vector operations # We could do this with the general functions of coords.py, # but that would be overkill for this simple 2D vectors def dotpr (v,w): """Return the dot product of vectors v and w""" return v[0]*w[0] + v[1]*w[1] def length (v): """Return the length of the vector v""" return math.sqrt(dotpr(v,v)) def projection(v,w): """Return the (signed) length of the projection of vector v on vector w.""" return dotpr(v,w)/length(w) # signals from signals import * # keys ESC = QtCore.Qt.Key_Escape RETURN = QtCore.Qt.Key_Return # Normal Enter ENTER = QtCore.Qt.Key_Enter # Num Keypad Enter # mouse actions PRESS = 0 MOVE = 1 RELEASE = 2 # mouse buttons LEFT = QtCore.Qt.LeftButton MIDDLE = QtCore.Qt.MidButton RIGHT = QtCore.Qt.RightButton _buttons = [LEFT,MIDDLE,RIGHT] # modifiers NONE = QtCore.Qt.NoModifier SHIFT = QtCore.Qt.ShiftModifier CTRL = QtCore.Qt.ControlModifier ALT = QtCore.Qt.AltModifier ALLMODS = SHIFT | CTRL | ALT _modifiers = [NONE,SHIFT,CTRL,ALT] _modifiername = ['NONE','SHIFT','CTRL','ALT'] def modifierName(mod): try: return _modifiername[_modifiers.index(mod)] except: return 'UNKNOWN' ############### OpenGL Format ################################# opengl_format = None def setOpenGLFormat(): """Set the correct OpenGL format. On a correctly installed system, the default should do well. The default OpenGL format can be changed by command line options:: --dri : use the Direct Rendering Infrastructure --nodri : do not use the DRI --alpha : enable the alpha buffer """ global opengl_format pf.debug("Get OpenGL Format") fmt = QtOpenGL.QGLFormat.defaultFormat() pf.debug("Got OpenGL Format") ## print fmt ## print "hallo" ## print "OpenGL: %s" % fmt.hasOpenGL(), ## print "OpenGL Version: %s" % int(fmt.openGLVersionFlags()), ## print "OpenGLOverlays: %s" % fmt.hasOpenGLOverlays(), ## print "Double Buffer: %s" % fmt.doubleBuffer(), ## print "Depth Buffer: %s" % fmt.depth(), ## print "RGBA: %s" % fmt.rgba(), ## print "Alpha Channel: %s" % fmt.alpha(), ## print "Accumulation Buffer: %s" % fmt.accum(), ## print "Stencil Buffer: %s" % fmt.stencil(), ## print "Stereo: %s" % fmt.stereo(), ## print "Direct Rendering: %s" % fmt.directRendering(), ## print "Overlay: %s" % fmt.hasOverlay(), ## print "Plane: %s" % fmt.plane(), ## print "Multisample Buffers: %s" % fmt.sampleBuffers(), pf.debug(OpenGLFormat(fmt)) if pf.options.dri is not None: fmt.setDirectRendering(pf.options.dri) ## if pf.options.alpha: ## fmt.setAlpha(True) pf.debug("Set OpenGL Format") pf.debug(OpenGLFormat(fmt)) QtOpenGL.QGLFormat.setDefaultFormat(fmt) #QtOpenGL.QGLFormat.setOverlayFormat(fmt) #fmt.setDirectRendering(False) opengl_format = fmt pf.debug(OpenGLFormat(fmt)) return fmt def getOpenGLContext(): ctxt = QtOpenGL.QGLContext.currentContext() if ctxt is not None: printOpenGLContext(ctxt) return ctxt def OpenGLFormat(fmt=None): """Some information about the OpenGL format.""" if fmt is None: fmt = opengl_format s = '\n'.join([ "OpenGL: %s" % fmt.hasOpenGL(), "OpenGL Version: %s" % int(fmt.openGLVersionFlags()), "OpenGLOverlays: %s" % fmt.hasOpenGLOverlays(), "Double Buffer: %s" % fmt.doubleBuffer(), "Depth Buffer: %s" % fmt.depth(), "RGBA: %s" % fmt.rgba(), "Alpha Channel: %s" % fmt.alpha(), "Accumulation Buffer: %s" % fmt.accum(), "Stencil Buffer: %s" % fmt.stencil(), "Stereo: %s" % fmt.stereo(), "Direct Rendering: %s" % fmt.directRendering(), "Overlay: %s" % fmt.hasOverlay(), "Plane: %s" % fmt.plane(), "Multisample Buffers: %s" % fmt.sampleBuffers(), '' ]) return s def printOpenGLContext(ctxt): if ctxt: print("context is valid: %d" % ctxt.isValid()) print("context is sharing: %d" % ctxt.isSharing()) else: print("No OpenGL context yet!") ################# Canvas Mouse Event Handler ######################### class CursorShapeHandler(object): """A class for handling the mouse cursor shape on the Canvas. """ cursor_shape = { 'default': QtCore.Qt.ArrowCursor, 'pick' : QtCore.Qt.CrossCursor, 'busy' : QtCore.Qt.BusyCursor, } def __init__(self,widget): """Create a CursorHandler for the specified widget.""" self.widget = widget def setCursorShape(self,shape): """Set the cursor shape to shape""" if shape not in QtCanvas.cursor_shape.keys(): shape = 'default' self.setCursor(QtGui.QCursor(QtCanvas.cursor_shape[shape])) def setCursorShapeFromFunc(self,func): """Set the cursor shape to shape""" if func in [ self.mouse_rectangle_zoom,self.mouse_pick ]: shape = 'pick' else: shape = 'default' self.setCursorShape(shape) class CanvasMouseHandler(object): """A class for handling the mouse events on the Canvas. """ def setMouse(self,button,func,mod=NONE): pf.debug(button,mod) self.mousefncsaved[mod][button].append(self.mousefnc[mod][button]) self.mousefnc[mod][button] = func self.setCursorShapeFromFunc(func) pf.debug("MOUSE %s" % func) pf.debug("MOUSE SAVED %s" % self.mousefncsaved[mod][button]) def resetMouse(self,button,mod=NONE): pf.debug("MOUSE SAVED %s" % self.mousefncsaved[mod][button]) try: func = self.mousefncsaved[mod][button].pop() except: pf.debug("AAAAAHHH, COULD NOT POP") func = None self.mousefnc[mod][button] = func self.setCursorShapeFromFunc(func) pf.debug("RESETMOUSE %s" % func) pf.debug("MOUSE SAVED %s" % self.mousefncsaved[mod][button]) def getMouseFunc(self): """Return the mouse function bound to self.button and self.mod""" return self.mousefnc.get(int(self.mod),{}).get(self.button,None) ################# Single Interactive OpenGL Canvas ############### class QtCanvas(QtOpenGL.QGLWidget,canvas.Canvas): """A canvas for OpenGL rendering. This class provides interactive functionality for the OpenGL canvas provided by the canvas.Canvas class. Interactivity is highly dependent on Qt4. Putting the interactive functions in a separate class makes it esier to use the Canvas class in non-interactive situations or combining it with other GUI toolsets. """ cursor_shape = { 'default': QtCore.Qt.ArrowCursor, 'pick' : QtCore.Qt.CrossCursor, 'draw' : QtCore.Qt.CrossCursor, 'busy' : QtCore.Qt.BusyCursor, } selection_filters = [ 'none', 'single', 'closest', 'connected', 'closest-connected' ] def __init__(self,*args): """Initialize an empty canvas with default settings.""" QtOpenGL.QGLWidget.__init__(self,*args) self.setMinimumSize(32,32) self.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding,QtGui.QSizePolicy.MinimumExpanding) self.setFocusPolicy(QtCore.Qt.StrongFocus) canvas.Canvas.__init__(self) self.setCursorShape('default') self.button = None self.mod = NONE self.dynamouse = True # dynamic mouse action works on mouse move self.dynamic = None # what action on mouse move self.mousefnc = {} self.mousefncsaved = {} for mod in _modifiers: self.mousefnc[mod] = {} self.mousefncsaved[mod] = {} for button in _buttons: self.mousefnc[mod][button] = None self.mousefncsaved[mod][button] = [] # Initial mouse funcs are dynamic handling # ALT is initially same as NONE and should never be changed for mod in (NONE,ALT): self.setMouse(LEFT,self.dynarot,mod) self.setMouse(MIDDLE,self.dynapan,mod) self.setMouse(RIGHT,self.dynazoom,mod) self.selection_mode = None self.selection = Collection() self.trackfunc = None self.pick_func = { 'actor' : self.pick_actors, 'element': self.pick_elements, 'point' : self.pick_points, 'edge' : self.pick_edges, 'number' : self.pick_numbers, } self.pickable = None self.drawmode = None self.drawing_mode = None self.drawing = None # Drawing options self.resetOptions() def getPickModes(self): return self.pick_func.keys() def resetOptions(self): """Reset the Drawing options to some defaults""" self.options = dict( view = None, # Keep the current camera angles bbox = 'auto', # Automatically zoom on the drawed object clear = False, # Clear on each drawing action shrink = None, ) def setOptions(self,d): """Set the Drawing options to some values""" self.options.update(d) def setCursorShape(self,shape): """Set the cursor shape to shape""" if shape not in QtCanvas.cursor_shape.keys(): shape = 'default' self.setCursor(QtGui.QCursor(QtCanvas.cursor_shape[shape])) def setCursorShapeFromFunc(self,func): """Set the cursor shape to shape""" if func in [ self.mouse_rectangle_zoom,self.mouse_pick ]: shape = 'pick' elif func == self.mouse_draw: shape = 'draw' else: shape = 'default' self.setCursorShape(shape) def setMouse(self,button,func,mod=NONE): pf.debug("setMouse %s %s %s" % (button,mod,func)) self.mousefncsaved[mod][button].append(self.mousefnc[mod][button]) self.mousefnc[mod][button] = func if button == LEFT and mod == NONE: self.setCursorShapeFromFunc(func) #print self.mousefnc def resetMouse(self,button,mod=NONE): pf.debug("resetMouse %s %s" % (button,mod)) try: func = self.mousefncsaved[mod][button].pop() except: func = None self.mousefnc[mod][button] = func if button == LEFT and mod == NONE: self.setCursorShapeFromFunc(func) #print self.mousefnc def getMouseFunc(self): """Return the mouse function bound to self.button and self.mod""" return self.mousefnc.get(int(self.mod),{}).get(self.button,None) def start_rectangle_zoom(self): self.setMouse(LEFT,self.mouse_rectangle_zoom) def finish_rectangle_zoom(self): self.update() self.resetMouse(LEFT) def mouse_rectangle_zoom(self,x,y,action): """Process mouse events during interactive rectangle zooming. On PRESS, record the mouse position. On MOVE, create a rectangular zoom window. On RELEASE, zoom to the picked rectangle. """ if action == PRESS: self.makeCurrent() self.update() if self.trackfunc: print "PRESS",self.trackfunc,pf.canvas.camera.ctr pf.canvas.camera.setTracking(True) x,y,z = pf.canvas.camera.ctr self.zplane = pf.canvas.project(x,y,z,True)[2] print 'ZPLANE',self.zplane self.trackfunc(x,y,self.zplane) self.begin_2D_drawing() GL.glEnable(GL.GL_COLOR_LOGIC_OP) # An alternative is GL_XOR # GL.glLogicOp(GL.GL_INVERT) # Draw rectangle self.draw_state_rect(x,y) self.swapBuffers() elif action == MOVE: if self.trackfunc: print "MOVE",self.trackfunc print 'ZPLANE',self.zplane self.trackfunc(x,y,self.zplane) # Remove old rectangle self.swapBuffers() self.draw_state_rect(*self.state) # Draw new rectangle self.draw_state_rect(x,y) self.swapBuffers() elif action == RELEASE: GL.glDisable(GL.GL_COLOR_LOGIC_OP) self.end_2D_drawing() x0 = min(self.statex,x) y0 = min(self.statey,y) x1 = max(self.statex,x) y1 = max(self.statey,y) self.zoomRectangle(x0,y0,x1,y1) self.finish_rectangle_zoom() ####################### INTERACTIVE PICKING ############################ def setPickable(self,nrs=None): """Set the list of pickable actors""" if nrs is None: self.pickable = None else: self.pickable = [ self.actors[i] for i in nrs if i in range(len(self.actors))] def start_selection(self,mode,filter): """Start an interactive picking mode. If selection mode was already started, mode is disregarded and this can be used to change the filter method. """ #pf.debug("START SELECTION") #pf.debug("Mode is %s" % self.selection_mode) if self.selection_mode is None: self.setMouse(LEFT,self.mouse_pick) self.setMouse(LEFT,self.mouse_pick,SHIFT) self.setMouse(LEFT,self.mouse_pick,CTRL) self.setMouse(RIGHT,self.emit_done) self.setMouse(RIGHT,self.emit_cancel,SHIFT) self.connect(self,DONE,self.accept_selection) self.connect(self,CANCEL,self.cancel_selection) self.selection_mode = mode self.selection_front = None if filter == 'none': filter = None self.selection_filter = filter if filter is None: self.selection_front = None self.selection.clear() self.selection.setType(self.selection_mode) #pf.debug("START SELECTION DONE") def wait_selection(self): """Wait for the user to interactively make a selection.""" #pf.debug("WAIT SELECTION") self.selection_timer = QtCore.QThread self.selection_busy = True while self.selection_busy: self.selection_timer.msleep(20) pf.app.processEvents() #pf.debug("WAIT SELECTION DONE") def finish_selection(self): """End an interactive picking mode.""" #pf.debug("FINISH SELECTION") self.resetMouse(LEFT) self.resetMouse(LEFT,SHIFT) self.resetMouse(LEFT,CTRL) self.resetMouse(RIGHT) self.resetMouse(RIGHT,SHIFT) self.disconnect(self,DONE,self.accept_selection) self.disconnect(self,CANCEL,self.cancel_selection) self.selection_mode = None #pf.debug("FINISH SELECTION DONE") def accept_selection(self,clear=False): """Accept or cancel an interactive picking mode. If clear == True, the current selection is cleared. """ self.selection_accepted = True if clear: self.selection.clear() self.selection_accepted = False self.selection_canceled = True self.selection_busy = False def cancel_selection(self): """Cancel an interactive picking mode and clear the selection.""" self.accept_selection(clear=True) def pick(self,mode='actor',oneshot=False,func=None,filter=None): """Interactively pick objects from the viewport. - `mode`: defines what to pick : one of ``['actor','element','point','number','edge']`` - `oneshot`: if True, the function returns as soon as the user ends a picking operation. The default is to let the user modify his selection and only to return after an explicit cancel (ESC or right mouse button). - `func`: if specified, this function will be called after each atomic pick operation. The Collection with the currently selected objects is passed as an argument. This can e.g. be used to highlight the selected objects during picking. - `filter`: defines what elements to retain from the selection: one of ``[None,'single','closest,'connected']``. - None (default) will return the complete selection. - 'closest' will only keep the element closest to the user. - 'connected' will only keep elements connected to - the closest element (set picked) - what is already in the selection (add picked). Currently this only works when picking mode is 'element' and for Actors having a partitionByConnection method. When the picking operation is finished, the selection is returned. The return value is always a Collection object. """ self.selection_canceled = False self.start_selection(mode,filter) while not self.selection_canceled: self.wait_selection() if not self.selection_canceled: # selection by mouse_picking self.pick_func[self.selection_mode]() if len(self.picked) != 0: if self.selection_filter is None: if self.mod == NONE: self.selection.set(self.picked) elif self.mod == SHIFT: self.selection.add(self.picked) elif self.mod == CTRL: self.selection.remove(self.picked) elif self.selection_filter == 'single': if self.mod == NONE: self.selection.set([self.closest_pick[0]]) elif self.mod == SHIFT: self.selection.add([self.closest_pick[0]]) elif self.mod == CTRL: self.selection.remove([self.closest_pick[0]]) elif self.selection_filter == 'closest': if self.selection_front is None or self.mod == NONE or \ (self.mod == SHIFT and self.closest_pick[1] < self.selection_front[1]): self.selection_front = self.closest_pick self.selection.set([self.closest_pick[0]]) elif self.selection_filter == 'connected': if self.selection_front is None or self.mod == NONE or len(self.selection.keys()) == 0: self.selection_front = self.closest_pick closest_actor,closest_elem = map(int,self.selection_front[0]) elif self.mod == SHIFT: closest_elem = self.selection.get(closest_actor)[0] if self.mod == NONE: self.selection.set(self.picked) elif self.mod == SHIFT: self.selection.add(self.picked) elif self.mod == CTRL: self.selection.remove(self.picked) if self.mod == NONE or self.mod == SHIFT: conn_elems = self.actors[closest_actor].object.connectedElements(closest_elem,self.selection.get(closest_actor)) self.selection.set(conn_elems,closest_actor) if func: func(self.selection) self.update() if oneshot: self.accept_selection() if func and not self.selection_accepted: func(self.selection) self.finish_selection() return self.selection def pickNumbers(self,*args,**kargs): """Go into number picking mode and return the selection.""" return self.pick('numbers',*args,**kargs) #################### Interactive drawing #################################### def idraw(self,mode='point',npoints=-1,zplane=0.,func=None,coords=None,preview=False): """Interactively draw on the canvas. This function allows the user to interactively create points in 3D space and collects the subsequent points in a Coords object. The interpretation of these points is left to the caller. - `mode`: one of the drawing modes, specifying the kind of objects you want to draw. This is passed to the specified `func`. - `npoints`: If -1, the user can create any number of points. When >=0, the function will return when the total number of points in the collection reaches the specified value. - `zplane`: the depth of the z-plane on which the 2D drawing is done. - `func`: a function that is called after each atomic drawing operation. It is typically used to draw a preview using the current set of points. The function is passed the current Coords and the `mode` as arguments. - `coords`: an initial set of coordinates to which the newly created points should be added. If specified, `npoints` also counts these initial points. - `preview`: **Experimental** If True, the preview funcion will also be called during mouse movement with a pressed button, allowing to preview the result before a point is created. The drawing operation is finished when the number of requested points has been reached, or when the user clicks the right mouse button or hits 'ENTER'. The return value is a (n,3) shaped Coords array. """ self.draw_canceled = False self.start_draw(mode,zplane,coords) try: if preview: self.previewfunc = func else: self.previewfunc = None while not self.draw_canceled: self.wait_selection() if not self.draw_canceled: self.drawn = Coords(self.drawn).reshape(-1,3) self.drawing = Coords.concatenate([self.drawing,self.drawn]) if func: func(self.drawing,self.drawmode) if npoints > 0 and len(self.drawing) >= npoints: self.accept_draw() if func and not self.draw_accepted: func(self.drawing,self.drawmode) finally: self.finish_draw() return self.drawing def start_draw(self,mode,zplane,coords): """Start an interactive drawing mode.""" self.setMouse(LEFT,self.mouse_draw) self.setMouse(RIGHT,self.emit_done) self.setMouse(RIGHT,self.emit_cancel,SHIFT) self.connect(self,DONE,self.accept_draw) self.connect(self,CANCEL,self.cancel_draw) self.drawmode = mode self.zplane = zplane self.drawing = Coords(coords) def finish_draw(self): """End an interactive drawing mode.""" self.resetMouse(LEFT) self.resetMouse(RIGHT) self.resetMouse(RIGHT,SHIFT) self.disconnect(self,DONE,self.accept_selection) self.disconnect(self,CANCEL,self.cancel_selection) self.drawmode = None def accept_draw(self,clear=False): """Cancel an interactive drawing mode. If clear == True, the current drawing is cleared. """ self.draw_accepted = True if clear: self.drawing = Coords() self.draw_accepted = False self.draw_canceled = True self.selection_busy = False def cancel_draw(self): """Cancel an interactive drawing mode and clear the drawing.""" self.accept_draw(clear=True) def mouse_draw(self,x,y,action): """Process mouse events during interactive drawing. On PRESS, do nothing. On MOVE, do nothing. On RELEASE, add the point to the point list. """ if action == PRESS: self.makeCurrent() self.update() if self.trackfunc: print "ENABLE TRACKING" pf.canvas.camera.setTracking(True) elif action == MOVE: if pf.app.hasPendingEvents(): return if self.trackfunc: self.trackfunc(x,y,self.zplane) #pf.app.processEvents() if self.previewfunc: self.swapBuffers() self.drawn = self.unProject(x,y,self.zplane) self.drawn = Coords(self.drawn).reshape(-1,3) self.previewfunc(Coords.concatenate([self.drawing,self.drawn]),self.drawmode) self.swapBuffers() elif action == RELEASE: self.drawn = self.unProject(x,y,self.zplane) self.selection_busy = False ########################################################################## def start_drawing(self,mode): """Start an interactive line drawing mode.""" pf.debug("START DRAWING MODE") self.setMouse(LEFT,self.mouse_draw_line) self.setMouse(RIGHT,self.emit_done) self.setMouse(RIGHT,self.emit_cancel,SHIFT) self.connect(self,DONE,self.accept_drawing) self.connect(self,CANCEL,self.cancel_drawing) #self.setCursorShape('pick') self.drawing_mode = mode self.edit_mode = None self.drawing = empty((0,2,2),dtype=int) def wait_drawing(self): """Wait for the user to interactively draw a line.""" self.drawing_timer = QtCore.QThread self.drawing_busy = True while self.drawing_busy: self.drawing_timer.msleep(20) pf.app.processEvents() def finish_drawing(self): """End an interactive drawing mode.""" pf.debug("END DRAWING MODE") #self.setCursorShape('default') self.resetMouse(LEFT) self.resetMouse(RIGHT) self.resetMouse(RIGHT,SHIFT) self.disconnect(self,DONE,self.accept_selection) self.disconnect(self,CANCEL,self.cancel_selection) self.drawing_mode = None def accept_drawing(self,clear=False): """Cancel an interactive drawing mode. If clear == True, the current drawing is cleared. """ pf.debug("CANCEL DRAWING MODE") self.drawing_accepted = True if clear: self.drawing = empty((0,2,2),dtype=int) self.drawing_accepted = False self.drawing_canceled = True self.drawing_busy = False def cancel_drawing(self): """Cancel an interactive drawing mode and clear the drawing.""" self.accept_drawing(clear=True) def edit_drawing(self,mode): """Edit an interactive drawing.""" self.edit_mode = mode self.drawing_busy = False def drawLinesInter(self,mode='line',oneshot=False,func=None): """Interactively draw lines on the canvas. - oneshot: if True, the function returns as soon as the user ends a drawing operation. The default is to let the user draw multiple lines and only to return after an explicit cancel (ESC or right mouse button). - func: if specified, this function will be called after each atomic drawing operation. The current drawing is passed as an argument. This can e.g. be used to show the drawing. When the drawing operation is finished, the drawing is returned. The return value is a (n,2,2) shaped array. """ self.drawing_canceled = False self.start_drawing(mode) while not self.drawing_canceled: self.wait_drawing() if not self.drawing_canceled: if self.edit_mode: # an edit mode from the edit combo was clicked if self.edit_mode == 'undo' and self.drawing.size != 0: self.drawing = delete(self.drawing,-1,0) elif self.edit_mode == 'clear': self.drawing = empty((0,2,2),dtype=int) elif self.edit_mode == 'close' and self.drawing.size != 0: line = asarray([self.drawing[-1,-1],self.drawing[0,0]]) self.drawing = append(self.drawing,line.reshape(-1,2,2),0) self.edit_mode = None else: # a line was drawn interactively self.drawing = append(self.drawing,self.drawn.reshape(-1,2,2),0) if func: func(self.drawing) if oneshot: self.accept_drawing() if func and not self.drawing_accepted: func(self.drawing) self.finish_drawing() return self.drawing ######## QtOpenGL interface ############################## def initializeGL(self): if pf.options.debug: p = self.sizePolicy() print(p.horizontalPolicy(), p.verticalPolicy(), p.horizontalStretch(), p.verticalStretch()) self.initCamera() self.glinit() self.resizeGL(self.width(),self.height()) self.setCamera() def resizeGL(self,w,h): self.setSize(w,h) def paintGL(self): if not self.mode2D: #pf.debugt("CANVAS DISPLAY") self.display() def getSize(self): return int(self.width()),int(self.height()) ####### MOUSE EVENT HANDLERS ############################ # Mouse functions can be bound to any of the mouse buttons # LEFT, MIDDLE or RIGHT. # Each mouse function should accept three possible actions: # PRESS, MOVE, RELEASE. # On a mouse button PRESS, the mouse screen position and the pressed # button are always saved in self.statex,self.statey,self.button. # The mouse function does not need to save these and can directly use # their values. # On a mouse button RELEASE, self.button is cleared, to avoid further # move actions. # Functions that change the camera settings should call saveModelView() # when they are done. # ATTENTION! The y argument is positive upwards, as in normal OpenGL # operations! def dynarot(self,x,y,action): """Perform dynamic rotation operation. This function processes mouse button events controlling a dynamic rotation operation. The action is one of PRESS, MOVE or RELEASE. """ if action == PRESS: w,h = self.getSize() self.state = [self.statex-w/2, self.statey-h/2 ] elif action == MOVE: w,h = self.getSize() # set all three rotations from mouse movement # tangential movement sets twist, # but only if initial vector is big enough x0 = self.state # initial vector d = length(x0) if d > h/8: # pf.debug(d) x1 = [x-w/2, y-h/2] # new vector a0 = math.atan2(x0[0],x0[1]) a1 = math.atan2(x1[0],x1[1]) an = (a1-a0) / math.pi * 180 ds = utils.stuur(d,[-h/4,h/8,h/4],[-1,0,1],2) twist = - an*ds self.camera.rotate(twist,0.,0.,1.) self.state = x1 # radial movement rotates around vector in lens plane x0 = [self.statex-w/2, self.statey-h/2] # initial vector if x0 == [0.,0.]: x0 = [1.,0.] dx = [x-self.statex, y-self.statey] # movement b = projection(dx,x0) if abs(b) > 5: val = utils.stuur(b,[-2*h,0,2*h],[-180,0,+180],1) rot = [ abs(val),-dx[1],dx[0],0 ] self.camera.rotate(*rot) self.statex,self.statey = (x,y) self.update() elif action == RELEASE: self.update() self.camera.saveModelView() def dynapan(self,x,y,action): """Perform dynamic pan operation. This function processes mouse button events controlling a dynamic pan operation. The action is one of PRESS, MOVE or RELEASE. """ if action == PRESS: pass elif action == MOVE: w,h = self.getSize() dx,dy = float(self.statex-x)/w, float(self.statey-y)/h self.camera.transArea(dx,dy) self.statex,self.statey = (x,y) self.update() elif action == RELEASE: self.update() self.camera.saveModelView() def dynazoom(self,x,y,action): """Perform dynamic zoom operation. This function processes mouse button events controlling a dynamic zoom operation. The action is one of PRESS, MOVE or RELEASE. """ if action == PRESS: self.state = [self.camera.getDist(),self.camera.area.tolist(),pf.cfg['gui/dynazoom']] elif action == MOVE: w,h = self.getSize() dx,dy = float(self.statex-x)/w, float(self.statey-y)/h for method,state,value,size in zip(self.state[2],[self.statex,self.statey],[x,y],[w,h]): #pf.debug("%s %s %s %s" % (method,state,value,size)) if method == 'area': d = float(state-value)/size f = exp(4*d) self.camera.zoomArea(f,area=asarray(self.state[1]).reshape(2,2)) elif method == 'dolly': d = utils.stuur(value,[0,state,size],[5,1,0.2],1.2) #pf.debug(d) self.camera.setDist(d*self.state[0]) self.update() elif action == RELEASE: self.update() self.camera.saveModelView() def wheel_zoom(self,delta): """Zoom by rotating a wheel over an angle delta""" f = 2**(delta/120.*pf.cfg['gui/wheelzoomfactor']) if pf.cfg['gui/wheelzoom'] == 'area': self.camera.zoomArea(f) elif pf.cfg['gui/wheelzoom'] == 'lens': self.camera.zoom(f) else: self.camera.dolly(f) self.update() def emit_done(self,x,y,action): """Emit a DONE event by clicking the mouse. This is equivalent to pressing the ENTER button.""" if action == RELEASE: self.emit(DONE,()) def emit_cancel(self,x,y,action): """Emit a CANCEL event by clicking the mouse. This is equivalent to pressing the ESC button.""" if action == RELEASE: self.emit(CANCEL,()) def draw_state_rect(self,x,y): """Store the pos and draw a rectangle to it.""" self.state = x,y decors.drawRect(self.statex,self.statey,x,y) def mouse_pick(self,x,y,action): """Process mouse events during interactive picking. On PRESS, record the mouse position. On MOVE, create a rectangular picking window. On RELEASE, pick the objects inside the rectangle. """ if action == PRESS: self.makeCurrent() self.update() self.begin_2D_drawing() #self.swapBuffers() GL.glEnable(GL.GL_COLOR_LOGIC_OP) # An alternative is GL_XOR # GL.glLogicOp(GL.GL_INVERT) # Draw rectangle self.draw_state_rect(x,y) self.swapBuffers() elif action == MOVE: # Remove old rectangle self.swapBuffers() self.draw_state_rect(*self.state) # Draw new rectangle self.draw_state_rect(x,y) self.swapBuffers() elif action == RELEASE: GL.glDisable(GL.GL_COLOR_LOGIC_OP) self.swapBuffers() self.end_2D_drawing() x,y = (x+self.statex)/2., (y+self.statey)/2. w,h = abs(x-self.statex)*2., abs(y-self.statey)*2. if w <= 0 or h <= 0: w,h = pf.cfg.get('pick/size',(20,20)) vp = GL.glGetIntegerv(GL.GL_VIEWPORT) self.pick_window = (x,y,w,h,vp) self.selection_busy = False def pick_actors(self): """Set the list of actors inside the pick_window.""" self.camera.loadProjection(pick=self.pick_window) self.camera.loadModelView() stackdepth = 1 npickable = len(self.actors) selbuf = GL.glSelectBuffer(npickable*(3+stackdepth)) GL.glRenderMode(GL.GL_SELECT) GL.glInitNames() for i,a in enumerate(self.actors): GL.glPushName(i) GL.glCallList(a.list) GL.glPopName() libGL.glRenderMode(GL.GL_RENDER) # Read the selection buffer store_closest = self.selection_filter == 'single' or \ self.selection_filter == 'closest' self.picked = [] if selbuf[0] > 0: buf = asarray(selbuf).reshape(-1,3+selbuf[0]) buf = buf[buf[:,0] > 0] self.picked = buf[:,3] if store_closest: w = buf[:,1].argmin() self.closest_pick = (self.picked[w], buf[w,1]) def pick_parts(self,obj_type,max_objects,store_closest=False): """Set the list of actor parts inside the pick_window. obj_type can be 'element', 'edge' or 'point' 'edge' is only available for mesh type geometry max_objects specifies the maximum number of objects The picked object numbers are stored in self.picked. If store_closest==True, the closest picked object is stored in as a tuple ( [actor,object] ,distance) in self.picked_closest A list of actors from which can be picked may be given. If so, the resulting keys are indices in this list. By default, the full actor list is used. """ self.picked = [] pf.debug('PICK_PARTS %s %s %s' % (obj_type,max_objects,store_closest)) if max_objects <= 0: pf.message("No such objects to be picked!") return self.camera.loadProjection(pick=self.pick_window) self.camera.loadModelView() stackdepth = 2 selbuf = GL.glSelectBuffer(max_objects*(3+stackdepth)) GL.glRenderMode(GL.GL_SELECT) GL.glInitNames() if self.pickable is None: pickable = self.actors else: pickable = self.pickable for i,a in enumerate(pickable): GL.glPushName(i) a.pickGL(obj_type) # this will push the number of the part GL.glPopName() self.picked = [] libGL.glRenderMode(GL.GL_RENDER) if selbuf[0] > 0: buf = asarray(selbuf).reshape(-1,3+selbuf[0]) buf = buf[buf[:,0] > 0] self.picked = buf[:,3:] #pf.debug("PICKBUFFER: %s" % self.picked) if store_closest and len(buf) > 0: w = buf[:,1].argmin() self.closest_pick = (self.picked[w], buf[w,1]) def pick_elements(self): """Set the list of actor elements inside the pick_window.""" npickable = 0 for a in self.actors: npickable += a.nelems() self.pick_parts('element',npickable,store_closest=\ self.selection_filter == 'single' or\ self.selection_filter == 'closest' or\ self.selection_filter == 'connected' ) def pick_points(self): """Set the list of actor points inside the pick_window.""" npickable = 0 for a in self.actors: pf.debug("ADDING %s pickable points"%a.npoints()) npickable += a.npoints() self.pick_parts('point',npickable,store_closest=\ self.selection_filter == 'single' or\ self.selection_filter == 'closest', ) def pick_edges(self): """Set the list of actor edges inside the pick_window.""" npickable = 0 for a in self.actors: if hasattr(a,'nedges'): npickable += a.nedges() self.pick_parts('edge',npickable,store_closest=\ self.selection_filter == 'single' or\ self.selection_filter == 'closest', ) def pick_numbers(self): """Return the numbers inside the pick_window.""" self.camera.loadProjection(pick=self.pick_window) self.camera.loadModelView() self.picked = [0,1,2,3] if self.numbers: self.picked = self.numbers.drawpick() def draw_state_line(self,x,y): """Store the pos and draw a line to it.""" self.state = x,y decors.drawLine(self.statex,self.statey,x,y) def mouse_draw_line(self,x,y,action): """Process mouse events during interactive drawing. On PRESS, record the mouse position. On MOVE, draw a line. On RELEASE, add the line to the drawing. """ if action == PRESS: self.makeCurrent() self.update() self.begin_2D_drawing() self.swapBuffers() GL.glEnable(GL.GL_COLOR_LOGIC_OP) # An alternative is GL_XOR # GL.glLogicOp(GL.GL_INVERT) # Draw rectangle if self.drawing.size != 0: self.statex,self.statey = self.drawing[-1,-1] self.draw_state_line(x,y) self.swapBuffers() elif action == MOVE: # Remove old rectangle self.swapBuffers() self.draw_state_line(*self.state) # Draw new rectangle self.draw_state_line(x,y) self.swapBuffers() elif action == RELEASE: GL.glDisable(GL.GL_COLOR_LOGIC_OP) #self.swapBuffers() self.end_2D_drawing() self.drawn = asarray([[self.statex,self.statey],[x,y]]) self.drawing_busy = False @classmethod def has_modifier(clas,e,mod): return ( e.modifiers() & mod ) == mod def mousePressEvent(self,e): """Process a mouse press event.""" pf.GUI.viewports.setCurrent(self) # on PRESS, always remember mouse position and button self.statex,self.statey = e.x(), self.height()-e.y() self.button = e.button() self.mod = e.modifiers() & ALLMODS #pf.debug("PRESS BUTTON %s WITH MODIFIER %s" % (self.button,self.mod)) func = self.getMouseFunc() if func: func(self.statex,self.statey,PRESS) e.accept() def mouseMoveEvent(self,e): """Process a mouse move event.""" # the MOVE event does not identify a button, use the saved one func = self.getMouseFunc() if func: func(e.x(),self.height()-e.y(),MOVE) e.accept() def mouseReleaseEvent(self,e): """Process a mouse release event.""" func = self.getMouseFunc() self.button = None # clear the stored button if func: func(e.x(),self.height()-e.y(),RELEASE) e.accept() def wheelEvent(self,e): """Process a wheel event.""" func = self.wheel_zoom if func: func(e.delta()) e.accept() # Any keypress with focus in the canvas generates a 'wakeup' signal. # This is used to break out of a wait status. # Events not handled here could also be handled by the toplevel # event handler. def keyPressEvent (self,e): self.emit(WAKEUP,()) if e.key() == ESC: self.emit(CANCEL,()) e.accept() elif e.key() == ENTER or e.key() == RETURN: self.emit(DONE,()) e.accept() else: e.ignore() ################# Multiple Viewports ############### class NewMultiCanvas(QtGui.QGridLayout): """An OpenGL canvas with multiple viewports and QT interaction. The MultiCanvas implements a central QT widget containing one or more QtCanvas widgets. """ def __init__(self,parent=None): """Initialize the multicanvas.""" QtGui.QGridLayout.__init__(self) self.all = [] self.current = None self.rowwise = True self.parent = parent def changeLayout(self,nvps=None,ncols=None,nrows=None,pos=None,rstretch=None,cstretch=None): """Change the lay-out of the viewports on the OpenGL widget. nvps: number of viewports ncols: number of columns nrows: number of rows pos: list holding the position and span of each viewport [[row,col,rowspan,colspan],...] rstretch: list holding the stretch factor for each row cstretch: list holding the stretch factor for each column (rows/columns with a higher stretch factor take more of the available space) Each of this parameters is optional. If pos is given, it specifies all viewports and nvps, nrows and ncols are disregarded. Else: If nvps is given, it specifies the number of viewports in the layout. Else, nvps will be set to the current number of viewports. If ncols is an int, viewports are laid out rowwise over ncols columns and nrows is ignored. If ncols is None and nrows is an int, viewports are laid out columnwise over nrows rows. If nvps is not equal to the current number of viewports, viewports will be added or removed to match the requested number. By default they are laid out rowwise over two columns. """ if pos is None: # get the new layout definition if nvps is None: nvps = len(self.all) if ncols is None: if nrows is None: ncols = self.ncols() if type(ncols) == int: pos = [ divmod(i,ncols) for i in range(nvps) ] elif type(nrows) == int: pos = [ divmod(i,nrows)[::-1] for i in range(nvps) ] else: return else: nvps = len(pos) print nvps,pos while len(self.all) < nvps: # create new viewports view = self.createView() self.all.append(view) while len(self.all) > nvps: # remove viewports self.removeView() # remove all views from the canvas for w in self.all: self.removeWidget(w) # w.hide() # create the new layout for view,args in zip(self.all,pos): self.addView(view,*args) self.setCurrent(self.all[-1]) def createView(self,shared=None): """Create a new viewport If another QtCanvas instance is passed, both will share the same display lists and textures. """ if shared is not None: pf.debug("SHARING display lists WITH %s" % shared) view = QtCanvas(self.parent,shared) if len(self.all) > 0: # copy default settings from previous view.resetDefaults(self.all[-1].settings) return(view) def addView(self,view,row,col,rowspan=1,colspan=1): """Add a new viewport and make it visible""" self.addWidget(view,row,col,rowspan,colspan) view.raise_() view.initializeGL() # Initialize OpenGL context and camera def removeView(self,view=None): """Remove a view from the canvas If view is None, the last one is removed. You can not remove a view when there is only one left. """ if len(self.all) > 1: if view is None: view = self.all.pop() else: i = self.all.find(view) if i < 0: return view = self.all.pop(i) if self.current == view: self.setCurrent(self.all[i-1]) self.removeWidget(view) view.close() def setCurrent(self,view): """Make the specified viewport the current one. view can be either a viewport or viewport number. """ if type(view) == int and view in range(len(self.all)): view = self.all[view] if view == self.current: return # already current if view in self.all: if self.current: self.current.focus = False self.current.updateGL() self.current = view self.current.focus = True self.current.updateGL() toolbar.updateTransparencyButton() toolbar.updatePerspectiveButton() toolbar.updateLightButton() def currentView(self): return self.all.index(self.current) def nrows(self): return self.rowCount() def ncols(self): return self.columnCount() def setStretch(self,rowstretch,colstretch): """Set the row and column stretch factors. rowstretch and colstretch are lists of stretch factors to be applied on the subsequent rows/columns. If the lists are shorter than the number of rows/columns, the """ if rowstretch: for i in range(min(len(rowstretch),self.nrows())): self.setRowStretch(i,rowstretch[i]) if colstretch: for i in range(min(len(colstretch),self.ncols())): self.setColumnStretch(i,colstretch[i]) def updateAll(self): pf.debug("UPDATING ALL VIEWPORTS") for v in self.all: v.update() pf.GUI.processEvents() def printSettings(self): for i,v in enumerate(self.all): pf.message(""" ## VIEWPORTS ## Viewport %s; Current:%s; Settings: %s """ % (i, v == self.current, v.settings)) def link(self,vp,to): """Link viewport vp to to""" print "LINK %s to %s" % (vp,to) print "LINKING CURRENTLY DISABLED" return nvps = len(self.all) if vp in range(nvps) and to in range(nvps) and vp != to: to = self.all[to] oldvp = self.all[vp] import warnings warnings.warn('warn_viewport_linking') newvp = self.newView(to) self.all[vp] = newvp self.removeWidget(oldvp) oldvp.close() self.showWidget(newvp) vp = newvp vp.actors = to.actors vp.bbox = to.bbox vp.show() vp.setCamera() vp.redrawAll() #vp.updateGL() pf.GUI.processEvents() class FramedGridLayout(QtGui.QGridLayout): """A QtGui.QGridLayout where each added widget is framed.""" def __init__(self,parent=None): """Initialize the multicanvas.""" QtGui.QGridLayout.__init__(self) # self.frames = [] def addWidget(*args): # f = QtGui.QFrame(w) # self.frames.append(f) QtGui.QGridLayout.addWidget(*args) def removeWidget(self,w): QtGui.QGridLayout.removeWidget(self,w) class MultiCanvas(FramedGridLayout): """An OpenGL canvas with multiple viewports and QT interaction. The MultiCanvas implements a central QT widget containing one or more QtCanvas widgets. """ def __init__(self,parent=None): """Initialize the multicanvas.""" FramedGridLayout.__init__(self) self.all = [] self.current = None self.ncols = 2 self.rowwise = True self.pos = None self.rstretch = None self.cstretch = None self.parent = parent def newView(self,shared=None): """Create a new viewport If another QtCanvas instance is passed, both will share the same display lists and textures. """ if shared is not None: pf.debug("SHARING display lists WITH %s" % shared) canv = QtCanvas(self.parent,shared) return(canv) def addView(self): """Add a new viewport to the widget""" canv = self.newView() if len(self.all) > 0: # copy default settings from previous canv.resetDefaults(self.all[-1].settings) self.all.append(canv) self.showWidget(canv) canv.initializeGL() # Initialize OpenGL context and camera self.setCurrent(canv) def setCurrent(self,canv): """Make the specified viewport the current one. canv can be either a viewport or viewport number. """ if type(canv) == int and canv in range(len(self.all)): canv = self.all[canv] if canv == self.current: return # already current if canv in self.all: if self.current: self.current.focus = False self.current.updateGL() self.current = canv self.current.focus = True self.current.updateGL() toolbar.updateTransparencyButton() toolbar.updatePerspectiveButton() toolbar.updateLightButton() def currentView(self): return self.all.index(self.current) def showWidget(self,w): """Show the view w.""" ind = self.all.index(w) if self.pos is None: row,col = divmod(ind,self.ncols) if not self.rowwise: row,col = col,row rspan,cspan = 1,1 elif ind < len(self.pos): row,col,rspan,cspan = self.pos[ind] else: return self.addWidget(w,row,col,rspan,cspan) w.raise_() # set the stretch factors if self.rstretch is not None: for i in range(row,row+rspan): if i >= len(self.rstretch): self.rstretch.append(1) self.setRowStretch(i,self.rstretch[i]) if self.cstretch is not None: for i in range(col,col+cspan): if i >= len(self.cstretch): self.cstretch.append(1) self.setColumnStretch(i,self.cstretch[i]) def removeView(self): if len(self.all) > 1: w = self.all.pop() if self.pos is not None: self.pos = self.pos[:-1] if self.current == w: self.setCurrent(self.all[-1]) self.removeWidget(w) w.close() # set the stretch factors pos = [self.getItemPosition(self.indexOf(w)) for w in self.all] if self.rstretch is not None: row = max([p[0]+p[2] for p in pos]) for i in range(row,len(self.rstretch)): self.setRowStretch(i,0) self.rstretch = self.rstretch[:row] if self.cstretch is not None: col = max([p[1]+p[3] for p in pos]) for i in range(col,len(self.cstretch)): self.setColumnStretch(i,0) self.cstretch = self.cstretch[:col] ## def setCamera(self,bbox,view): ## self.current.setCamera(bbox,view) def updateAll(self): pf.debug("UPDATING ALL VIEWPORTS") for v in self.all: v.update() pf.GUI.processEvents() def printSettings(self): for i,v in enumerate(self.all): pf.message(""" ## VIEWPORTS ## Viewport %s; Current:%s; Settings: %s """ % (i, v == self.current, v.settings)) def changeLayout(self,nvps=None,ncols=None,nrows=None,pos=None,rstretch=None,cstretch=None): """Change the lay-out of the viewports on the OpenGL widget. nvps: number of viewports ncols: number of columns nrows: number of rows pos: list holding the position and span of each viewport [[row,col,rowspan,colspan],...] rstretch: list holding the stretch factor for each row cstretch: list holding the stretch factor for each column (rows/columns with a higher stretch factor take more of the available space) Each of this parameters is optional. If a number of viewports is given, viewports will be added or removed to match the requested number. By default they are laid out rowwise over two columns. If ncols is an int, viewports are laid out rowwise over ncols columns and nrows is ignored. If ncols is None and nrows is an int, viewports are laid out columnwise over nrows rows. Alternatively, the pos argument can be used to specify the layout of the viewports. """ # add or remove viewports to match the requested number if type(nvps) == int: while len(self.all) > nvps: self.removeView() while len(self.all) < nvps: self.addView() # get the new layout definition if type(ncols) == int: rowwise = True pos = None elif type(nrows) == int: ncols = nrows rowwise = False pos = None elif type(pos) == list and len(pos) == len(self.all): ncols = None rowwise = None else: return # remove the viewport widgets for w in self.all: self.removeWidget(w) # assign the new layout arguments self.ncols = ncols self.rowwise = rowwise self.pos = pos self.rstretch = rstretch self.cstretch = cstretch # add the viewport widgets for w in self.all: self.showWidget(w) def link(self,vp,to): """Link viewport vp to to""" print "LINK %s to %s" % (vp,to) nvps = len(self.all) if vp in range(nvps) and to in range(nvps) and vp != to: to = self.all[to] oldvp = self.all[vp] import warnings warnings.warn('warn_viewport_linking') newvp = self.newView(to) self.all[vp] = newvp self.removeWidget(oldvp) oldvp.close() self.showWidget(newvp) vp = newvp vp.actors = to.actors vp.bbox = to.bbox vp.show() vp.setCamera() vp.redrawAll() #vp.updateGL() pf.GUI.processEvents() def _auto_initialize(): global MultiCanvas try: if pf.options.newviewports: MultiCanvas = NewMultiCanvas except: pass _auto_initialize() # End pyformex-0.8.6/pyformex/gui/decors.py0000644000211500021150000004670411705104656017540 0ustar benebene00000000000000# $Id: decors.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """2D decorations for the OpenGL canvas.""" from OpenGL import GL from PyQt4 import QtOpenGL from drawable import * from text import * from marks import TextMark import colors import gluttext ### Some drawing functions ############################################### def drawDot(x,y): """Draw a dot at canvas coordinates (x,y).""" GL.glBegin(GL.GL_POINTS) GL.glVertex2f(x,y) GL.glEnd() def drawLine(x1,y1,x2,y2): """Draw a straight line from (x1,y1) to (x2,y2) in canvas coordinates.""" GL.glBegin(GL.GL_LINES) GL.glVertex2f(x1,y1) GL.glVertex2f(x2,y2) GL.glEnd() def drawGrid(x1,y1,x2,y2,nx,ny): """Draw a rectangular grid of lines The rectangle has (x1,y1) and and (x2,y2) as opposite corners. There are (nx,ny) subdivisions along the (x,y)-axis. So the grid has (nx+1) * (ny+1) lines. nx=ny=1 draws a rectangle. nx=0 draws 1 vertical line (at x1). nx=-1 draws no vertical lines. ny=0 draws 1 horizontal line (at y1). ny=-1 draws no horizontal lines. """ GL.glBegin(GL.GL_LINES) ix = range(nx+1) if nx==0: jx = [1] nx = 1 else: jx = ix[::-1] for i,j in zip(ix,jx): x = (i*x2+j*x1)/nx GL.glVertex2f(x, y1) GL.glVertex2f(x, y2) iy = range(ny+1) if ny==0: jy = [1] ny = 1 else: jy = iy[::-1] for i,j in zip(iy,jy): y = (i*y2+j*y1)/ny GL.glVertex2f(x1, y) GL.glVertex2f(x2, y) GL.glEnd() def drawRect(x1,y1,x2,y2): """Draw the circumference of a rectangle.""" drawGrid(x1,y1,x2,y2,1,1) def drawRectangle(x1,y1,x2,y2,color): """Draw a single rectangular quad.""" color = resize(asarray(color),(4,3)) coord = [(x1,y1),(x2,y1),(x2,y2),(x1,y2)] GL.glBegin(GL.GL_QUADS) for c,x in zip(color,coord): GL.glColor3fv(c) GL.glVertex2fv(x) GL.glEnd() ### Decorations ############################################### class Decoration(Drawable): """A decoration is a 2-D drawing at canvas position x,y. All decorations have at least the following attributes: - x,y : (int) window coordinates of the insertion point - drawGL() : function that draws the decoration at (x,y). This should only use openGL function that are allowed in a display list. """ def __init__(self,x,y,**kargs): """Create a decoration at canvas coordinates x,y""" self.x = int(x) self.y = int(y) if 'nolight' not in kargs: kargs['nolight'] = True Drawable.__init__(self,**kargs) # Marks database: a dict with mark name and a function to draw # the mark. The _marks_ = { 'dot':drawDot, } class Mark(Decoration): """A mark at a fixed position on the canvas.""" def __init__(self,x,y,mark='dot',color=None,linewidth=None,**kargs): Decoration.__init__(self,x,y,**kargs) self.x = x self.y = y if not mark in _marks_: raise ValueError,"Unknown mark: %s" % mark self.mark = mark self.color = saneColor(color) self.linewidth = saneLineWidth(linewidth) def drawGL(self,**kargs): if self.color is not None: GL.glColor3fv(self.color) if self.linewidth is not None: GL.glLineWidth(self.linewidth) _marks_[self.mark](self.x,self.y) class Line(Decoration): """A straight line on the canvas.""" def __init__(self,x1,y1,x2,y2,color=None,linewidth=None,**kargs): Decoration.__init__(self,x1,y1,**kargs) self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2 self.color = saneColor(color) self.linewidth = saneLineWidth(linewidth) def drawGL(self,**kargs): if self.color is not None: GL.glColor3fv(self.color) if self.linewidth is not None: GL.glLineWidth(self.linewidth) drawLine(self.x1,self.y1,self.x2,self.y2) ## ## class QText(Decoration): ## ## """A viewport decoration showing a text.""" ## ## def __init__(self,text,x,y,adjust='left',font=None,size=None,color=None): ## ## """Create a text actor""" ## ## Decoration.__init__(self,x,y) ## ## self.text = str(text) ## ## self.adjust = adjust ## ## self.font = getFont(font,size) ## ## self.color = saneColor(color) ## ## count = 0 ## ## # QT text color does not seem to work good with display lists, ## ## # therefore we redefine draw(), not drawGL() ## ## def draw(self,mode='wireframe',color=None): ## ## """Draw the text.""" ## ## self.count += 1 ## ## # pf.canvas.makeCurrent() ## ## if self.color is not None: ## ## GL.glColor3fv(self.color) ## ## pf.canvas.renderText(self.x,pf.canvas.height()-self.y,self.text,self.font) ## ## # pf.canvas.swapBuffers() ## ## # pf.canvas.updateGL() class GlutText(Decoration): """A viewport decoration showing a text string. - text: a simple string, a multiline string or a list of strings. If it is a string, it will be splitted on the occurrence of '\\n' characters. - x,y: insertion position on the canvas - gravity: a string that determines the adjusting of the text with respect to the insert position. It can be a combination of one of the characters 'N or 'S' to specify the vertical positon, and 'W' or 'E' for the horizontal. The default(empty) string will center the text. """ def __init__(self,text,x,y,font='9x15',size=None,gravity=None,color=None,zoom=None,**kargs): """Create a text actor""" Decoration.__init__(self,x,y,**kargs) self.text = str(text) self.font = gluttext.glutSelectFont(font,size) if gravity is None: gravity = 'E' self.gravity = gravity self.color = saneColor(color) self.zoom = zoom def drawGL(self,**kargs): """Draw the text.""" ## if self.zoom: ## pf.canvas.zoom_2D(self.zoom) if self.color is not None: GL.glColor3fv(self.color) gluttext.glutDrawText(self.text,self.x,self.y,font=self.font,gravity=self.gravity) ## if self.zoom: ## pf.canvas.zoom_2D() Text = GlutText class ColorLegend(Decoration): """A labeled colorscale legend. When showing the distribution of some variable over a domain by means of a color encoding, the viewer expects some labeled colorscale as a guide to decode the colors. The ColorLegend decoration provides such a color legend. This class only provides the visual details of the scale. The conversion of the numerical values to the matching colors is provided by the :class:`colorscale.ColorLegend` class. Parameters: - `colorlegend`: a :class:`colorscale.ColorLegend` instance providing conversion between numerical values and colors - `x,y,w,h`: four integers specifying the position and size of the color bar rectangle - `ngrid`: int: number of intervals for the grid lines to be shown. If > 0, grid lines are drawn around the color bar and between the ``ngrid`` intervals. If = 0, no grid lines are drawn. If < 0 (default), the value is set equal to the number of colors (as set in the ``colorlegend``) or to 0 if this number is higher than 50. - `linewidth`: float: width of the grid lines. If not specified, the current canvas line width is used. - `nlabel`: int: number of intervals for the labels to be shown. If > 0, labels will be displayed at `nlabel` interval borders, if possible. The number of labels displayed thus will be ``nlabel+1``, or less if the labels would otherwise be too close or overlapping. If 0, no labels are shown. If < 0 (default), a default number of labels is shown. - `font`, `size`: font and size to be used for the labels - `dec`: int: number of decimals to be used in the labels - `scale`: int: exponent of 10 for the scaling factor of the label values. The displayed values will be equal to the real values multiplied with ``10**scale``. - `lefttext`: bool: if True, the labels will be drawn to the left of the color bar. The default is to draw the labels at the right. Some practical guidelines: - The number of colors is defined by the ``colorlegend`` argument. - Large numbers of colors result inb a quasi continuous color scheme. - With a high number of colors, grid lines disturb the image, so either use ``ngrid=0`` or ``ngrid=`` to only draw a border around the colors. - With a small number of colors, set ``ngrid = len(colorlegend.colors)`` to add gridlines between each color. Without it, the individual colors in the color bar may seem to be not constant, due to an optical illusion. Adding the grid lines reduces this illusion. - When using both grid lines and labels, set both ``ngrid`` and ``nlabel`` to the same number or make one a multiple of the other. Not doing so may result in a very confusing picture. - The best practices are to use either a low number of colors (<=20) and the default ``ngrid`` and ``nlabel``, or a high number of colors (>=200) and the default values or a low value for ``nlabel``. The `ColorScale` example script provides opportunity to experiment with different settings. """ def __init__(self,colorlegend,x,y,w,h,ngrid=0,linewidth=None,nlabel=-1,font=None,size=None,dec=2,scale=0,lefttext=False,**kargs): """Initialize the ColorLegend.""" Decoration.__init__(self,x,y,**kargs) self.cl = colorlegend self.w = int(w) self.h = int(h) self.ngrid = int(ngrid) if self.ngrid < 0: self.ngrid = len(self.cl.colors) if self.ngrid > 50: self.ngrid = 0 self.linewidth = saneLineWidth(linewidth) self.nlabel = int(nlabel) if self.nlabel < 0: self.nlabel = len(self.cl.colors) self.font = gluttext.glutSelectFont(font,size) self.dec = dec # number of decimals self.scale = 10 ** scale # scale all numbers with 10**scale self.lefttext = lefttext self.xgap = 4 # hor. gap between color bar and labels self.ygap = 4 # (min) vert. gap between labels def drawGL(self,**kargs): self.decorations = [] n = len(self.cl.colors) pf.debug("NUMBER OF COLORS: %s" % n) x1 = float(self.x) x2 = float(self.x+self.w) y0 = float(self.y) dy = float(self.h)/n # colors y1 = y0 #GL.glLineWidth(1.5) for i,c in enumerate(self.cl.colors): y2 = y0 + (i+1)*dy GL.glColor3f(*c) GL.glRectf(x1,y1,x2,y2) y1 = y2 if self.nlabel > 0 or self.ngrid > 0: GL.glColor3f(*colors.black) # labels if self.nlabel > 0: fh = gluttext.glutFontHeight(self.font) pf.debug("FONT HEIGHT %s" % fh) # minimum label distance dh = fh + self.ygap maxlabel = float(self.h)/dh # maximum n umber of labels if self.nlabel <= maxlabel: dh = float(self.h)/self.nlabel # respect requested number if self.lefttext: x1 = x1 - self.xgap gravity = 'W' else: x1 = x2 + self.xgap gravity = 'E' # FOR 3-VALUE SCALES THIS SHOULD BE DONE IN TWO PARTS, # FROM THE CENTER OUTWARDS, AND THEN ADDING THE # MIN AND MAX VALUES for i,v in enumerate(self.cl.limits): y2 = y0 + i*dy if y2 >= y1 or i == 0 or i == len(self.cl.limits)-1: if y2 >= self.y+self.h-dh/2 and i < len(self.cl.limits)-1: continue t = Text(("%%.%df" % self.dec) % (v*self.scale),x1,round(y2),font=self.font,gravity=gravity) self.decorations.append(t) t.drawGL(**kargs) y1 = y2 + dh # grid: after values, to be on top if self.ngrid > 0: if self.linewidth is not None: GL.glLineWidth(self.linewidth) drawGrid(self.x,self.y,self.x+self.w,self.y+self.h,1,self.ngrid) def use_list(self): Decoration.use_list(self) for t in self.decorations: t.use_list() class Rectangle(Decoration): """A 2D-rectangle on the canvas.""" def __init__(self,x1,y1,x2,y2,color=None,**kargs): Decoration.__init__(self,x1,y1,**kargs) self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2 self.setColor(color,ncolors=4) def drawGL(self,**kargs): drawRectangle(self.x1,self.y1,self.x2,self.y2,self.color) class Grid(Decoration): """A 2D-grid on the canvas.""" def __init__(self,x1,y1,x2,y2,nx=1,ny=1,color=None,linewidth=None,**kargs): Decoration.__init__(self,x1,y1,**kargs) self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2 self.nx = nx self.ny = ny self.color = saneColor(color) self.linewidth = saneLineWidth(linewidth) def drawGL(self,**kargs): if self.color is not None: GL.glColor3fv(self.color) if self.linewidth is not None: GL.glLineWidth(self.linewidth) drawGrid(self.x1,self.y1,self.x2,self.y2,self.nx,self.ny) class LineDrawing(Decoration): """A collection of straight lines on the canvas.""" def __init__(self,data,color=None,linewidth=None,**kargs): """Initially a Line Drawing. data can be a 2-plex Formex or equivalent coordinate data. The z-coordinates of the Formex are unused. A (n,2,2) shaped array will do as well. """ data = data.view() data = data.reshape((-1,2,data.shape[-1])) data = data[:,:,:2] self.data = data.astype(Float) x1,y1 = self.data[0,0] Decoration.__init__(self,x1,y1,**kargs) self.color = saneColor(color) self.linewidth = saneLineWidth(linewidth) def drawGL(self,**kargs): if self.color is not None: GL.glColor3fv(self.color) if self.linewidth is not None: GL.glLineWidth(self.linewidth) GL.glBegin(GL.GL_LINES) for e in self.data: GL.glVertex2fv(e[0]) GL.glVertex2fv(e[1]) GL.glEnd() # Not really a decoration, though it could be made into one # Can this be merged with actors.AxesActor ? # class Triade(Drawable): """An OpenGL actor representing a triade of global axes. - `pos`: position on the canvas: two characters, of which first sets horizontal position ('l', 'c' or 'r') and second sets vertical position ('b', 'c' or 't'). - `size`: size in pixels of the zone displaying the triade. - `pat`: shape to be drawn in the coordinate planes. Default is a square. '16' givec a triangle. '' disables the planes. - `legend`: text symbols to plot at the end of the axes. A 3-character string or a tuple of 3 strings. """ def __init__(self,pos='lb',siz=100,pat='3:012934',legend='xyz',color=[red,green,blue,cyan,magenta,yellow],**kargs): Drawable.__init__(self,**kargs) self.pos = pos self.siz = siz self.pat = pat self.legend = legend self.color = color def _draw_me(self): """Draw the triade components.""" GL.glBegin(GL.GL_LINES) pts = Formex('1').coords.reshape(-1,3) GL.glColor3f(*black) for i in range(3): #GL.glColor(*self.color[i]) for x in pts: GL.glVertex3f(*x) pts = pts.rollAxes(1) GL.glEnd() # Coord planes if self.pat: GL.glBegin(GL.GL_TRIANGLES) pts = Formex(self.pat) #pts += pts.reverse() pts = pts.scale(0.5).coords.reshape(-1,3) for i in range(3): pts = pts.rollAxes(1) GL.glColor3f(*self.color[i]) for x in pts: GL.glVertex3f(*x) GL.glEnd() # Coord axes denomination for i,x in enumerate(self.legend): p = unitVector(i)*1.1 t = TextMark(p,x) t.drawGL() def _draw_relative(self): """Draw the triade in the origin and with the size of the 3D space.""" GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPushMatrix() GL.glTranslatef (*self.pos) GL.glScalef (self.size,self.size,self.size) self._draw_me() GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPopMatrix() def _draw_absolute(self): """Draw the triade in the lower left corner.""" GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPushMatrix() # Cancel the translations rot = GL.glGetFloatv(GL.GL_MODELVIEW_MATRIX) rot[3,0:3] = [0.,0.,0.] GL.glLoadMatrixf(rot) vp = GL.glGetIntegerv(GL.GL_VIEWPORT) x,y,w,h = vp w0,h0 = self.siz,self.siz # we force aspect ratio 1 if self.pos[0] == 'l': x0 = x elif self.pos[0] =='r': x0 = x + w-w0 else: x0 = x + (w-w0)/2 if self.pos[1] == 'b': y0 = y elif self.pos[1] =='t': y0 = y + h-h0 else: y0 = y + (h-h0)/2 GL.glViewport(x0,y0,w0,h0) GL.glMatrixMode(GL.GL_PROJECTION) GL.glPushMatrix() GL.glLoadIdentity() fovy = 45. fv = tand(fovy*0.5) fv *= 4. fh = fv # BEWARE: near/far should be larger than size, but not very large # or the depth sort will fail frustum = (-fh,fh,-fv,fv,-3.,100.) GL.glOrtho(*frustum) GL.glDisable(GL.GL_LIGHTING) GL.glDisable (GL.GL_BLEND) GL.glPolygonMode(GL.GL_FRONT_AND_BACK,GL.GL_FILL) GL.glDisable(GL.GL_CULL_FACE) GL.glClearDepth(1.0) GL.glDepthMask (GL.GL_TRUE) GL.glDepthFunc(GL.GL_LESS) GL.glEnable(GL.GL_DEPTH_TEST) self._draw_me() GL.glViewport(*vp) GL.glMatrixMode(GL.GL_PROJECTION) GL.glPopMatrix() GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPopMatrix() def draw(self,**kargs): self._draw_absolute() # End pyformex-0.8.6/pyformex/gui/menu.py0000644000211500021150000004577511705104656017234 0ustar benebene00000000000000# $Id: menu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Menus for the pyFormex GUI. This modules implements specialized classes and functions for building the pyFormex GUI menu system. """ import pyformex from pyformex.gui import * from PyQt4 import QtGui,QtCore # For Sphinx import odict import utils import fileMenu import cameraMenu import prefMenu import viewportMenu import scriptMenu import toolbar import helpMenu import image import draw import script import os from gettext import gettext as _ ############################# Menu ############################## class BaseMenu(object): """A general menu class. A hierarchical menu that keeps a list of its item names and actions. The item names are normalized by removing all '&' characters and converting the result to lower case. It thus becomes easy to search for an existing item in a menu. This class is not intended for direct use, but through subclasses. Subclasses should implement at least the following methods: - addSeparator() - insertSeperator(before) - addAction(text,action) - insertAction(before,text,action) - addMenu(text,menu) - insertMenu(before,text,menu) QtGui.Menu and QtGui.MenuBar provide these methods. """ def __init__(self,title='AMenu',parent=None,before=None,items=None): """Create a menu.""" self._title = title pyformex.debug("Creating menu %s" % title) self.parent = parent self.separators = odict.ODict() self._actions_ = [] if items: self.insertItems(items) if parent and isinstance(parent,BaseMenu): before = parent.action(before) parent.insert_menu(self,before) def actionList(self): """Return a list with the current actions.""" return [ utils.strNorm(str(c.text())) for c in self.actions() ] def index(self,text): """Return the index of the specified item in the actionlist. If the requested item is not in the actionlist, -1 is returned. """ try: return self.actionList().index(utils.strNorm(text)) except ValueError: return -1 def action(self,text): """Return the action with specified text. First, a normal action is tried. If none is found, a separator is tried. """ if text is None: return None if text in self.actions(): return text i = self.index(text) if i >= 0: return self.actions()[i] else: return self.separators.get(utils.strNorm(text),None) def item(self,text): """Return the item with specified text. For a normal action or a separator, an qction is returned. For a menu action, a menu is returned. """ i = self.index(text) if i >= 0: a = self.actions()[i] m = a.menu() if m: return m else: return a else: return self.separators.get(utils.strNorm(text),None) def nextitem(self,text): """Returns the name of the next item. This can be used to replace the current item with another menu. If the item is the last, None is returned. """ itemlist = self.actionList() i = itemlist.index(utils.strNorm(text)) #print "POS = %s/%s" % (i,len(itemlist)) if i >= 0 and i < len(itemlist)-1: return itemlist[i+1] else: return None def removeItem(self,item): """Remove an item from this menu.""" #print self.actionList() action = self.action(item) if action: #print "INDEED REMOVING %s = %s" % (item,action) self.removeAction(action) if isinstance(action,QtGui.QMenu): action.close() del action #print self.actionList() # The need for the following functions demonstrates how much more # powerful a dynamically typed language as Python is as compared to # the C++ language used by Qt def insert_sep(self,before=None): """Create and insert a separator""" if before: return self.insertSeparator(before) else: return self.addSeparator() def insert_menu(self,menu,before=None): """Insert an existing menu.""" if before: return self.insertMenu(before,menu) else: return self.addMenu(menu) def insert_action(self,action,before=None): """Insert an action.""" if before: return self.insertAction(before,action) else: #print ("ADDING SAVED ACTION %s" % action) self._actions_.append(action) return self.addAction(action) def create_insert_action(self,str,val,before=None): """Create and insert an action.""" if before: raise RuntimeError,"THIS CAN NOT WORK" return self.insertAction(before,str,val) else: return self.addAction(str,val) def insertItems(self,items,before=None): """Insert a list of items in the menu. Parameters: - `items`: a list of menuitem tuples. Each item is a tuple of two or three elements: (text, action, options): - `text`: the text that will be displayed in the menu item. It is stored in a normalized way: all lower case and with '&' removed. - `action`: can be any of the following: - a Python function or instance method : it will be called when the item is selected, - a string with the name of a function/method, - a list of Menu Items: a popup Menu will be created that will appear when the item is selected, - an existing Menu, - None : this will create a separator item with no action. - `options`: optional dictionary with following honoured fields: - `icon`: the name of an icon to be displayed with the item text. This name should be that of one of the icons in the pyFormex icondir. - `shortcut`: is an optional key combination to select the item. - `tooltip`: a text that is displayed as popup help. - `before`: if specified, should be the text *or* the action of one of the items in the Menu (not the items list!): the new list of items will be inserted before the specified item. """ before = self.action(before) for item in items: txt,val = item[:2] if len(item) > 2: options = item[2] else: options = {} if val is None: a = self.insert_sep(before) self.separators[txt] = a elif isinstance(val, list): a = Menu(txt,parent=self,before=before) a.insertItems(val) else: if type(val) == str: val = eval(val) if 'data' in options: # DActions should be saved to keep them alive !!! a = DAction(txt,data = options['data']) QtCore.QObject.connect(a,QtCore.SIGNAL(a.signal),val) self.insert_action(a,before) else: if before is not None: raise RuntimeError,"I can not insert a QAction menu item before an existing one." #print ("CREATE %s,%s,%s" % (txt,val,before)) a = self.create_insert_action(txt,val,before) for k,v in options.items(): if k == 'icon': a.setIcon(QtGui.QIcon(QtGui.QPixmap(utils.findIcon(v)))) elif k == 'shortcut': a.setShortcut(v) elif k == 'tooltip': a.setToolTip(v) elif k == 'checkable': a.setCheckable(v) elif k == 'checked': a.setCheckable(True) a.setChecked(v) elif k == 'disabled': a.setDisabled(True) class Menu(BaseMenu,QtGui.QMenu): """A popup/pulldown menu.""" def __init__(self,title='UserMenu',parent=None,before=None,tearoff=False,items=None): """Create a popup/pulldown menu. If parent==None, the menu is a standalone popup menu. If parent is given, the menu will be inserted in the parent menu. If parent==pyformex.GUI, the menu is inserted in the main menu bar. If a parent is given, and tearoff==True, the menu can be teared-off. If insert == True, the menu will be inserted in the main menubar before the item specified by before. If before is None or not the normalized text of an item of the main menu, the new menu will be inserted at the end. Calling the close() function of an inserted menu will remove it from the main menu. If insert == False, the created menu will be an independent dialog and the user will have to process it explicitely. """ QtGui.QMenu.__init__(self,title,parent) BaseMenu.__init__(self,title,parent,before,items) if parent is None: self.setWindowFlags(QtCore.Qt.Dialog) self.setWindowTitle(title) else: if tearoff: print "TEAR OFF menus currently not implemented" tearoff = False self.setTearOffEnabled(tearoff) self.done = False def process(self): if not self.done: if not self.insert: self.show() pyformex.app.processEvents() def remove(self): self.close() self.parent.removeItem(self.title()) class MenuBar(BaseMenu,QtGui.QMenuBar): """A menu bar allowing easy menu creation.""" def __init__(self,title='TopMenuBar'): """Create the menubar.""" QtGui.QMenuBar.__init__(self) BaseMenu.__init__(self,title) def title(self): return self._title ###################### Action List ############################################ class DAction(QtGui.QAction): """A DAction is a QAction that emits a signal with a string parameter. When triggered, this action sends a signal (default 'Clicked') with a custom string as parameter. The connected slot can then act depending on this parameter. """ signal = "Clicked" def __init__(self,name,icon=None,data=None,signal=None): """Create a new DAction with name, icon and string data. If the DAction is used in a menu, a name is sufficient. For use in a toolbar, you will probably want to specify an icon. When the action is triggered, the data is sent as a parameter to the SLOT function connected with the 'Clicked' signal. If no data is specified, the name is used as data. See the views.py module for an example. """ QtGui.QAction.__init__(self,name,None) if icon: self.setIcon(icon) if signal is None: signal = DAction.signal self.signal = signal if data is None: data = name self.setData(QtCore.QVariant(data)) self.connect(self,QtCore.SIGNAL("triggered()"),self.activated) def activated(self): self.emit(QtCore.SIGNAL(self.signal), str(self.data().toString())) class ActionList(object): """Menu and toolbar with named actions. An action list is a list of strings, each connected to some action. The actions can be presented in a menu and/or a toolbar. On activating one of the menu or toolbar buttons, a given signal is emitted with the button string as parameter. A fixed function can be connected to this signal to act dependent on the string value. """ def __init__(self,actions=[],function=None,menu=None,toolbar=None,icons=None,text=None): """Create an new action list, empty by default. A list of strings can be passed to initialize the actions. If a menu and/or toolbar are passed, a button is added to them for each string in the action list. If a function is passed, it will be called with the string as parameter when the item is triggered. If no icon names are specified, they are taken equal to the action names. Icons will be taken from the installed icon directory. If you want to specify other icons, use the add() method. """ self.actions = [] self.function = function self.menu = menu self.toolbar = toolbar if icons is None: icons = actions icons = map(utils.findIcon,icons) if text is None: text = actions for name,icon,txt in zip(actions,icons,text): self.add(name,icon,txt) def add(self,name,icon=None,text=None): """Add a new name to the actions list and create a matching DAction. If the actions list has an associated menu or toolbar, a matching button will be inserted in each of these. If an icon is specified, it will be used on the menu and toolbar. The icon is either a filename or a QIcon object. If text is specified, it is displayed instead of the action's name. """ if type(icon) == str: if os.path.exists(icon): icon = QtGui.QIcon(QtGui.QPixmap(icon)) else: raise RuntimeError,'Icons not installed properly' if text is None: text = name a = DAction(text,icon,name) if self.function: QtCore.QObject.connect(a,QtCore.SIGNAL(a.signal),self.function) self.actions.append([name,a]) if self.menu: self.menu.addAction(a) if self.toolbar: self.toolbar.addAction(a) def names(self): """Return an ordered list of names of the action items.""" return [ i[0] for i in self.actions ] ## ########################################################################### # pyFormex main menus save = NotImplemented saveAs = NotImplemented ## def editor(): ## if pyformex.GUI.editor: ## print("Close editor") ## pyformex.GUI.closeEditor() ## else: ## print("Open editor") ## pyformex.GUI.showEditor() def resetGUI(): """Reset the GUI to its default operating mode. When an exception is raised during the execution of a script, the GUI may be left in a non-consistent state. This function may be called to reset most of the GUI components to their default operating mode. """ ## resetPick() pyformex.GUI.resetCursor() pyformex.GUI.actions['Play'].setEnabled(True) pyformex.GUI.actions['Step'].setEnabled(True) pyformex.GUI.actions['Continue'].setEnabled(False) pyformex.GUI.actions['Stop'].setEnabled(False) def resetWarnings(): """Reset the warning filters to the default.""" del pyformex.prefcfg['warnings/filters'] print("This will only become effective in your future sessions!") print "FILTERS:",pyformex.prefcfg['warnings/filters'] # The menu actions can be simply function names instead of strings, if the # functions have already been defined here. def printwindow(): pyformex.app.syncX() r = pyformex.GUI.frameGeometry() print("Qt4 geom(w,h,x,y): %s,%s,%s,%s" % (r.width(),r.height(),r.x(),r.y())) print("According to xwininfo, (x,y) is %s,%s" % pyformex.GUI.XPos()) _geometry=None def saveGeometry(): global _geometry _geometry = pyformex.GUI.saveGeometry() def restoreGeometry(): pyformex.GUI.restoreGeometry(_geometry) def moveCorrect(): pyformex.GUI.move(*pyformex.GUI.XPos()) def closeLogFile(): if draw.logfile: draw.logfile.close() draw.logfile = None def openLogFile(): fn = draw.askFilename(filter=['*.log','*'],multi=False) if fn: closeLogFile() draw.logfile = open(fn,'w') def saveBoard(): fn = draw.askFilename(filter=['*.txt','*'],multi=False,exist=False) if fn: pyformex.GUI.board.save(fn) def createMenuData(): """Returns the default pyFormex GUI menu data.""" ActionMenuData = [ (_('&Step'),draw.step), (_('&Continue'),draw.fforward), ## (_('&Reset Picking Mode'),resetPick), (_('&Reset GUI'),resetGUI), (_('&Reset Warning Filters'),resetWarnings), (_('&Force Finish Script'),script.force_finish), ## (_('&Execute single statement'),command), (_('&Save Message Board'),saveBoard), (_('&Open Log File'),openLogFile), (_('&Close Log File'),closeLogFile), (_('&ListFormices'),script.printall), (_('&PrintGlobalNames'),script.printglobalnames), (_('&PrintGlobals'),script.printglobals), (_('&PrintConfig'),script.printconfig), (_('&Print Detected Software'),script.printdetected), (_('&PrintBbox'),draw.printbbox), (_('&Print Viewport Settings'),draw.printviewportsettings), (_('&Print Window Geometry'),printwindow), (_('&Correct the Qt4 Geometry'),moveCorrect), (_('&Save Geometry'),saveGeometry), (_('&Restore Geometry'),restoreGeometry), (_('&Toggle Input Timeout'),toolbar.timeout), ] MenuData = [ (_('&File'),fileMenu.MenuData), (_('&Actions'),ActionMenuData), (_('&Help'),helpMenu.createMenuData()) ] # Insert configurable menus if pyformex.cfg.get('gui/prefsmenu','True'): MenuData[1:1] = prefMenu.MenuData if pyformex.cfg.get('gui/viewportmenu','True'): MenuData[2:2] = viewportMenu.MenuData if pyformex.cfg.get('gui/cameramenu','True'): MenuData[3:3] = [(_('&Camera'),cameraMenu.MenuData)] return MenuData # End pyformex-0.8.6/pyformex/gui/text.py0000644000211500021150000000360311705104656017234 0ustar benebene00000000000000# $Id: text.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Functions related to text and fonts""" import pyformex from PyQt4 import QtGui def getFont(font=None,size=None): """Get the best fonts matching font name and size If nothing is specified, returns the default GUI font. """ if font is None: font = pyformex.GUI.font() else: font = QtGui.QFont(font) if size is not None: font.setPointSize(size) return font def fontHeight(font=None,size=None): """Return the height in pixels of the given font. This can be used to determine the canvas coordinates where the text is to be drawn. """ font = getFont(font,size) fh = font.pixelSize() if fh < 0: fh = QtGui.QFontInfo(font).pixelSize() return fh # End pyformex-0.8.6/pyformex/gui/marks.py0000644000211500021150000001150011705104656017360 0ustar benebene00000000000000# $Id: marks.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """OpenGL marks for annotating 3D actors.""" from OpenGL import GL,GLU from colors import * from formex import * from drawable import * from text import * import gluttext ### Marks ############################################### class Mark(Drawable): """A 2D drawing inserted at a 3D position of the scene. The minimum attributes and methods are: pos : 3D point where the mark will be drawn draw() : function to draw the mark """ def __init__(self,pos,nolight=True,**kargs): self.pos = pos Drawable.__init__(self,nolight=nolight,**kargs) class AxesMark(Mark): """Two viewport axes drawn at a 3D position.""" def __init__(self,pos,color=None,**kargs): Mark.__init__(self,pos,**kargs) self.color = saneColor(color) def drawGL(self,**kargs): if self.color is not None: GL.glColor3fv(self.color) GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT,1) GL.glRasterPos3fv(self.pos) a = 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x80 b = 0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00 bitmap = [b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,a,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b] GL.glBitmap(81,81,41,41,0,0,bitmap) class TextMark(Mark): """A text drawn at a 3D position.""" def __init__(self,pos,text,color=None,font='sans',size=18,**kargs): Mark.__init__(self,pos,**kargs) self.text = text self.color = saneColor(color) self.font = gluttext.glutSelectFont(font,size) def drawGL(self,**kargs): if self.color is not None: GL.glColor3fv(self.color) GL.glRasterPos3fv(self.pos) gluttext.glutRenderText(self.text,self.font) ## def use_list(self): ## Mark.use_list(self) ## gluttext.glutRenderText(self.text,self.font) ## #x,y,z = self.pos ## #pf.canvas.renderText(x,y,z,self.text,self.font) class MarkList(Mark): """A list of numbers drawn at 3D positions.""" def __init__(self,pos,val,color=black,font='sans',size=18,leader='',gravity='',**kargs): """Create a number list. pos is an (N,3) array of positions. val is an (N,) array of marks to be plot at those positions. While intended to plot integer numbers, val can be any object that allows index operations for the required length N and allows its items to be formatted as a string. """ if len(val) < len(pos): raise ValueError,"Not enough values for positions" Mark.__init__(self,pos,**kargs) self.val = val self.color = saneColor(color) self.font = gluttext.glutSelectFont(font,size) #self.font = getFont(font,size) self.leader = str(leader) self.gravity = gravity def drawGL(self,**kargs): if self.color is not None: GL.glColor3fv(self.color) for p,v in zip(self.pos,self.val): GL.glRasterPos3fv(p) gluttext.glutRenderText(self.leader+str(v),self.font,self.gravity) def drawpick(self): """This functions mimicks the drawing of a number list for picking.""" GL.glSelectBuffer(16+3*len(self.val)) GL.glRenderMode(GL.GL_SELECT) GL.glInitNames() # init the name stack for p,v in zip(self.pos,self.val): GL.glPushName(v) GL.glRasterPos3fv(p) #drawGlutText(str(v),self.font) GL.glPopName() buf = GL.glRenderMode(GL.GL_RENDER) numbers =[] for r in buf: numbers += map(int,r[2]) return numbers # End pyformex-0.8.6/pyformex/gui/scriptMenu.py0000644000211500021150000004574711705104656020420 0ustar benebene00000000000000# $Id: scriptMenu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Menu with pyFormex scripts.""" import pyformex as pf from PyQt4 import QtCore, QtGui import utils import script,draw import menu import os,random from gettext import gettext as _ catname = 'scripts.cat' def extractKeyword(s): """Extract a ``keyword = value`` pair from a string. If the input string `s` is of the form ``keyword = value`` a tuple (keyword,value) is returned, else None. """ i = s.find('=') if i >= 0: try: key = s[:i].strip() if len(key) > 0: return key, eval(s[i+1:].strip()) except: pf.debug("Error processing keywords %s" % s.strip('\n')) pass return None def scriptKeywords(fn,keyw=None): """Read the script keywords from a script file. - `fn`: the full path name of a pyFormex script file. - `keyw`: an optional list of keywords. Script keywords are written in the form:: key = value in the docstring of the script. The docstring is the first non-indented multiline string of the file. A multiline string is a string delimited by triple double-quotes. Matching lines are placed in a dictionary which becomes the return value. If a list of keywords is given, the return dictionary will only contain the matching values. """ fil = open(fn,'r') keys = {} ok = False for line in fil: if not ok and line.startswith('"""'): ok = True line = line[3:] if ok: i = line.find('"""') if i >= 0: line = line[:i] pair = extractKeyword(line) if pair: keys.update((pair,)) if i >= 0: return keys return keys def sortSets(d): """Turn the set values in d into sorted lists. - `d`: a Python dictionary All the values in the dictionary are checked. Those that are of type `set` are converted to a sorted list. """ for k in d: if type(d[k]) == set: d[k] = list(d[k]) d[k].sort() def getDocString(scriptfile): fil = open(scriptfile,'r') s = fil.read() i = s.find('"""') if i >= 0: j = s.find('"""',i+1) if j >= i+2: return s[i+2:j] return '' def getDescription(doc): txt = doc.partition('.. Description') #print txt[1] #print len(txt[1]) return ' '.join(txt[1:]) class ScriptMenu(QtGui.QMenu): """A menu of pyFormex scripts in a directory or list. This class creates a menu of pyFormex scripts collected from a directory or specified in a list. It is e.g. used in the pyFormex GUI to create the examples menu, and for the scripts history. The pyFormex scripts can then be executed from the menu. The user may use this class to add his own scripts into the pyFormex GUI. Only files that are recognized by :func:`utils.is_pyFormex()` as being pyFormex scripts will be added to the menu. The constructor takes the following arguments: - `title`: the top level label for the menu - `files`: a list of file names of pyFormex scripts. If no `dir` nor `ext` arguments are given, these should be the full path names to the script files. If omitted, all files in the directory `dir` whose name is ending with `ext` *and do not start with either '.' or '_'*, will be selected. - `dir`: an optional directory path. If given, it will be prepended to each file name in `files` and `recursive` will be True by default. - `ext`: an extension to be added to each filename. If `dir` was specified, the default extension is '.py'. If no `dir` was specified, the default extension is an empty string. - `recursive`: if True, a cascading menu of all pyFormex scripts in the directory and below will be constructed. - `max`: if specified, the list of files will be truncated to this number of items. Adding more files to the menu will then be done at the top and the surplus number of files will be dropped from the bottom of the list. The defaults were thus chosen to be convenient for the two most frequent uses of this class:: ScriptMenu('My Scripts',dir="/path/to/my/sciptsdir") creates a menu will all pyFormex scripts in the specified path and its subdirectories. :: ScriptMenu('History',files=["/my/script1.py","/some/other/script.pye"],recursive=False) is typically used to create a history menu of previously visited files. With the resulting file list, a menu is created. Selecting a menu item will make the corresponding file the current script and unless the `autoplay` configuration variable was set to False, the script is executed. If the file specification was done by a directory path only, some extra options will be included in the menu. They are fairly self-explaining and mainly intended for the pyFormex developers, in order to test the functionality by running a sets of example scripts: - :menuselection:`Run next script` - :menuselection:`Run all following scripts` - :menuselection:`Run all scripts` - :menuselection:`Run a random script` - :menuselection:`Run all in random order` Furthermore, if the menu is a toplevel one, it will have the following extra options: - :menuselection:`Classify scripts` - :menuselection:`Remove catalog` - :menuselection:`Reload scripts` The first option uses the keyword specifications in the scripts docstring to make a classification of the scripts according to keywords. See the :func:`scriptKeywords()` function for more info. The second option removes the classification. Both options are especially useful for the pyFormex examples. The last option reloads a ScriptMenu. This can be used to update the menu when you created a new script file. """ def __init__(self,title,dir=None,files=None,ext=None,recursive=None,max=0,autoplay=False,toplevel=True): """Create a menu with pyFormex scripts to play.""" QtGui.QMenu.__init__(self,title) self.dir = dir self.files = files if self.dir is None and self.files is None: raise ValueError,"At least one of 'dir' or 'files' must be set." if ext is None: if self.dir is None: ext = '' else: ext = '.py' self.ext = ext if recursive is None: recursive = True self.recursive = recursive and self.dir is not None self.toplevel = toplevel self.max = max self.autoplay = autoplay self.menus = [] self.load() def fileName(self,scriptname): """Return the full pathname for a scriptname.""" fn = scriptname + self.ext if self.dir: return os.path.join(self.dir,fn) else: return fn def loadSubmenus(self,dirs=[]): if not dirs: dirs = os.listdir(self.dir) filtr = lambda s:os.path.isdir(os.path.join(self.dir,s)) dirs = filter(filtr,dirs) filtr = lambda s: s[0]!='.' and s[0]!='_' dirs = filter(filtr,dirs) dirs.sort() for d in dirs: m = ScriptMenu(d,os.path.join(self.dir,d),autoplay=self.autoplay,recursive=self.recursive) self.addMenu(m) self.menus.append(m) def getFiles(self): """Get a list of scripts in self.dir""" files = os.listdir(self.dir) filtr = lambda s: s[0]!='.' and s[0]!='_' files = filter(filtr,files) if self.ext: filtr = lambda s: s.endswith(self.ext) files = filter(filtr,files) n = len(self.ext) files = [ f[:-n] for f in files ] files = self.filterFiles(files) ## filtr = lambda s:utils.is_pyFormex(self.fileName(s)) ## files = filter(filtr,files) ## if self.max > 0 and len(files) > self.max: ## files = files[:self.max] files.sort() return files def filterFiles(self,files): """Filter a list of scripts""" filtr = lambda s:utils.is_pyFormex(self.fileName(s)) files = filter(filtr,files) if self.max > 0 and len(files) > self.max: files = files[:self.max] return files def loadFiles(self,files=None): """Load the script files in this menu""" if files is None: files = self.getFiles() self.files = self.filterFiles(files) if pf.options.debug: print("Found Scripts in %s" % self.dir) print(self.files) self.actions = [ self.addAction(f) for f in self.files ] self.connect(self,QtCore.SIGNAL("triggered(QAction*)"),self.run) if self.dir: self.addSeparator() self.addAction('Run next script',self.runNext) self.addAction('Run all following scripts',self.runAllNext) self.addAction('Run all scripts',self.runAll) self.addAction('Run a random script',self.runRandom) self.addAction('Run all in random order',self.runAllRandom) self.current = "" def loadCatalog(self): catfile = os.path.join(self.dir,catname) if os.path.exists(catfile): execfile(catfile,globals()) for k in kat: if k == 'all': files = col[k] else: files = [] mk = ScriptMenu(k.capitalize(),dir=self.dir,files=files,recursive=False,toplevel=False,autoplay=self.autoplay) for i in cat[k]: ki = '%s/%s' % (k,i) mi = ScriptMenu(i.capitalize(),dir=self.dir,files=col.get(ki,[]),recursive=False,toplevel=False,autoplay=self.autoplay) mk.addMenu(mi) mk.menus.append(mi) self.addMenu(mk) self.menus.append(mk) self.files = [] return True return False def load(self): if self.dir is None: self.loadFiles(self.files) else: if self.files is None: self.loadCatalog() if self.recursive: self.loadSubmenus() if self.files or self.files is None: self.loadFiles(self.files) if self.toplevel: self.addAction('Classify scripts',self._classify) self.addAction('Remove catalog',self._unclassify) self.addAction('Reload scripts',self.reload) def run(self,action): """Run the selected script.""" script = str(action.text()) if script in self.files: self.runScript(script) def runScript(self,filename): """Run the specified script.""" self.current = filename selected = self.fileName(filename) pf.debug("Playing script %s" % selected) pf.GUI.setcurfile(selected) if self.autoplay: pf.debug("Drawing Options: %s" % pf.canvas.options) draw.reset() draw.play() def runNext(self): """Run the next script.""" try: i = self.files.index(self.current) + 1 except ValueError: i = 0 print("You should first run a script from the menu, to define the next") return pf.debug("This is script %s out of %s" % (i,len(self.files))) if i < len(self.files): self.runScript(self.files[i]) def runAllNext(self): """Run the current and all following scripts.""" try: i = self.files.index(self.current) except ValueError: i = 0 print("You should first run a script from the menu, to define the following") return pf.debug("Running scripts %s-%s" % (i,len(self.files))) self.runAllFiles(self.files[i:]) pf.debug("Exiting runAllNext") def runAll(self): """Run all scripts.""" pf.debug("Playing all scripts in order") self.runAllFiles(self.files) pf.debug("Finished playing all scripts") ### THIS should be moved to a playAll function in draw/script module ### Currently, it is only intended for testing the examples ### THus we can permit to add some adhoc solutions, like resetting ### the layout at each new script def runAllFiles(self,files,randomize=False,pause=0.): """Run all the scripts in given list.""" pf.GUI.actions['Stop'].setEnabled(True) if randomize: random.shuffle(files) for f in files: while script.scriptlock: print "WAITING BECAUSE OF SCRIPTLOCK" draw.sleep(5) draw.layout(1) self.runScript(f) if draw.exitrequested: break ## if pause > 0.: ## sleep(pause) pf.GUI.actions['Stop'].setEnabled(False) def runRandom(self): """Run a random script.""" i = random.randint(0,len(self.files)-1) self.runScript(self.files[i]) def runAllRandom(self): """Run all scripts in a random order.""" pf.debug("Playing all scripts in random order") self.runAllFiles(self.files,randomize=True) pf.debug("Finished playing all scripts") def reload(self): """Reload the scripts from dir. This is only available if a directory path was specified and no files. """ pf.debug("RELOADING THIS MENU") if self.dir: self.clear() self.menus = [] self.files = None self.load() def add(self,filename,strict=True): """Add a new filename to the front of the menu. By default, only legal pyFormex scripts can be added. """ if strict and not utils.is_pyFormex(filename): return files = self.files if filename in files: files.remove(filename) files[0:0] = [ filename ] if self.max > 0 and len(files) > self.max: files = files[:self.max] while len(self.actions) < len(files): self.actions.append(self.addAction(filename)) for a,f in zip(self.actions,self.files): a.setText(f) def classify(self): """Classify the files in submenus according to keywords.""" kat = ['level','topics','techniques','all'] cat = dict([ (k,set()) for k in kat]) cat['level'] = [ 'beginner', 'normal', 'advanced' ] col = {'all':set()} for f in self.filterFiles(self.getFiles()): col['all'].update([f]) fn = self.fileName(f) d = scriptKeywords(fn) for k,v in d.items(): if not k in kat: pf.debug("Skipping unknown keyword %s in script %s" % (k,fn)) continue if k == 'level': v = [v] else: cat[k].update(v) for i in v: ki = '%s/%s' % (k,i) if not ki in col.keys(): col[ki] = set() col[ki].update([f]) sortSets(cat) sortSets(col) return kat,cat,col def _classify(self): """Classify, symlink and reload the scripts""" if self.dir: f = os.path.join(self.dir,catname) s = "kat = %r\ncat = %r\ncol = %r\n" % self.classify() open(f,'w').writelines(s) self.reload() def _unclassify(self): """Remove the catalog and reload the scripts unclassified""" if self.dir: f = os.path.join(self.dir,catname) if os.path.exists(f): os.remove(f) self.reload() ############### The pyFormex Script menu ############################ from prefMenu import setScriptDirs def createScriptMenu(parent=None,before=None): """Create the menu(s) with pyFormex scripts This creates a menu with all examples distributed with pyFormex. By default, this menu is put in the top menu bar with menu label 'Examples'. The user can add his own script directories through the configuration settings. In that case the 'Examples' menu and menus for all the configured script paths will be gathered in a top level popup menu labeled 'Scripts'. The menu will be placed in the top menu bar before the specified item. If a menu item named 'Examples' or 'Scripts' already exists, it is replaced. """ from odict import ODict scriptmenu = menu.Menu('&Scripts',parent=parent,before=before) scriptmenu.menuitems = ODict() # Create a copy to leave the cfg unchanged! scriptdirs = [] + pf.cfg['scriptdirs'] # Fill in missing default locations : this enables the user # to keep the pyFormex installed examples in his config knownscriptdirs = { 'examples': pf.cfg['examplesdir'] } for i,item in enumerate(scriptdirs): if type(item[0]) is str and not item[1] and item[0].lower() in knownscriptdirs: scriptdirs[i] = (item[0].capitalize(),knownscriptdirs[item[0].lower()]) for txt,dirname in scriptdirs: pf.debug("Loading script dir %s" % dirname) if os.path.exists(dirname): m = ScriptMenu(txt,dir=dirname,autoplay=True) scriptmenu.insert_menu(m) txt = utils.strNorm(txt) scriptmenu.menuitems[txt] = m scriptmenu.insertItems([ ('---',None), (_('&Configure Script Paths'),setScriptDirs), (_('&Reload Script Menu'),reloadScriptMenu), ]) return scriptmenu def reloadScriptMenu(): menu = pf.GUI.menu.item('scripts') if menu is not None: before = pf.GUI.menu.nextitem('scripts') pf.GUI.menu.removeItem('scripts') newmenu = createScriptMenu(pf.GUI.menu,before) # End pyformex-0.8.6/pyformex/gui/cameraMenu.py0000644000211500021150000000751511705104656020333 0ustar benebene00000000000000# $Id: cameraMenu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Camera handling menu""" import pyformex as pf import draw import toolbar from gettext import gettext as _ from guifunc import * from plugins.cameratools import showCameraTool @viewport_function def zoomIn(*args,**kargs): pass @viewport_function def zoomOut(*args,**kargs): pass @viewport_function def dollyIn(*args,**kargs): pass @viewport_function def dollyOut(*args,**kargs): pass @viewport_function def panLeft(*args,**kargs): pass @viewport_function def panRight(*args,**kargs): pass @viewport_function def panDown(*args,**kargs): pass @viewport_function def panUp(*args,**kargs): pass @viewport_function def transLeft(*args,**kargs): pass @viewport_function def transRight(*args,**kargs): pass @viewport_function def transDown(*args,**kargs): pass @viewport_function def transUp(*args,**kargs): pass @viewport_function def rotLeft(*args,**kargs): pass @viewport_function def rotRight(*args,**kargs): pass @viewport_function def rotDown(*args,**kargs): pass @viewport_function def rotUp(*args,**kargs): pass @viewport_function def twistRight(*args,**kargs): pass @viewport_function def twistLeft(*args,**kargs): pass @viewport_function def lockCamera(*args,**kargs): pass @viewport_function def unlockCamera(*args,**kargs): pass @viewport_function def reportCamera(*args,**kargs): pass @viewport_function def zoomAll(*args,**kargs): pass @viewport_function def zoomRectangle(*args,**kargs): pass MenuData = [ (_('&LocalAxes'),draw.setLocalAxes), (_('&GlobalAxes'),draw.setGlobalAxes), (_('&Projection'),toolbar.setProjection), (_('&Perspective'),toolbar.setPerspective), (_('&Zoom Rectangle'),zoomRectangle), (_('&Zoom All'),zoomAll), (_('&Zoom In'),zoomIn), (_('&Zoom Out'),zoomOut), (_('&Dolly In'),dollyIn), (_('&Dolly Out'),dollyOut), (_('&Pan Left'),panLeft), (_('&Pan Right'),panRight), (_('&Pan Down'),panDown), (_('&Pan Up'),panUp), (_('&Translate'),[ (_('Translate &Left'),transLeft), (_('Translate &Right'),transRight), (_('Translate &Down'),transDown), (_('Translate &Up'),transUp), ]), (_('&Rotate'),[ (_('Rotate &Left'),rotLeft), (_('Rotate &Right'),rotRight), (_('Rotate &Down'),rotDown), (_('Rotate &Up'),rotUp), (_('Rotate &ClockWise'),twistRight), (_('Rotate &CCW'),twistLeft), ]), (_('&Lock'),lockCamera), (_('&Unlock'),unlockCamera), ('---',None), (_('&Report'),reportCamera), (_('&Settings'),showCameraTool), ] # End pyformex-0.8.6/pyformex/gui/fileMenu.py0000644000211500021150000003606011705104656020017 0ustar benebene00000000000000# $Id: fileMenu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Functions from the File menu.""" import os,shutil import pyformex as pf import widgets import utils import project import draw from script import processArgs,play import image import plugins from gettext import gettext as _ from prefMenu import updateSettings ##################### handle project files ########################## def openProject(fn=None,exist=False,access=['wr','rw','w','r'],default='wr'): """Open a (new or old) Project file. The user is asked for a Project file name and the access modalities. Depending on the parameters and the results of the dialog, either a new project is create or an old is opened, or nothing is done. In case a project is opened, it is returned, else the return value is None. The default will let the user create new project files as well as open existing ones. Use create=False or the convenience function openProject to only accept existing project files. If a compression level (1..9) is given, the contents will be compressed, resulting in much smaller project files at the cost of """ cur = fn if fn else '.' typ = utils.fileDescription(['pyf','all']) res = widgets.ProjectSelection(cur,typ,exist=exist,access=access,default=default,convert=True).getResult() if not res: return fn = res.fn if not fn.endswith('.pyf'): fn += '.pyf' access = res.acc compression = res.cpr convert = res.cvt signature = pf.Version[:pf.Version.rfind('-')] # OK, we have all data, now create/open the project pf.message("Opening project %s" % fn) pf.GUI.setBusy() # loading may take a while try: proj = project.Project(fn,access=access,convert=convert,signature=signature,compression=compression) except: proj = None raise finally: pf.GUI.setBusy(False) return proj def setProject(proj): """Open a (new or old) Project file and make it the current project. The user is asked for a Project file name and the access modalities. Depending on the results of the dialog: - either an new project is create or an old is opened, - the old data may be discarded, added to the current exported symbols, or replace them - the opened Project may become the current Project, or its data are just imported in the current Project. The default will let the user create new project files as well as open existing ones. Use create=False or the convenience function openProject to only accept existing project files. If a compression level (1..9) is given, the contents will be compressed, resulting in much smaller project files at the cost of Only one pyFormex project can be open at any time. The open project owns all the global data created and exported by any script. If makeDefault is True, an already open project will be closed and the opened project becomes the current project. If makeDefault is False, the project data are imported into pf.PF and the current project does not change. This means that if a project was open, the imported data will be added to it. If addGlobals is None, the user is asked whether the current globals should be added to the project. Set True or False to force or reject the adding without asking. """ pf.message("Setting current project to %s" % proj.filename) pf.message("Project contents: %s" % proj.keys()) pf.PF = proj if pf.PF.filename: updateSettings({'workdir':os.path.dirname(pf.PF.filename)},save=True) pf.GUI.setcurproj(pf.PF.filename) if hasattr(proj,'_autoscript_'): _ignore = "Ignore it!" _show = "Show it" _edit = "Load it in the editor" _exec = "Execute it" res = draw.ask("There is an autoscript stored inside the project.\nIf you received this project file from an untrusted source, you should probably not execute it.",[_ignore,_show,_edit,_exec]) if res == _show: res = draw.showText(proj._autoscript_)#,actions=[_ignore,_edit,_show]) return if res == _exec: draw.playScript(proj._autoscript_) elif res == _edit: fn = "_autoscript_.py" draw.checkWorkdir() f = open(fn,'w') f.write(proj._autoscript_) f.close() openScript(fn) editScript(fn) if hasattr(proj,'autofile') and draw.ack("The project has an autofile attribute: %s\nShall I execute this script?" % proj.autofile): processArgs([proj.autofile]) pf.message("Exported symbols: %s" % pf.PF.keys()) def createProject(): """Open an new project. Ask the user to select an existing project file, and then open it. """ closeProject() proj = openProject(pf.PF.filename,exist=False) if proj is not None: # may be empty setProject(proj) def openExistingProject(): """Open an existing project. Ask the user to select an existing project file, and then open it. """ closeProject() proj = openProject(pf.PF.filename,exist=True) if proj is not None: # may be empty setProject(proj) def importProject(): """Import an existing project. Ask the user to select an existing project file, and then import its data into the current project. """ proj = openProject(exist=True,access='r') if proj: # only if non-empty pf.PF.update(proj) def setAutoScript(): """Set the current script as autoScript in the project""" if pf.cfg['curfile'] and pf.GUI.canPlay: pf.PF._autoscript_ = open(pf.cfg['curfile']).read() def setAutoFile(): """Set the current script as autoScriptFile in the project""" if pf.cfg['curfile'] and pf.GUI.canPlay: pf.PF.autofile = pf.cfg['curfile'] def removeAutoScript(): delattr(pf.PF,'_autoscript_') def removeAutoFile(): delattr(pf.PF,'autofile') def saveProject(): """Save the current project. If the current project is a named one, its contents are written to file. This function does nothing if the current project is a temporary one. """ if pf.PF.filename is not None: pf.message("Saving Project contents: %s" % pf.PF.keys()) pf.GUI.setBusy() pf.PF.save() pf.GUI.setBusy(False) def saveAsProject(): proj = openProject(pf.PF.filename,exist=False,access=['w'],default='w') if proj is not None: # even if empty pf.PF.filename = proj.filename pf.PF.gzip = proj.gzip saveProject() def listProject(): """Print all global variable names.""" pf.message("Exported symbols: %s" % pf.PF.keys()) def clearProject(): """Clear the contents of the current project.""" pf.PF.clear() def closeProject(save=None): """Close the current project, saving it or not. Parameters: - `save`: None, True or False. Determines whether the project should be saved prior to closing it. If None, it will be asked from the user. Note that this parameter is only used for named Projects. Temporary Projects are never saved implicitely. """ if pf.PF.filename is not None: if save is None: save = draw.ack("Save the current project?") pf.message("Closing project %s (save=%s)" % (pf.PF.filename,save)) if save: saveProject() if pf.PF: pf.message("Exported symbols: %s" % pf.PF.keys()) if draw.ask("What shall I do with the exported symbols?",["Delete","Keep"]) == "Delete": pf.PF.clear() pf.PF.filename = None pf.GUI.setcurproj('None') def closeProjectWithoutSaving(): """Close the current project without saving it.""" closeProject(False) def askCloseProject(): if pf.PF and pf.PF.filename is not None: choices = ['Exit without saving','SaveAs and Exit','Save and Exit'] if pf.PF.access == 'r': choices = choices[:2] res = draw.ask("You have an unsaved open project: %s\nWhat do you want me to do?"%pf.PF.filename,choices,default=2) res = choices.index(res) if res == 1: saveAsProject() elif res == 2: saveProject() def convertProjectFile(): proj = openProject(pf.PF.filename,access=['c'],default='c',exist=True) print "got proj" if proj is not None: print "will now convert" proj.convert(proj.filename.replace('.pyf','_converted.pyf')) def uncompressProjectFile(): proj = openProject(pf.PF.filename,access=['u'],default='u',exist=True) if proj is not None: proj.uncompress(proj.filename.replace('.pyf','_uncompressed.pyf')) ##################### handle script files ########################## def openScript(fn=None,exist=True,create=False): """Open a pyFormex script and set it as the current script. If no filename is specified, a file selection dialog is started to select an existing script, or allow to create a new file if exist is False. If the file exists and is a pyFormex script, it is set ready to execute. If create is True, a default pyFormex script template will be written to the file, overwriting the contents if the file existed. Then, the script is loaded into the editor. We encourage the use of createScript() to create new scripts and leave openScript() to open existing scripts. """ if fn is None: cur = pf.cfg['curfile'] if cur is None: cur = pf.cfg['workdir'] if cur is None: cur = '.' typ = utils.fileDescription('pyformex') fn = widgets.FileSelection(cur,typ,exist=exist).getFilename() if fn: if create: if not exist and os.path.exists(fn) and not draw.ack("The file %s already exists.\n Are you sure you want to overwrite it?" % fn): return None template = pf.cfg['scripttemplate'] if (os.path.exists(template)): shutil.copyfile(template,fn) updateSettings({'workdir':os.path.dirname(fn)},save=True) pf.GUI.setcurfile(fn) pf.GUI.history.add(fn) if create: editScript(fn) return fn def createScript(fn=None): return openScript(fn,exist=False,create=True) def editScript(fn=None): """Load the current file in the editor. This only works if the editor was set in the configuration. The author uses 'emacsclient' to load the files in a running copy of Emacs. If a filename is specified, that file is loaded instead. """ if pf.cfg['editor']: if fn is None: fn = pf.cfg['curfile'] pid = utils.spawn('%s %s' % (pf.cfg['editor'],fn)) else: draw.warning('No known editor was found or configured') ##################### other functions ########################## def saveImage(multi=False): """Save an image to file. This will show the Save Image dialog, with the multisave mode checked if multi = True. Then, depending on the user's selection, it will either: - save the current Canvas/Window to file - start the multisave/autosave mode - do nothing """ pat = map(utils.fileDescription, ['img','icon','all']) dia = widgets.SaveImageDialog(pf.cfg['workdir'],pat,multi=multi) opt = dia.getResult() if opt: if opt.fm == 'From Extension': opt.fm = None if opt.qu < 0: opt.qu = -1 updateSettings({'workdir':os.path.dirname(opt.fn)},save=True) image.save(filename=opt.fn, format=opt.fm, quality=opt.qu, size=opt.sz, window=opt.wi, multi=opt.mu, hotkey=opt.hk, autosave=opt.au, border=opt.bo, rootcrop=opt.rc ) def saveIcon(): """Save an image as icon. This will show the Save Image dialog, with the multisave mode off and asking for an icon file name. Then save the current rendering to that file. """ ## We should create a specialized input dialog, asking also for the size fn = draw.askNewFilename(filter=utils.fileDescription('icon')) if fn: image.saveIcon(fn,size=32) def startMultiSave(): """Start/change multisave mode.""" saveImage(True) def stopMultiSave(): """Stop multisave mode.""" image.save() from imageViewer import ImageViewer viewer = None def showImage(): """Display an image file.""" global viewer fn = draw.askFilename(filter=utils.fileDescription('img')) if fn: viewer = ImageViewer(pf.app,fn) viewer.show() MenuData = [ ## (_('&Open project'),openProject), (_('&Start new project'),createProject), (_('&Open existing project'),openExistingProject), (_('&Import a project'),importProject), (_('&Set current script as AutoScript'),setAutoScript), (_('&Remove the AutoScript'),removeAutoScript), (_('&Set current script as AutoFile'),setAutoFile), (_('&Remove the AutoFile'),removeAutoFile), (_('&List project contents'),draw.listAll), (_('&Save project'),saveProject), (_('&Save project As'),saveAsProject), ## (_('&Close project without saving'),closeProjectWithoutSaving), (_('&Clear project'),clearProject), (_('&Close project'),closeProject), ('---',None), (_('&Convert Project File'),convertProjectFile), (_('&Uncompress Project File'),uncompressProjectFile), ('---',None), (_('&Create new script'),createScript), (_('&Open existing script'),openScript), (_('&Play script'),play), (_('&Edit script'),editScript), (_('&Change workdir'),draw.askDirname), (_('---1'),None), (_('&Save Image'),saveImage), (_('Start &MultiSave'),startMultiSave), (_('Save &Next Image'),image.saveNext), (_('Create &Movie'),image.createMovie), (_('&Stop MultiSave'),stopMultiSave), (_('&Save as Icon'),saveIcon), (_('&Show Image'),showImage), (_('---2'),None), (_('E&xit'),draw.closeGui), ] #onExit(closeProject) # End pyformex-0.8.6/pyformex/gui/image.py0000644000211500021150000004074611705104656017343 0ustar benebene00000000000000#!/usr/bin/env python # $Id: image.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Saving OpenGL renderings to image files. This module defines some functions that can be used to save the OpenGL rendering and the pyFormex GUI to image files. There are even provisions for automatic saving to a series of files and creating a movie from these images. """ import pyformex as pf from OpenGL import GL from PyQt4 import QtCore,QtGui,QtOpenGL import utils import os # The image formats recognized by pyFormex image_formats_qt = [] image_formats_qtr = [] image_formats_gl2ps = [] image_formats_fromeps = [] def imageFormats(type='w'): if type == 'r': return image_formats_qtr else: return image_formats_qt + image_formats_gl2ps + image_formats_fromeps # global parameters for multisave mode multisave = None # imported module gl2ps = None def initialize(): """Initialize the image module.""" global image_formats_qt,image_formats_qtr,image_formats_gl2ps,image_formats_fromeps,gl2ps,_producer,_gl2ps_types # Find interesting supporting software utils.hasExternal('ImageMagick') # Set some globals pf.debug("LOADING IMAGE FORMATS") image_formats_qt = map(str,QtGui.QImageWriter.supportedImageFormats()) image_formats_qtr = map(str,QtGui.QImageReader.supportedImageFormats()) ## if pf.cfg.get('imagesfromeps',False): ## pf.image_formats_qt = [] if utils.hasModule('gl2ps'): import gl2ps _producer = pf.Version + ' (%s)' % pf.cfg.get('help/website','') _gl2ps_types = { 'ps':gl2ps.GL2PS_PS, 'eps':gl2ps.GL2PS_EPS, 'tex':gl2ps.GL2PS_TEX, 'pdf':gl2ps.GL2PS_PDF, } if utils.checkVersion('gl2ps','1.03') >= 0: _gl2ps_types.update({ 'svg':gl2ps.GL2PS_SVG, 'pgf':gl2ps.GL2PS_PGF, }) image_formats_gl2ps = _gl2ps_types.keys() image_formats_fromeps = [ 'ppm', 'png', 'jpeg', 'rast', 'tiff', 'xwd', 'y4m' ] pf.debug(""" Qt image types for saving: %s Qt image types for input: %s gl2ps image types: %s image types converted from EPS: %s""" % (image_formats_qt,image_formats_qtr,image_formats_gl2ps,image_formats_fromeps)) def imageFormats(): """Return a list of the valid image formats. image formats are lower case strings as 'png', 'gif', 'ppm', 'eps', etc. The available image formats are derived from the installed software. """ return image_formats_qt + \ image_formats_gl2ps + \ image_formats_fromeps def checkImageFormat(fmt,verbose=False): """Checks image format; if verbose, warn if it is not. Returns the image format, or None if it is not OK. """ pf.debug("Format requested: %s" % fmt) pf.debug("Formats available: %s" % imageFormats()) if fmt in imageFormats(): if fmt == 'tex' and verbose: pf.warning("This will only write a LaTeX fragment to include the 'eps' image\nYou have to create the .eps image file separately.\n") return fmt else: if verbose: error("Sorry, can not save in %s format!\n" "I suggest you use 'png' format ;)"%fmt) return None def imageFormatFromExt(ext): """Determine the image format from an extension. The extension may or may not have an initial dot and may be in upper or lower case. The format is equal to the extension characters in lower case. If the supplied extension is empty, the default format 'png' is returned. """ if len(ext) > 0: if ext[0] == '.': ext = ext[1:] fmt = ext.lower() else: fmt = 'png' return fmt ##### LOW LEVEL FUNCTIONS ########## def save_canvas(canvas,fn,fmt='png',quality=-1,size=None): """Save the rendering on canvas as an image file. canvas specifies the qtcanvas rendering window. fn is the name of the file fmt is the image file format """ # make sure we have the current content displayed (on top) canvas.makeCurrent() canvas.raise_() canvas.display() pf.app.processEvents() if fmt in image_formats_qt: pf.debug("Image format can be saved by Qt") wc,hc = canvas.getSize() try: w,h = size except: w,h = wc,hc if (w,h) == (wc,hc): # Save directly from current rendering pf.debug("Saving image from canvas with size %sx%s" % (w,h)) GL.glFlush() qim = canvas.grabFrameBuffer() else: pf.debug("Saving image from virtual buffer with size %sx%s" % (w,h)) vcanvas = QtOpenGL.QGLFramebufferObject(w,h) vcanvas.bind() canvas.resize(w,h) canvas.display() GL.glFlush() qim = vcanvas.toImage() vcanvas.release() canvas.resize(wc,hc) del vcanvas print "SAVING %s in format %s with quality %s" % (fn,fmt,quality) if qim.save(fn,fmt,quality): sta = 0 else: sta = 1 elif fmt in image_formats_gl2ps: pf.debug("Image format can be saved by gl2ps") sta = save_PS(canvas,fn,fmt) elif fmt in image_formats_fromeps: pf.debug("Image format can be converted from eps") fneps = os.path.splitext(fn)[0] + '.eps' delete = not os.path.exists(fneps) save_PS(canvas,fneps,'eps') if os.path.exists(fneps): cmd = 'pstopnm -portrait -stdout %s' % fneps if fmt != 'ppm': cmd += '| pnmto%s > %s' % (fmt,fn) utils.runCommand(cmd) if delete: os.remove(fneps) return sta initialize() if gl2ps: def save_PS(canvas,filename,filetype=None,title='',producer='',viewport=None): """ Export OpenGL rendering to PostScript/PDF/TeX format. Exporting OpenGL renderings to PostScript is based on the PS2GL library by Christophe Geuzaine (http://geuz.org/gl2ps/), linked to Python by Toby Whites's wrapper (http://www.esc.cam.ac.uk/~twhi03/software/python-gl2ps-1.1.2.tar.gz) This function is only defined if the gl2ps module is found. The filetype should be one of 'ps', 'eps', 'pdf' or 'tex'. If not specified, the type is derived from the file extension. In case of the 'tex' filetype, two files are written: one with the .tex extension, and one with .eps extension. """ fp = open(filename, "wb") if filetype: filetype = _gl2ps_types[filetype] else: s = filename.lower() for ext in _gl2ps_types.keys(): if s.endswith('.'+ext): filetype = _gl2ps_types[ext] break if not filetype: filetype = gl2ps.GL2PS_EPS if not title: title = filename if not viewport: viewport = GL.glGetIntegerv(GL.GL_VIEWPORT) bufsize = 0 state = gl2ps.GL2PS_OVERFLOW opts = gl2ps.GL2PS_SILENT | gl2ps.GL2PS_SIMPLE_LINE_OFFSET | gl2ps.GL2PS_USE_CURRENT_VIEWPORT ##| gl2ps.GL2PS_NO_BLENDING | gl2ps.GL2PS_OCCLUSION_CULL | gl2ps.GL2PS_BEST_ROOT ##color = GL[[0.,0.,0.,0.]] #print("VIEWPORT %s" % str(viewport)) #print(fp) viewport=None while state == gl2ps.GL2PS_OVERFLOW: bufsize += 1024*1024 gl2ps.gl2psBeginPage(title, _producer, viewport, filetype, gl2ps.GL2PS_BSP_SORT, opts, GL.GL_RGBA, 0, None, 0, 0, 0, bufsize, fp, '') canvas.display() GL.glFinish() state = gl2ps.gl2psEndPage() fp.close() return 0 def save_window(filename,format,quality=-1,windowname=None): """Save a window as an image file. This function needs a filename AND format. If a window is specified, the named window is saved. Else, the main pyFormex window is saved. """ if windowname is None: windowname = pf.GUI.windowTitle() pf.GUI.raise_() pf.GUI.repaint() pf.GUI.toolbar.repaint() pf.GUI.update() pf.canvas.makeCurrent() pf.canvas.raise_() pf.canvas.update() pf.app.processEvents() cmd = 'import -window "%s" %s:%s' % (windowname,format,filename) sta,out = utils.runCommand(cmd) return sta def save_main_window(filename,format,quality=-1,border=False): """Save the main pyFormex window as an image file. This function needs a filename AND format. This is an alternative for save_window, by grabbin it from the root window, using save_rect. This allows us to grab the border as well. """ pf.GUI.repaint() pf.GUI.toolbar.repaint() pf.GUI.update() pf.canvas.update() pf.app.processEvents() if border: geom = pf.GUI.frameGeometry() else: geom = pf.GUI.geometry() x,y,w,h = geom.getRect() return save_rect(x,y,w,h,filename,format,quality) def save_rect(x,y,w,h,filename,format,quality=-1): """Save a rectangular part of the screen to a an image file.""" cmd = 'import -window root -crop "%sx%s+%s+%s" %s:%s' % (w,h,x,y,format,filename) sta,out = utils.runCommand(cmd) return sta #### USER FUNCTIONS ################ def save(filename=None,window=False,multi=False,hotkey=True,autosave=False,border=False,rootcrop=False,format=None,quality=-1,size=None,verbose=False): """Saves an image to file or Starts/stops multisave mode. With a filename and multi==False (default), the current viewport rendering is saved to the named file. With a filename and multi==True, multisave mode is started. Without a filename, multisave mode is turned off. Two subsequent calls starting multisave mode without an intermediate call to turn it off, do not cause an error. The first multisave mode will implicitely be ended before starting the second. In multisave mode, each call to saveNext() will save an image to the next generated file name. Filenames are generated by incrementing a numeric part of the name. If the supplied filename (after removing the extension) has a trailing numeric part, subsequent images will be numbered continuing from this number. Otherwise a numeric part '-000' will be added to the filename. If window is True, the full pyFormex window is saved. If window and border are True, the window decorations will be included. If window is False, only the current canvas viewport is saved. If hotkey is True, a new image will be saved by hitting the 'S' key. If autosave is True, a new image will be saved on each execution of the 'draw' function. If neither hotkey nor autosave are True, images can only be saved by executing the saveNext() function from a script. If no format is specified, it is derived from the filename extension. fmt should be one of the valid formats as returned by imageFormats() If verbose=True, error/warnings are activated. This is usually done when this function is called from the GUI. """ #print "SAVE: quality=%s" % quality global multisave # Leave multisave mode if no filename or starting new multisave mode if multisave and (filename is None or multi): pf.message("Leave multisave mode") QtCore.QObject.disconnect(pf.GUI,QtCore.SIGNAL("Save"),saveNext) multisave = None if filename is None: return #chdir(filename) name,ext = os.path.splitext(filename) # Get/Check format if not format: # is None: format = checkImageFormat(imageFormatFromExt(ext)) if not format: return if multi: # Start multisave mode names = utils.NameSequence(name,ext) if os.path.exists(names.peek()): next = names.next() pf.message("Start multisave mode to files: %s (%s)" % (names.name,format)) #print(hotkey) if hotkey: QtCore.QObject.connect(pf.GUI,QtCore.SIGNAL("Save"),saveNext) if verbose: pf.warning("Each time you hit the '%s' key,\nthe image will be saved to the next number." % pf.cfg['keys/save']) multisave = (names,format,quality,size,window,border,hotkey,autosave,rootcrop) print("MULTISAVE %s "% str(multisave)) return multisave is None else: # Save the image if window: if rootcrop: sta = save_main_window(filename,format,quality,border=border) else: sta = save_window(filename,format,quality) else: if size == pf.canvas.getSize(): size = None sta = save_canvas(pf.canvas,filename,format,quality,size) if sta: pf.debug("Error while saving image %s" % filename) else: pf.message("Image file %s written" % filename) return # Keep the old name for compatibility saveImage = save def saveNext(): """In multisave mode, saves the next image. This is a quiet function that does nothing if multisave was not activated. It can thus safely be called on regular places in scripts where one would like to have a saved image and then either activate the multisave mode or not. """ if multisave: names,format,quality,size,window,border,hotkey,autosave,rootcrop = multisave name = names.next() save(name,window,False,hotkey,autosave,border,rootcrop,format,quality,size,False) def saveIcon(fn,size=32): """Save the current rendering as an icon.""" savew,saveh = pf.canvas.width(),pf.canvas.height() pf.canvas.resize(size,size) if not fn.endswith('.xpm'): fn += '.xpm' save(fn) pf.canvas.resize(savew,saveh) def autoSaveOn(): """Returns True if autosave multisave mode is currently on. Use this function instead of directly accessing the autosave variable. """ return multisave and multisave[-2] def createMovie(encoder='ffmpeg'): """Create a movie from a saved sequence of images. encoder is one of: 'ffmpeg, mencoder, convert' """ if not multisave: pf.warning('You need to start multisave mode first!') return names,format,quality,window,border,hotkey,autosave,rootcrop = multisave glob = names.glob() ## if glob.split('.')[-1] != 'jpg': ## pf.warning("Currently you need to save in 'jpg' format to create movies") ## return if encoder == 'convert': cmd = "convert -delay 1 -colors 256 %s output.gif" % names.glob() elif encoder == 'mencoder': cmd = "mencoder -ovc lavc -fps 5 -o output.avi %s" % names.glob() elif encoder == 'mencoder1': cmd = "mencoder \"mf://%s\" -mf fps=10 -o output1.avi -ovc lavc -lavcopts vcodec=msmpeg4v2:vbitrate=800" % names.glob() else: cmd = "ffmpeg -qscale 1 -r 1 -i %s output.mp4" % names.glob() pf.debug(cmd) utils.runCommand(cmd) def saveMovie(filename,format,windowname=None): """Create a movie from the pyFormex window.""" if windowname is None: windowname = pf.GUI.windowTitle() pf.GUI.raise_() pf.GUI.repaint() pf.GUI.toolbar.repaint() pf.GUI.update() pf.canvas.makeCurrent() pf.canvas.raise_() pf.canvas.update() pf.app.processEvents() windowid = windowname cmd = "xvidcap --fps 5 --window %s --file %s" % (windowid,filename) pf.debug(cmd) #sta,out = utils.runCommand(cmd) return sta initialize() ### End pyformex-0.8.6/pyformex/gui/drawlock.py0000644000211500021150000001111511705104656020053 0ustar benebene00000000000000## $Id: drawlock.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A locking mechanism for the drawing functions.""" import pyformex as pf import threading from time import sleep # WE SHOULD REIMPLEMENT THIS USING PYTHON threading.Condition? class DrawLock(object): """A timed lock to slow down drawing processes""" def __init__(self): self.allowed = True self.locked = False self.timer = None def wait(self): """Wait for the drawing lock to be released. This method can be called to wait until the lock is released, while still processing GUI events. """ if self.allowed: while self.locked: pf.canvas.update() pf.app.processEvents() sleep(0.01) # to avoid overusing the cpu def lock(self,time=None): """Lock the drawing function for the next time seconds. If a no time is specified, a global value is used. """ #print self.allowed,self.locked if self.allowed and not self.locked: if time is None: time = pf.GUI.drawwait if time > 0: pf.debug('STARTING TIMER') self.locked = True self.timer = threading.Timer(time,self.release) self.timer.start() # ?? SHOULD block and release only be activated if self.allowed ?? def block(self): """Lock the drawing function indefinitely.""" if self.timer: self.timer.cancel() self.locked = True def release(self): """Release the lock on the drawing function. If a timer is running, cancel it. """ #print "RELEASING LOCK" self.locked = False if self.timer: self.timer.cancel() def free(self): """Release the lock and prevent waits until allow() is called.""" #print "FREEING THE DRAW LOCK" self.allowed = False self.release() def allow(self): """Allow draw waits. This is called after a free() to reinstall the draw locking. """ self.allowed = True def repeat(func,duration=-1,maxcount=-1,*args,**kargs): """Repeatedly execute a function. func(*args,**kargs) is repeatedly executed until one of the following conditions is met: - the function returns a value that evaluates to False - duration >= 0 and duration seconds have elapsed - maxcount >=0 and maxcount executions have been reached The default will indefinitely execute the function until it returns False. Between each execution of the function, application events are processed. """ pf.debug("REPEAT: %s, %s" % (duration,maxcount)) global _repeat_timed_out _repeat_timed_out = False _repeat_count_reached = False def timeOut(): global _repeat_timed_out #print "REPEAT TIMED OUT" _repeat_timed_out = True if duration >= 0: timer = threading.Timer(duration,timeOut) timer.start() count = 0 while True: pf.app.processEvents() res = func(*args,**kargs) _exit_requested = not(res) count += 1 if maxcount >= 0: _repeat_count_reached = count >= maxcount if _exit_requested or _repeat_timed_out or _repeat_count_reached: pf.debug("Count: %s, TimeOut: %s" % (count,_repeat_timed_out)) break pf.debug("BREAK FROM REPEAT") pf.GUI.drawlock.release() #### End pyformex-0.8.6/pyformex/gui/imageViewer.py0000755000211500021150000002471511705104656020526 0ustar benebene00000000000000#!/usr/bin/env python # $Id: imageViewer.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A general image viewer Part of this code was borrowed from the TrollTech Qt examples. """ caption = "pyFormex Image Viewer" from PyQt4 import QtCore, QtGui def tr(s): return QtCore.QCoreApplication.translate('ImageViewer',s) class ImageViewer(QtGui.QMainWindow): def __init__(self,parent=None,path=None): QtGui.QMainWindow.__init__(self) self.parent = parent self.filename = path self.image = QtGui.QLabel() self.image.setBackgroundRole(QtGui.QPalette.Base) self.image.setSizePolicy(QtGui.QSizePolicy.Ignored,QtGui.QSizePolicy.Ignored) #self.image.setScaledContents(True) self.scroll = QtGui.QScrollArea() self.scroll.setBackgroundRole(QtGui.QPalette.Dark) self.scroll.setWidget(self.image) self.scroll.setWidgetResizable(True) self.setCentralWidget(self.scroll) self.createActions() self.createMenus() self.setWindowTitle(tr(caption)) self.resize(500,400) self.setSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Minimum) if path: self.openfile(path) def openfile(self,filename=None): if filename is None: filename = self.filename if filename is None: filename = QtCore.QDir.currentPath() filename = QtGui.QFileDialog.getOpenFileName(self,tr("Open File"),filename) if filename.isEmpty(): return image = QtGui.QImage(filename) if image.isNull(): QtGui.QMessageBox.information(self,tr(caption),tr("Cannot load %1.").arg(filename)) return #print("Size %sx%s" % (image.width(),image.height())) self.filename = str(filename) self.image.setPixmap(QtGui.QPixmap.fromImage(image)) self.scaleFactor = 1.0 self.printAct.setEnabled(True) self.fitToWindowAct.setEnabled(True) self.updateActions() ## if not self.fitToWindowAct.isChecked(): self.image.adjustSize() self.normalSize() def print_(self): if not self.image.pixmap(): return self.printer = QtGui.QPrinter() dialog = QtGui.QPrintDialog(self.printer,self) if dialog.exec_(): painter = QtGui.QPainter(self.printer) rect = painter.viewport() size = self.image.pixmap().size() size.scale(rect.size(),QtCore.Qt.KeepAspectRatio) painter.setViewport(rect.x(),rect.y(),size.width(),size.height()) painter.setWindow(self.image.pixmap().rect()) painter.drawPixmap(0,0,self.image.pixmap()) def zoomIn(self): self.scaleImage(1.25) def zoomOut(self): self.scaleImage(0.8) def normalSize(self): self.image.resize(self.image.pixmap().size()) #self.image.adjustSize() self.scaleFactor = 1.0 def fitToWindow(self): fitToWindow = self.fitToWindowAct.isChecked() self.scroll.setWidgetResizable(fitToWindow) if not fitToWindow: self.normalSize() self.updateActions() def fitToImage(self): self.normalSize() self.scroll.resize(self.image.size()) def about(self): QtGui.QMessageBox.about(self,tr("About pyFormex Image Viewer"),tr("""

The pyFormex Image Viewer was shaped after the Image Viewer from the TrollTech Qt documentation.

The example shows how to combine QLabel and QScrollArea to display an image. QLabel is typically used for displaying a text,but it can also display an image. QScrollArea provides a scrolling view around another widget. If the child widget exceeds the size of the frame,QScrollArea automatically provides scroll bars.

The example demonstrates how QLabel's ability to scale its contents (QLabel.scaledContents),and QScrollArea's ability to automatically resize its contents (QScrollArea.widgetResizable),can be used to implement zooming and scaling features.

In addition the example shows how to use QPainter to print an image.

""")) def createActions(self): self.openAct = QtGui.QAction(tr("&Open..."),self) self.openAct.setShortcut(tr("Ctrl+O")) self.connect(self.openAct,QtCore.SIGNAL('triggered()'),self.openfile) self.printAct = QtGui.QAction(tr("&Print..."),self) self.printAct.setShortcut(tr("Ctrl+P")) self.printAct.setEnabled(False) self.connect(self.printAct,QtCore.SIGNAL('triggered()'),self.print_) self.zoomInAct = QtGui.QAction(tr("Zoom &In (25%)"),self) self.zoomInAct.setShortcut(tr("Ctrl++")) self.zoomInAct.setEnabled(False) self.connect(self.zoomInAct,QtCore.SIGNAL('triggered()'),self.zoomIn) self.zoomOutAct = QtGui.QAction(tr("Zoom &Out (25%)"),self) self.zoomOutAct.setShortcut(tr("Ctrl+-")) self.zoomOutAct.setEnabled(False) self.connect(self.zoomOutAct,QtCore.SIGNAL('triggered()'),self.zoomOut) self.normalSizeAct = QtGui.QAction(tr("&Normal Size"),self) self.normalSizeAct.setShortcut(tr("Ctrl+S")) self.normalSizeAct.setEnabled(False) self.connect(self.normalSizeAct,QtCore.SIGNAL('triggered()'),self.normalSize) self.fitToImageAct = QtGui.QAction(tr("Fit &Window to Image"),self) self.fitToImageAct.setShortcut(tr("Ctrl+W")) self.fitToImageAct.setEnabled(False) #self.fitToImageAct.setCheckable(True) self.connect(self.fitToImageAct,QtCore.SIGNAL('triggered()'),self.fitToImage) self.fitToWindowAct = QtGui.QAction(tr("&Fit Image to Window"),self) self.fitToWindowAct.setShortcut(tr("Ctrl+F")) self.fitToWindowAct.setEnabled(False) self.fitToWindowAct.setCheckable(True) self.connect(self.fitToWindowAct,QtCore.SIGNAL('triggered()'),self.fitToWindow) self.aboutAct = QtGui.QAction(tr("&About"),self) self.connect(self.aboutAct,QtCore.SIGNAL('triggered()'),self.about) if isinstance(self.parent,QtGui.QApplication): self.exitAct = QtGui.QAction(tr("E&xit"),self) self.exitAct.setShortcut(tr("Ctrl+Q")) self.connect(self.exitAct,QtCore.SIGNAL('triggered()'),self.close) self.aboutQtAct = QtGui.QAction(tr("About &Qt"),self) self.connect(self.aboutQtAct,QtCore.SIGNAL('triggered()'),self.parent,QtCore.SLOT('aboutQt()')) elif isinstance(self.parent,QtGui.QDialog): self.acceptAct = QtGui.QAction(tr("&Accept"),self) self.acceptAct.setShortcut(tr("Ctrl+A")) self.connect(self.acceptAct,QtCore.SIGNAL('triggered()'),self.parent,QtCore.SLOT('accept()')) self.rejectAct = QtGui.QAction(tr("&Reject"),self) self.rejectAct.setShortcut(tr("Ctrl+Q")) self.connect(self.rejectAct,QtCore.SIGNAL('triggered()'),self.parent,QtCore.SLOT('reject()')) def createMenus(self): self.fileMenu = QtGui.QMenu(tr("&File"),self) self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.printAct) self.fileMenu.addSeparator() self.viewMenu = QtGui.QMenu(tr("&View"),self) self.viewMenu.addAction(self.zoomInAct) self.viewMenu.addAction(self.zoomOutAct) self.viewMenu.addAction(self.normalSizeAct) self.viewMenu.addSeparator() self.viewMenu.addAction(self.fitToImageAct) self.viewMenu.addAction(self.fitToWindowAct) self.helpMenu = QtGui.QMenu(tr("&Help"),self) self.helpMenu.addAction(self.aboutAct) self.menuBar().addMenu(self.fileMenu) self.menuBar().addMenu(self.viewMenu) self.menuBar().addMenu(self.helpMenu) if isinstance(self.parent,QtGui.QApplication): self.fileMenu.addAction(self.exitAct) self.helpMenu.addAction(self.aboutQtAct) elif isinstance(self.parent,QtGui.QDialog): self.fileMenu.addAction(self.acceptAct) self.fileMenu.addAction(self.rejectAct) def updateActions(self): self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked()) self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked()) self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked()) self.fitToImageAct.setEnabled(not self.fitToWindowAct.isChecked()) def scaleImage(self,factor): if not self.image.pixmap(): return self.scaleFactor *= factor self.image.resize(self.scaleFactor * self.image.pixmap().size()) self.adjustScrollBar(self.scroll.horizontalScrollBar(),factor) self.adjustScrollBar(self.scroll.verticalScrollBar(),factor) self.zoomInAct.setEnabled(self.scaleFactor < 3.0) self.zoomOutAct.setEnabled(self.scaleFactor > 0.333) def adjustScrollBar(self,scrollBar,factor): scrollBar.setValue(int(factor * scrollBar.value() + ((factor - 1) * scrollBar.pageStep()/2))) def main(): import sys global app app = QtGui.QApplication(sys.argv) if len(sys.argv) > 1: path = sys.argv[1] else: path = None viewer = ImageViewer(app,path) viewer.show() sys.exit(app.exec_()) if __name__ == '__main__': main() pyformex-0.8.6/pyformex/gui/helpMenu.py0000644000211500021150000002206211705104656020025 0ustar benebene00000000000000# -*- coding: utf-8 -*- # $Id: helpMenu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Display help""" import pyformex as pf import os,sys import draw import utils import tempfile import random import viewport from gettext import gettext as _ def help(page=None): """Display a html help page. If no page is specified, the help manual is displayed. If page is a string starting with 'http:', the page is displayed with the command set in pf.cfg['browser'], else with the command in pf.cfg['viewer'] """ if not page: page = pf.cfg['help/manual'] if page.startswith('http:'): browser = pf.cfg['browser'] else: browser = pf.cfg['viewer'] pid = utils.spawn(' '.join([browser,page])) def catchAndDisplay(expression): """Catch stdout from a Python expression and display it in a window.""" save = sys.stdout try: f = tempfile.TemporaryFile('w+') sys.stdout = f eval(expression) f.seek(0) draw.showText(f.read()) finally: sys.stdout = save ## def qappargs(): ## """Display informeation on the Qt application arguments.""" ## qtversion = utils.hasModule('pyqt4') ## qtversion = '.'.join(qtversion.split('.')[:2]) ## link = "http://doc.trolltech.com/%s/qapplication.html#QApplication" % qtversion ## help(link) def opengl(): """Display the OpenGL format description.""" draw.showText(viewport.OpenGLFormat()) def detected(): """Display the detected software components.""" utils.checkModule() utils.checkExternal() draw.showText(utils.reportDetected()) def about(): """Display short information about pyFormex.""" draw.showInfo(""".. %s %s A tool for generating, manipulating and transforming 3D geometrical models by sequences of mathematical operations. %s Distributed under the GNU GPL version 3 or later """ % (pf.Version,'='*len(pf.Version),pf.Copyright)) _developers = [ 'Matthieu De Beule', 'Gianluca De Santis', 'Bart Desloovere', 'Francesco Iannaccone', 'Peter Mortier', 'Tim Neels', 'Tomas Praet', 'Sofie Van Cauter', 'Benedict Verhegghe', ] def developers(): """Display the list of developers.""" random.shuffle(_developers) draw.showInfo(""" The following people have contributed to pyFormex. They are listed in random order. %s If you feel that your name was left out in error, please write to benedict.verhegghe@ugent.be. """ % '\n'.join(_developers)) _cookies = [ "Smoking may be hazardous to your health.", "Windows is a virus.", "Coincidence does not exist. Perfection does.", "It's all in the code.", "Python is the universal glue.", "Intellectual property is a mental illness.", "Programmers are tools for converting caffeine into code.", "There are 10 types of people in the world: those who understand binary, and those who don't.", "Linux: the choice of a GNU generation", "Everything should be made as simple as possible, but not simpler. (A. Einstein)", "Perfection [in design] is achieved, not when there is nothing more to add, but when there is nothing left to take away. (Antoine de Saint-Exupéry)", "Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to build bigger and better idiots. So far, the universe is winning. (Rick Cook)", "In theory, theory and practice are the same. In practice, they're not. (Yoggi Berra)", "Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program. (Linus Torvalds)", "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. (Martin Golding)", "If Microsoft had developed Internet, we could not ever see the source code of web pages. HTML might be a complied language then.", "What one programmer can do in one month, two programmers can do in two months.", "Windows 9x: n. 32 bit extensions and a graphical shell for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor, written by a 2 bit company that can't stand 1 bit of competition. (Cygwin FAQ)", "You know, when you have a program that does something really cool, and you wrote it from scratch, and it took a significant part of your life, you grow fond of it. When it's finished, it feels like some kind of amorphous sculpture that you've created. It has an abstract shape in your head that's completely independent of its actual purpose. Elegant, simple, beautiful.\nThen, only a year later, after making dozens of pragmatic alterations to suit the people who use it, not only has your Venus-de-Milo lost both arms, she also has a giraffe's head sticking out of her chest and a cherubic penis that squirts colored water into a plastic bucket. The romance has become so painful that each day you struggle with an overwhelming urge to smash the fucking thing to pieces with a hammer. (Nick Foster)", "One of my most productive days was throwing away 1000 lines of code. (Ken Thompson)", ] random.shuffle(_cookies) def roll(l): l.append(l.pop(0)) def cookie(): draw.showInfo(_cookies[0],["OK"]) roll(_cookies) def showURL(link): """Show a html document in the browser. `link` is an URL of a html document. If it does not start with `http://`, this will be prepended. The resulting URL is passed to the user's default or configured browser. """ if not link.startswith('http://'): link = 'http://'+link help(link) def showFileOrURL(link): """Show a html document or a text file. `link` is either a file name or an URL of a html document. If `link` starts with `http://`, it is interpreted as an URL and the corresponding document is shown in the user's browser. Else, `link` is interpreted as a filename and iof the file exists, it is shown in a pyFormex text window. """ if link.startswith('http://'): showURL(link) else: draw.showFile(link) def searchText(): from widgets import simpleInputItem as _I res = draw.askItems([_I('text','',text='String to grep')]) if res: text = res['text'] out = draw.grepSource(text) draw.showText(out,mono=True,modal=False) def createMenuData(): """Returns the help menu data""" DocsMenuData = [(k,help,{'data':v}) for k,v in pf.cfg['help/docs']] Docs2MenuData = [(k,draw.showFile,{'data':v}) for k,v in pf.cfg['help/docs2']] LinksMenuData = [(k,showURL,{'data':v}) for k,v in pf.cfg['help/links']] DevLinksMenuData = [(k,showFileOrURL,{'data':v}) for k,v in pf.cfg['help/developer']] try: MenuData = DocsMenuData + [ (_('&Search text in source'),searchText), (_('&About current script'),draw.showDescription), ('---',None), ] + Docs2MenuData + [ (_('&Detected Software'),detected), (_('&OpenGL Format'),opengl), (_('&Fortune Cookie'),cookie), (_('&Favourite Links'),LinksMenuData), (_('&Developers'),developers), (_('&About'),about), ('---',None), (_('&Developer Guidelines'),DevLinksMenuData), ] except: MenuData = [] if pf.svnversion: def install_dxfparser(): extdir = os.path.join(pf.cfg['pyformexdir'],'external','dxfparser') sta,out = utils.runCommand("cd %s; make && gksu make install" % extdir) if sta: info = out else: if utils.hasExternal('dxfparser',force=True): info = "Succesfully installed dxfparser" else: info ="You should now restart pyFormex!" draw.showInfo(info) return sta MenuData.append((_('&Install Externals'),[ (_('dxfparser'),install_dxfparser), ])) return MenuData # End pyformex-0.8.6/pyformex/gui/prefMenu.py0000644000211500021150000005237411705104656020042 0ustar benebene00000000000000# $Id: prefMenu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Functions for the Pref menu.""" import pyformex as pf from main import savePreferences import os from gettext import gettext as _ import utils import widgets from widgets import simpleInputItem as I, groupInputItem as G, tabInputItem as T import toolbar import draw def updateSettings(res,save=None): """Update the current settings (store) with the values in res. res is a dictionary with configuration values. The current settings will be updated with the values in res. If res contains a key '_save_', or a `save` argument is supplied, and its value is True, the preferences will also be saved to the user's preference file. Else, the user will be asked whether he wants to save the changes. """ pf.debug(res,"\nACCEPTED SETTINGS") if save is None: save = res.get('_save_',None) if save is None: save = draw.ack("Save the current changes to your configuration file?") # Do not use 'pf.cfg.update(res)' here! # It will not work with our Config class! # a set to uniquely register updatefunctions todo = set([]) for k in res: if k.startswith('_'): # skip temporary variables continue changed = False # first try to set in prefs, as these will cascade to cfg if save and pf.prefcfg[k] != res[k]: pf.prefcfg[k] = res[k] changed = True # if not saved, set in cfg print "Setting %s = %s" % (k,res[k]) #print pf.cfg.keys() if pf.cfg[k] != res[k]: pf.cfg[k] = res[k] changed = True if changed and pf.GUI: # register the corresponding update function if k in _activate_settings: todo.add(_activate_settings[k]) # We test for pf.GUI in case we want to call updateSettings before # the GUI is created if pf.GUI: #print todo for f in todo: f() pf.debug(pf.cfg,"\nNEW SETTINGS") pf.debug(pf.prefcfg,"\n\nNEW PREFERENCES") def settings(): import plugins import sendmail from elements import elementTypes dia = None def close(): dia.close() def accept(save=False): dia.acceptData() res = dia.results res['_save_'] = save pf.debug(res) ok_plugins = utils.subDict(res,'_plugins/') res['gui/plugins'] = [ p for p in ok_plugins if ok_plugins[p]] updateSettings(res) plugins.loadConfiguredPlugins() def acceptAndSave(): accept(save=True) def autoSettings(keylist): return [I(k,pf.cfg[k]) for k in keylist] def changeScriptDirs(): setScriptDirs() pf.debug("SCRIPTDIRS NOW " % pf.cfg['scriptdirs']) dia.updateData({'scriptdirs':pf.cfg['scriptdirs']}) mouse_settings = autoSettings(['gui/rotfactor','gui/panfactor','gui/zoomfactor','gui/autozoomfactor','gui/dynazoom','gui/wheelzoom']) plugin_items = [ I('_plugins/'+name,name in pf.cfg['gui/plugins'],text=text) for name,text in plugins.pluginMenus() ] #print plugin_items appearence = [ I('gui/style',pf.GUI.currentStyle(),choices=pf.GUI.getStyles()), I('gui/font',pf.app.font().toString(),'font'), ] toolbartip = "Currently, changing the toolbar position will only be in effect when you restart pyFormex" toolbars = [ I('gui/%s'%t,pf.cfg['gui/%s'%t],text=getattr(pf.GUI,t).windowTitle(),choices=['left','right','top','bottom'],tooltip=toolbartip) for t in [ 'camerabar','modebar','viewbar' ] ] cur = pf.cfg['gui/splash'] # if not cur: # cur = pf.cfg.get('icondir','.') viewer = widgets.ImageView(cur,maxheight=200) def changeSplash(fn): #print "CURRENT %s" % fn fn = draw.askImageFile(fn) if fn: viewer.showImage(fn) return fn mail_settings = [ I('mail/sender',pf.cfg.get('mail/sender',sendmail.mail),text="My mail address"), I('mail/server',pf.cfg.get('mail/server','localhost'),text="Outgoing mail server") ] dia = widgets.InputDialog( caption='pyFormex Settings', store=pf.cfg, items=[ T('General',[ I('syspath',tooltip="If you need to import modules from a non-standard path, you can supply additional paths to search here."), I('editor',tooltip="The command to be used to edit a script file. The command will be executed with the path to the script file as argument."), I('viewer',tooltip="The command to be used to view an HTML file. The command will be executed with the path to the HTML file as argument."), I('browser',tooltip="The command to be used to browse the internet. The command will be executed with an URL as argument."), I('help/docs'), I('autorun',text='Startup script',tooltip='This script will automatically be run at pyFormex startup'), I('scriptdirs',text='Script Paths',tooltip='pyFormex will look for scripts in these directories',buttons=[('Edit',changeScriptDirs)]), ], ), T('GUI',[ G('Appearence',appearence), G('Components',toolbars+[ I('gui/coordsbox',pf.cfg['gui/coordsbox']), I('gui/showfocus',pf.cfg['gui/showfocus']), I('gui/timeoutbutton',pf.cfg['gui/timeoutbutton']), I('gui/timeoutvalue',pf.cfg['gui/timeoutvalue']), ], ), I('gui/splash',text='Splash image',itemtype='button',func=changeSplash), viewer, ]), T('Canvas',[ I('canvas/bgmode',pf.cfg['canvas/bgmode'],choices=pf.canvas.bgmodes), I('canvas/bgcolor',pf.cfg['canvas/bgcolor'],itemtype='color'), I('canvas/bgcolor2',pf.cfg['canvas/bgcolor2'],itemtype='color'), ], ), T('Drawing',[ I('_info_00_',itemtype='info',text='Changes to these options currently only become effective after restarting pyFormex!'), I('draw/quadline',text='Draw as quadratic lines',itemtype='list',check=True,choices=elementTypes(1),tooltip='Line elements checked here will be drawn as quadratic lines whenever possible.'), I('draw/quadsurf',text='Draw as quadratic surfaces',itemtype='list',check=True,choices=elementTypes(2)+elementTypes(3),tooltip='Surface and volume elements checked here will be drawn as quadratic surfaces whenever possible.'), ] ), T('Mouse',mouse_settings), T('Plugins',plugin_items), T('Environment',[ G('Mail',mail_settings), # G('Jobs',jobs_settings), ]), ], enablers =[ ('canvas/bgmode','vertical gradient','canvas/bgcolor2'), ('canvas/bgmode','horizontal gradient','canvas/bgcolor2'), ], actions=[ ('Close',close), ('Accept and Save',acceptAndSave), ('Accept',accept), ], ) #dia.resize(800,400) dia.show() def askConfigPreferences(items,prefix=None,store=None): """Ask preferences stored in config variables. Items in list should only be keys. store is usually a dictionary, but can be any class that allow the setdefault method for lookup while setting the default, and the store[key]=val syntax for setting the value. If a prefix is given, actual keys will be 'prefix/key'. The current values are retrieved from the store, and the type returned will be in accordance. If no store is specified, the global config pf.cfg is used. """ if store is None: store = pf.cfg if prefix: items = [ '%s/%s' % (prefix,i) for i in items ] itemlist = [ I(i,store[i]) for i in items ] + [ I('_save_',True,text='Save changes') ] res = widgets.InputDialog(itemlist,'Config Dialog',pf.GUI).getResult() pf.debug(res) if res and store==pf.cfg: updateSettings(res) return res def setDrawWait(): askConfigPreferences(['draw/wait']) pf.GUI.drawwait = pf.cfg['draw/wait'] def setLinewidth(): askConfigPreferences(['draw/linewidth']) def setAvgNormalTreshold(): askConfigPreferences(['render/avgnormaltreshold']) def setAvgNormalSize(): askConfigPreferences(['mark/avgnormalsize']) def setSize(): pf.GUI.resize(800,600) def setPickSize(): w,h = pf.cfg['pick/size'] res = draw.askItems([['w',w],['h',h]]) pf.prefcfg['pick/size'] = (int(res['w']),int(res['h'])) def set_mat_value(field): key = field.text().replace('material/','') val = field.value() vp = pf.GUI.viewports.current mat = vp.material mat.setValues(**{key:val}) #print vp.material vp.update() def set_light_value(field): light = field.data key = field.text() val = field.value() #print light,key,val draw.set_light_value(light,key,val) def createLightDialogItems(light=0,enabled=True): keys = [ 'ambient', 'diffuse', 'specular', 'position' ] tgt = 'render/light%s'%light val = pf.cfg[tgt] print "LIGHT %s" % light print "CFG %s " % val #print "DICT %s" % pf.canvas.lights.lights[light].__dict__ #print "DICT %s" % dir(pf.canvas.lights.lights[light]) items = [ I('enabled',enabled), ] + [ I(k,val[k],itemtype='slider',min=0,max=100,scale=0.01,func=set_light_value,data=light) for k in [ 'ambient', 'diffuse', 'specular' ] ] + [ I('position',val['position']), ] return items ## def showLighting(): ## print "ACCORDING TO CANVAS:" ## print pf.canvas.lights ## print "ACCORDING TO CFG:" ## print pf.cfg['render'] ## def setLighting(): ## nlights = pf.cfg['render/nlights'] ## mat_items = [ ## {'name':a,'text':a,'value':getattr(pf.canvas,a),'itemtype':'slider','min':0,'max':100,'scale':0.01,'func':set_mat_value } for a in [ 'ambient', 'diffuse', 'specular', 'emission'] ] + [ ## {'name':a,'text':a,'value':getattr(pf.canvas,a),'itemtype':'slider','min':1,'max':64,'scale':1.,'func':set_mat_value } for a in ['shininess'] ] ## enabled = [ pf.cfg['render/light%s'%light] is not None and pf.cfg['render/light%s'%light].get('enabled',False) for light in range(nlights) ] ## pf.debug("ENABLED LIGHTS") ## choices = pf.canvas.light_model.keys() ## # DO NOT ALLOW THE LIGHT MODEL TO BE CHANGED ## choices = [ 'ambient and diffuse' ] ## items = [ ## {'name':'lightmodel','value':pf.canvas.lightmodel,'choices':choices,'tooltip':"""The light model defines which light components are set by the color setting functions. The default light model is 'ambient and diffuse'. The other modes are experimentally. Use them only if you know what you are doing."""}, ## G('material',mat_items), ## # I('nlights',4,text='Number of lights'), ## ] + [ ## T('light%s'%light, createLightDialogItems(light,True)) for light in range(nlights) ## ] ## enablers = [ ## ('lightmodel','','material/ambient','material/diffuse'), ## ] ## dia = None ## def close(): ## dia.close() ## def accept(save=False): ## dia.acceptData() ## #print "RESULTS",dia.results ## res = {} ## res['material'] = utils.subDict(dia.results,'material/') ## for i in range(8): ## key = 'light%s'%i ## res[key] = utils.subDict(dia.results,key+'/') ## rest = [ k for k in dia.results.keys() if not (k.startswith('material') or k.startswith('light')) ] ## rest = dict((k,dia.results[k]) for k in rest) ## res.update(rest) ## res = utils.prefixDict(res,'render/') ## res['_save_'] = save ## updateSettings(res) ## def acceptAndSave(): ## accept(save=True) ## def addLight(): ## accept(save=False) ## dia.close() ## def createDialog(): ## dia = widgets.InputDialog( ## caption='pyFormex Settings', ## enablers = enablers, ## #store=pf.cfg, ## items=items, ## prefix='render/', ## autoprefix=True, ## actions=[ ## ('Close',close), ## ('Accept and Save',acceptAndSave), ## ('Apply',accept), ## ] ## ) ## return dia ## dia = createDialog() ## dia.show() ## #if res: ## # updateSettings({tgt:res}) ## # pf.canvas.resetLights() def setRendering(): import canvas vp = pf.GUI.viewports.current dia = None def enableLightParams(mode): if dia is None: return mode = str(mode) on = mode.startswith('smooth') for f in ['ambient','material']: dia['render/'+f].setEnabled(on) dia['material'].setEnabled(on) def updateLightParams(matname): matname=str(matname) mat = pf.GUI.materials[matname] val = utils.prefixDict(mat.dict(),'material/') print "UPDATE",val dia.updateData(val) def close(): dia.close() def accept(save=False): dia.acceptData() print "RESULTS",dia.results if dia.results['render/mode'].startswith('smooth'): res = utils.subDict(dia.results,'render/',strip=False) matname = dia.results['render/material'] matdata = utils.subDict(dia.results,'material/') # Currently, set both in cfg and Material db pf.cfg['material/%s' % matname] = matdata pf.GUI.materials[matname] = canvas.Material(matname,**matdata) else: res = utils.selectDict(dia.results,['render/mode','render/lighting']) res['_save_'] = save print "RES",res updateSettings(res) print pf.cfg vp = pf.GUI.viewports.current vp.resetLighting() #if pf.cfg['render/mode'] != vp.rendermode: print "SETMODE %s %s" % (pf.cfg['render/mode'],pf.cfg['render/lighting']) vp.setRenderMode(pf.cfg['render/mode'],pf.cfg['render/lighting']) print vp.rendermode,vp.lighting vp.update() toolbar.updateLightButton() def acceptAndSave(): accept(save=True) def createDialog(): matnames = pf.GUI.materials.keys() mat = vp.material mat_items = [ I(a,text=a,value=getattr(mat,a),itemtype='slider',min=0,max=100,scale=0.01,func=set_mat_value) for a in [ 'ambient', 'diffuse', 'specular', 'emission'] ] + [ I(a,text=a,value=getattr(mat,a),itemtype='slider',min=1,max=128,scale=1.,func=set_mat_value) for a in ['shininess'] ] items = [ I('render/mode',vp.rendermode,text='Rendering Mode',itemtype='select',choices=canvas.Canvas.rendermodes),#,onselect=enableLightParams), I('render/lighting',vp.lighting,text='Use Lighting'), I('render/ambient',vp.lightprof.ambient,text='Global Ambient Lighting'), I('render/material',vp.material.name,text='Material',choices=matnames,onselect=updateLightParams), G('material',text='Material Parameters',items=mat_items), ] enablers = [ ('render/lighting',True,'render/ambient','render/material','material'), ] dia = widgets.InputDialog( caption='pyFormex Settings', enablers = enablers, #store=pf.cfg, items=items, #prefix='render/', autoprefix=True, actions=[ ('Close',close), ('Apply and Save',acceptAndSave), ('Apply',accept), ] ) enableLightParams(vp.rendermode) return dia dia = createDialog() dia.show() def setScriptDirs(): dia = createScriptDirsDialog() dia.exec_() def createScriptDirsDialog(): _dia=None _table=None def insertRow(): ww = widgets.FileSelection(pf.cfg['workdir'],'*',exist=True,dir=True) fn = ww.getFilename() if fn: scr = pf.cfg['scriptdirs'] _table.model().insertRows() scr[-1] = ['New',fn] _table.update() def editRow(): row = _table.currentIndex().row() scr = pf.cfg['scriptdirs'] item = scr[row] res = draw.askItems([('Label',item[0]),('Path',item[1])]) if res: scr[row] = [res['Label'],res['Path']] _table.update() def removeRow(): row = _table.currentIndex().row() _table.model().removeRows(row,1) _table.update() def moveUp(): row = _table.currentIndex().row() scr = pf.cfg['scriptdirs'] if row > 0: a,b = scr[row-1:row+1] scr[row-1] = b scr[row] = a _table.setFocus() # For some unkown reason, this seems needed to # immediately update the widget _table.update() pf.app.processEvents() def saveTable(): #print pf.cfg['scriptdirs'] pf.prefcfg['scriptdirs'] = pf.cfg['scriptdirs'] #global _dia,_table from scriptMenu import reloadScriptMenu scr = pf.cfg['scriptdirs'] _table = widgets.Table(scr,chead=['Label','Path']) _dia = widgets.GenericDialog( widgets=[_table], title='Script paths', actions=[('New',insertRow),('Edit',editRow),('Delete',removeRow),('Move Up',moveUp),('Reload',reloadScriptMenu),('Save',saveTable),('OK',)], ) return _dia def setOptions(): options = [ 'debug' ] # Currently No user changeable options ['test'] options = [ o for o in options if hasattr(pf.options,o) ] items = [ I(o,getattr(pf.options,o)) for o in options ] ## # currently we only have All or None as debug levels ## debug_levels = [ 'All','None' ] ## if pf.options.debug: ## debug = 'All' ## else: ## debug = 'None' ## items.append(I('debug',debug,'vradio',choices=debug_levels)) res = draw.askItems(items) if res: print res for o in options: setattr(pf.options,o,res[o]) #setattr(pf.options,'debug',debug_levels.index(res['debug'])-1) print("Options: %s" % pf.options) ## if o == 'debug': ## pf.setDebugFunc() # Functions defined to delay binding def coordsbox(): """Toggle the coordinate display box on or off""" pf.GUI.coordsbox.setVisible(pf.cfg['gui/coordsbox']) def timeoutbutton(): """Toggle the timeout button on or off""" toolbar.addTimeoutButton(pf.GUI.toolbar) def updateCanvas(): pf.canvas.update() def updateStyle(): pf.GUI.setAppearence() def updateToolbars(): pf.GUI.updateToolBars() def updateBackground(): pf.canvas.setBgColor(pf.cfg['canvas/bgcolor'],pf.cfg['canvas/bgcolor2'],pf.cfg['canvas/bgmode']) pf.canvas.update() # This sets the functions that should be called when a setting has changed _activate_settings = { 'gui/coordsbox':coordsbox, 'gui/timeoutbutton':timeoutbutton, 'gui/showfocus':updateCanvas, 'gui/style':updateStyle, 'gui/font':updateStyle, 'gui/camerabar':updateToolbars, 'gui/viewbar':updateToolbars, 'gui/modebar':updateToolbars, 'canvas/bgmode':updateBackground, 'canvas/bgcolor':updateBackground, 'canvas/bgcolor2':updateBackground, } MenuData = [ (_('&Settings'),[ (_('&Settings Dialog'),settings), (_('&Options'),setOptions), ('---',None), (_('&Draw Wait Time'),setDrawWait), (_('Avg&Normal Treshold'),setAvgNormalTreshold), (_('Avg&Normal Size'),setAvgNormalSize), (_('&Pick Size'),setPickSize), (_('&Rendering'),setRendering), #(_('&Set Material Type'),setMaterial), #(_('&Change Material Parameters'),changeMaterial), ## (_('&Show Lighting'),showLighting), ('---',None), (_('&Save Preferences Now'),savePreferences), # (_('&Make current settings the defaults'),savePreferences), # (_('&Reset current settings to the saved defaults'),savePreferences), ]), ] # End pyformex-0.8.6/pyformex/gui/camera.py0000644000211500021150000006336311705104656017511 0ustar benebene00000000000000#!/usr/bin/env python # $Id: camera.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """OpenGL camera handling""" from numpy import * inverse = linalg.linalg.inv multiply = dot def tand(arg): """Return the tan of an angle in degrees.""" return tan(arg*pi/180.) import copy import OpenGL.GL as GL import OpenGL.GLU as GLU def printModelviewMatrix(s="%s"): print(s % GL.glGetFloatv(GL.GL_MODELVIEW_MATRIX)) built_in_views = { 'front': (0.,0.,0.), 'back': (180.,0.,0.), 'right': (90.,0.,0.), 'left': (270.,0.,0.), 'top': (0.,90.,0.), 'bottom': (0.,-90.,0.), 'iso0': (45.,45.,0.), 'iso1': (45.,135.,0.), 'iso2': (45.,225.,0.), 'iso3': (45.,315.,0.), 'iso4': (-45.,45.,0.), 'iso5': (-45.,135.,0.), 'iso6': (-45.,225.,0.), 'iso7': (-45.,315.,0.), } class ViewAngles(dict): """A dict to keep named camera angle settings. This class keeps a dictionary of named angle settings. Each value is a tuple of (longitude, latitude, twist) camera angles. This is a static class which should not need to be instantiated. There are seven predefined values: six for looking along global coordinate axes, one isometric view. """ def __init__(self,data = built_in_views): dict.__init__(self,data) self['iso'] = self['iso0'] def get(self,name): """Get the angles for a named view. Returns a tuple of angles (longitude, latitude, twist) if the named view was defined, or None otherwise """ return dict.get(self,name,None) view_angles = ViewAngles() class Camera(object): """A camera for OpenGL rendering. The Camera class holds all the camera related settings related to the rendering of a scene in OpenGL. These include camera position, the viewing direction of the camera, and the lens parameters (opening angle, front and back clipping planes). This class also provides convenient methods to change the settings so as to get smooth camera manipulation. Camera position and orientation: The camera viewing line is defined by two points: the position of the camera and the center of the scene the camera is looking at. We use the center of the scene as the origin of a local coordinate system to define the camera position. For convenience, this could be stored in spherical coordinates, as a distance value and two angles: longitude and latitude. Furthermore, the camera can also rotate around its viewing line. We can define this by a third angle, the twist. From these four values, the needed translation vector and rotation matrix for the scene rendering may be calculated. Inversely however, we can not compute a unique set of angles from a given rotation matrix (this is known as 'gimball lock'). As a result, continuous (smooth) camera rotation by e.g. mouse control requires that the camera orientation be stored as the full rotation matrix, rather than as three angles. Therefore we store the camera position and orientation as follows: - `ctr`: `[ x,y,z ]` : the reference point of the camera: this is always a point on the viewing axis. Usually, it is set to the center of the scene you are looking at. - `dist`: distance of the camera to the reference point. - `rot`: a 3x3 rotation matrix, rotating the global coordinate system thus that the z-direction is oriented from center to camera. These values have influence on the ModelView matrix. Camera lens settings: The lens parameters define the volume that is seen by the camera. It is described by the following parameters: - `fovy`: the vertical lens opening angle (Field Of View Y), - `aspect`: the aspect ratio (width/height) of the lens. The product `fovy * aspect` is the horizontal field of view. - `near, far`: the position of the front and back clipping planes. They are given as distances from the camera and should both be strictly positive. Anything that is closer to the camera than the `near` plane or further away than the `far` plane, will not be shown on the canvas. Camera methods that change these values will not directly change the ModelView matrix. The :meth:`loadModelView` method has to be called explicitely to make the settings active. These values have influence on the Projection matrix. Methods that change the camera position, orientation or lens parameters will not directly change the related ModelView or Projection matrix. They will just flag a change in the camera settings. The changes are only activated by a call to the :meth:`loadModelView` or :meth:`loadProjection` method, which will test the flags to see whether the corresponding matrix needs a rebuild. The default camera is at distance 1.0 of the center point [0.,0.,0.] and looking in the -z direction. Near and far clipping planes are by default set to 0.1, resp 10 times the camera distance. """ # DEVELOPERS: # The camera class assumes that matrixmode is always ModelView on entry. # For operations in other modes, an explicit switch before the operations # and afterwards back to ModelView should be performed. def __init__(self,center=[0.,0.,0.], long=0., lat=0., twist=0., dist=1.): """Create a new camera at position (0,0,0) looking along the -z axis""" self.locked = False self.setCenter(*center) self.setRotation(long,lat,twist) self.setDist(dist) self.setLens(45.,4./3.) self.setClip(0.1,10.) self.area = None self.resetArea() self.keep_aspect = True self.setPerspective(True) self.viewChanged = True self.tracking = False self.m = self.p = self.v = None # Use only these access functions to make implementation changes easier def getCenter(self): """Return the camera reference point (the scene center).""" return self.ctr def getRot(self): """Return the camera rotation matrix.""" return self.rot def getDist(self): """Return the camera distance.""" return self.dist def lock(self,onoff=True): """Lock/unlock a camera. When a camera is locked, its position and lens parameters can not be changed. This can e.g. be used in multiple viewports layouts to create fixed views from different angles. """ self.locked = bool(onoff) def setCenter(self,x,y,z): """Set the center of the camera in global cartesian coordinates.""" if not self.locked: self.ctr = [x,y,z] self.viewChanged = True def setAngles(self,angles): """Set the rotation angles. angles is either: - a tuple of angles (long,lat,twist) - a named view corresponding to angles in view_angles - None """ if not self.locked: if type(angles) is str: angles = view_angles.get(angles) if angles is None: return self.setRotation(*angles) def setRotation(self,long,lat,twist=0): """Set the rotation matrix of the camera from three angles.""" if not self.locked: GL.glMatrixMode(GL.GL_MODELVIEW) GL.glLoadIdentity() GL.glRotatef(-twist % 360, 0.0, 0.0, 1.0) GL.glRotatef(lat % 360, 1.0, 0.0, 0.0) GL.glRotatef(-long % 360, 0.0, 1.0, 0.0) self.rot = GL.glGetFloatv(GL.GL_MODELVIEW_MATRIX) self.viewChanged = True def setDist(self,dist): """Set the distance.""" if not self.locked: if dist > 0.0 and dist != inf: self.dist = dist self.viewChanged = True def report(self): """Return a report of the current camera settings.""" return """Camera Settings: Center: %s Distance: %s Rotation Matrix: %s Field of View y: %s Aspect Ratio: %s Area: %s, %s Near/Far Clip: %s, %s """ % (self.ctr,self.dist,self.rot,self.fovy,self.aspect,self.area[0],self.area[1],self.near,self.far) def dolly(self,val): """Move the camera eye towards/away from the scene center. This has the effect of zooming. A value > 1 zooms out, a value < 1 zooms in. The resulting enlargement of the view will approximately be 1/val. A zero value will move the camera to the center of the scene. The front and back clipping planes may need adjustment after a dolly operation. """ if not self.locked: self.setDist(self.getDist() * val) #print("DIST %s" % self.dist) self.viewChanged = True def pan(self,val,axis=0): """Rotate the camera around axis through its eye. The camera is rotated around an axis through the eye point. For axes 0 and 1, this will move the center, creating a panning effect. The default axis is parallel to the y-axis, resulting in horizontal panning. For vertical panning (axis=1) a convenience alias tilt is created. For axis = 2 the operation is equivalent to the rotate operation. """ if not self.locked: if axis==0 or axis ==1: pos = self.getPosition() self.eye[axis] = (self.eye[axis] + val) % 360 center = diff(pos,sphericalToCartesian(self.eye)) self.setCenter(*center) elif axis==2: self.twist = (self.twist + val) % 360 self.viewChanged = True def tilt(self,val): """Rotate the camera up/down around its own horizontal axis. The camera is rotated around and perpendicular to the plane of the y-axis and the viewing axis. This has the effect of a vertical pan. A positive value tilts the camera up, shifting the scene down. The value is specified in degrees. """ if not self.locked: self.pan(val,1) self.viewChanged = True def move(self,dx,dy,dz): """Move the camera over translation (dx,dy,dz) in global coordinates. The center of the camera is moved over the specified translation vector. This has the effect of moving the scene in opposite direction. """ if not self.locked: x,y,z = self.ctr self.setCenter(x+dx,y+dy,z+dz) ## def truck(self,dx,dy,dz): ## """Move the camera translation vector in local coordinates. ## This has the effect of moving the scene in opposite direction. ## Positive coordinates mean: ## first coordinate : truck right, ## second coordinate : pedestal up, ## third coordinate : dolly out. ## """ ## #pos = self.getPosition() ## ang = self.getAngles() ## tr = [dx,dy,dz] ## for i in [1,0,2]: ## r = rotationMatrix(i,ang[i]) ## tr = multiply(tr, r) ## self.move(*tr) ## self.viewChanged = True def lookAt(self,eye,center,up): if not self.locked: GL.glMatrixMode(GL.GL_MODELVIEW) GL.glLoadIdentity() GLU.gluLookAt(*concatenate([eye,center,up])) self.saveModelView() def rotate(self,val,vx,vy,vz): """Rotate the camera around current camera axes.""" if not self.locked: GL.glMatrixMode(GL.GL_MODELVIEW) self.saveModelView() GL.glLoadIdentity() GL.glTranslatef(0,0,-self.dist) GL.glRotatef(val,vx,vy,vz) GL.glMultMatrixf(self.rot) dx,dy,dz = self.getCenter() GL.glTranslatef(-dx,-dy,-dz) self.saveModelView() def saveModelView (self): """Save the ModelView matrix.""" if not self.locked: self.m = GL.glGetFloatv(GL.GL_MODELVIEW_MATRIX) self.rot = copy.deepcopy(self.m) self.trl = copy.deepcopy(self.rot[3,0:3]) #print("Translation: %s" % self.trl) self.rot[3,0:3] = [0.,0.,0.] #print "Rotation: %s" % self.rot def setModelView(self): """Set the ModelView matrix from camera parameters. """ if not self.locked: # The camera operations are applied on the model space # Arguments should be taken negative and applied in backwards order GL.glMatrixMode(GL.GL_MODELVIEW) GL.glLoadIdentity() # translate over camera distance GL.glTranslate(0,0,-self.dist) # rotate according to current rotation matrix GL.glMultMatrixf(self.rot) # translate to center dx,dy,dz = self.getCenter() GL.glTranslatef(-dx,-dy,-dz) def loadModelView (self,m=None): """Load the ModelView matrix. There are thrre uses of this function: - Without argument and if the viewing parameters have not changed since the last save of the ModelView matrix, this will just reload the ModelView matrix from the saved value. - If an argument is supplied, it should be a legal ModelView matrix and that matrix will be loaded (and saved) as the new ModelView matrix. - Else, a new ModelView matrix is set up from the camera parameters, and it is loaded and saved. In the latter two cases, the new ModelView matrix is saved, and if a camera attribute `modelview_callback` has been set, a call to this function is done, passing the camera instance as parameter. """ if not self.locked: GL.glMatrixMode(GL.GL_MODELVIEW) if m is not None or self.viewChanged: if m is not None: GL.glLoadMatrixf(m) else: self.setModelView() self.saveModelView() try: self.modelview_callback(self) except: pass self.viewChanged = False else: GL.glLoadMatrixf(self.m) def loadCurrentRotation (self): """Load the current ModelView matrix with translations canceled out.""" rot = GL.glGetFloatv(GL.GL_MODELVIEW_MATRIX) rot[3,0:3] = [0.,0.,0.] GL.glLoadMatrixf(rot) def translate(self,vx,vy,vz,local=True): if not self.locked: if local: vx,vy,vz = self.toWorld([vx,vy,vz,1]) self.move(-vx,-vy,-vz) def transform(self,v): """Transform a vertex using the currently saved Modelview matrix.""" if len(v) == 3: v = v + [ 1. ] v = multiply([v],self.m)[0] return [ a/v[3] for a in v[0:3] ] def toWorld(self,v,trl=False): """Transform a vertex from camera to world coordinates. The specified vector can have 3 or 4 (homogoneous) components. This uses the currently saved rotation matrix. """ a = inverse(array(self.rot)) if len(v) == 3: v = v + [ 1. ] v = multiply(array(v),a) return v[0:3] / v[3] def setLens(self,fovy=None,aspect=None): """Set the field of view of the camera. We set the field of view by the vertical opening angle fovy and the aspect ratio (width/height) of the viewing volume. A parameter that is not specified is left unchanged. """ if fovy: self.fovy = min(abs(fovy),180) if aspect: self.aspect = abs(aspect) self.lensChanged = True def resetArea(self): """Set maximal camera area. Resets the camera window area to its maximum values corresponding to the fovy setting, symmetrical about the camera axes. """ self.setArea(0.,0.,1.,1.,False) def setArea(self,hmin,vmin,hmax,vmax,relative=True,center=False,clip=True): """Set the viewable area of the camera.""" area = array([hmin,vmin,hmax,vmax]) if clip: area = area.clip(0.,1.) if area[0] < area[2] and area[1] < area[3]: area = area.reshape(2,2) mean = (area[1]+area[0]) * 0.5 diff = (area[1]-area[0]) * 0.5 if relative: if center: mean = zeros(2) if self.keep_aspect: aspect = diff[0] / diff[1] if aspect > 1.0: diff[1] = diff[0] #/ self.aspect # no aspect factor: this is relative!!! else: diff[0] = diff[1] #* self.aspect area[0] = mean-diff area[1] = mean+diff #print("RELATIVE AREA %s" % (area)) area = (1.-area) * self.area[0] + area * self.area[1] #print("OLD ZOOM AREA %s (aspect %s)" % (self.area,self.aspect)) #print("NEW ZOOM AREA %s" % (area)) self.area = area self.lensChanged = True def zoomArea(self,val=0.5,area=None): """Zoom in/out by shrinking/enlarging the camera view area. The zoom factor is relative to the current setting. Values smaller than 1.0 zoom in, larger values zoom out. """ if val>0: #val = (1.-val) * 0.5 #self.setArea(val,val,1.-val,1.-val,center=center) if area is None: area = self.area #print("ZOOM AREA %s (%s)" % (area.tolist(),val)) mean = (area[1]+area[0]) * 0.5 diff = (area[1]-area[0]) * 0.5 * val area[0] = mean-diff area[1] = mean+diff self.area = area #print("CAMERA AREA %s" % self.area.tolist()) self.lensChanged = True def transArea(self,dx,dy): """Pan by moving the vamera area. dx and dy are relative movements in fractions of the current area size. """ #print("TRANSAREA %s,%s" % (dx,dy)) area = self.area diff = (area[1]-area[0]) * array([dx,dy]) area += diff self.area = area self.lensChanged = True def setClip(self,near,far): """Set the near and far clipping planes""" if near > 0 and near < far: self.near,self.far = near,far self.lensChanged = True else: print("Error: Invalid Near/Far clipping values") ## def setClipRel(self,near,far): ## """Set the near and far clipping planes""" ## if near > 0 and near < far: ## self.near,self.far = near,far ## self.lensChanged = True ## else: ## print("Error: Invalid Near/Far clipping values") def setPerspective(self,on=True): """Set perspective on or off""" self.perspective = on self.lensChanged = True ## def zoom(self,val=0.5): ## """Zoom in/out by shrinking/enlarging the camera view angle. ## The zoom factor is relative to the current setting. ## Use setFovy() to specify an absolute setting. ## """ ## if val>0: ## self.fovy *= val ## self.lensChanged = True def loadProjection(self,force=False,pick=None,keepmode=False): """Load the projection/perspective matrix. The caller will have to setup the correct GL environment beforehand. No need to set matrix mode though. This function will switch to GL_PROJECTION mode before loading the matrix !! CHANGED: does not switch back to GL_MODELVIEW mode! A pick region can be defined to use the camera in picking mode. pick defines the picking region center and size (x,y,w,h). This function does it best at autodetecting changes in the lens settings, and will only reload the matrix if such changes are detected. You can optionally force loading the matrix. """ GL.glMatrixMode(GL.GL_PROJECTION) if self.lensChanged or force: GL.glLoadIdentity() if pick: GLU.gluPickMatrix(*pick) fv = tand(self.fovy*0.5) if self.perspective: fv *= self.near else: fv *= self.dist fh = fv * self.aspect x0,x1 = 2*self.area - 1.0 frustum = (fh*x0[0],fh*x1[0],fv*x0[1],fv*x1[1],self.near,self.far) if self.perspective: GL.glFrustum(*frustum) else: GL.glOrtho(*frustum) try: self.projection_callback(self) except: pass if not keepmode: GL.glMatrixMode(GL.GL_MODELVIEW) #### global manipulation ################### def set3DMatrices(self): self.loadProjection() self.loadModelView() # this is saved by loadModelView #self.m = GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX) ##!!! self.p and self.v should be saved as we do with self.m self.p = GL.glGetDoublev(GL.GL_PROJECTION_MATRIX) self.v = GL.glGetIntegerv(GL.GL_VIEWPORT) def project(self,x,y,z): "Map the object coordinates (x,y,z) to window coordinates.""" self.set3DMatrices() return GLU.gluProject(x,y,z,self.m,self.p,self.v) def unProject(self,x,y,z): "Map the window coordinates (x,y,z) to object coordinates.""" self.set3DMatrices() return GLU.gluUnProject(x,y,z,self.m,self.p,self.v) def setTracking(self,onoff=True): """Enable/disable coordinate tracking using the camera""" if onoff: self.tracking = True self.set3DMatrices() else: self.tracking = False ############################################################################# if __name__ == "__main__": from OpenGL.GLUT import * import sys def init(): GL.glClearColor (0.0, 0.0, 0.0, 0.0) GL.glShadeModel (GL.GL_FLAT) def display(): global cam GL.glClear (GL.GL_COLOR_BUFFER_BIT) GL.glColor3f (1.0, 1.0, 1.0) GL.glLoadIdentity () # clear the matrix cam.loadProjection() cam.loadModelView() glutWireCube (1.0) GL.glFlush () def reshape (w, h): GL.glViewport (0, 0, w, h) GL.glMatrixMode (GL.GL_PROJECTION) GL.glLoadIdentity () GL.glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0) GL.glMatrixMode (GL.GL_MODELVIEW) def keyboard(key, x, y): global cam if key == 27: sys.exit() elif key == 'd': cam.dolly(1.1) elif key == 'D': cam.dolly(0.9) elif key == 'r': cam.rotate(5.) elif key == 'R': cam.rotate(-5.) elif key == 's': cam.rotate(5.,1) elif key == 'S': cam.rotate(-5.,1) elif key == 'w': cam.rotate(5.,2) elif key == 'W': cam.rotate(-5.,2) elif key == 'p': cam.pan(5.) elif key == 'P': cam.pan(-5.) elif key == 't': cam.tilt(5.) elif key == 'T': cam.tilt(-5.) elif key == 'h': cam.move(0.2,0.,0.) elif key == 'H': cam.move(-0.2,0.,0.) elif key == 'v': cam.move(0.,0.2,0.) elif key == 'V': cam.move(0.,-0.2,0.) elif key == '+': cam.zoom(0.8) elif key == '-': cam.zoom(1.25) ## elif key == 'x': ## cam.truck([0.5,0.,0.]) ## elif key == 'X': ## cam.truck([-0.5,0.,0.]) ## elif key == 'y': ## cam.truck([0.,0.5,0.]) ## elif key == 'Y': ## cam.truck([0.,-0.5,0.]) ## elif key == 'z': ## cam.truck([0.,0.,0.5]) ## elif key == 'Z': ## cam.truck([0.,0.,-0.5]) elif key == 'o': cam.setPerspective(not cam.perspective) else: print(key) display() def main(): global cam glutInit(sys.argv) glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB) glutInitWindowSize (500, 500) #glutInitWindowPosition (100, 100) glutCreateWindow (sys.argv[0]) init () cam = Camera(center=[0.,0.,0.],dist=5.) cam.setLens(45.,1.) glutDisplayFunc(display) glutReshapeFunc(reshape) glutKeyboardFunc(keyboard) glutMainLoop() return 0 main() # End pyformex-0.8.6/pyformex/gui/viewportMenu.py0000644000211500021150000002115011705104656020751 0ustar benebene00000000000000# $Id: viewportMenu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Viewport Menu.""" import pyformex as pf import canvas import widgets import draw from gettext import gettext as _ from widgets import simpleInputItem as _I, compatInputItem as _C def setTriade(): try: pos = pf.canvas.triade.pos siz = pf.canvas.triade.siz except: pos = 'lb' siz = 100 res = draw.askItems([ ('triade',True), ('pos',pos,'select',{'choices':['lt','lc','lb','ct','cc','cb','rt','rc','rb']}), ('size',siz), ]) if res: draw.setTriade(res['triade'],res['pos'],res['size']) def setBgColor(): """Interactively set the viewport background colors.""" mode = pf.canvas.settings.bgmode color = pf.canvas.settings.bgcolor color2 = pf.canvas.settings.bgcolor2 dialog = widgets.InputDialog([ _I('mode',mode,choices=pf.canvas.bgmodes), _I('bgcolor',color,itemtype='color',text='Solid/Top/Left background color'), _I('bgcolor2',color2,itemtype='color',text='Bottom/Right background color'), ],caption='Config Dialog',enablers=[('mode','vertical gradient','bgcolor2'),('mode','horizontal gradient','bgcolor2')]) res = dialog.getResult() pf.debug(res) if res: pf.canvas.setBgColor(res['bgcolor'],res['bgcolor2'],res['mode']) pf.canvas.update() def setFgColor(): """Change the default drawing color.""" color = pf.canvas.settings.fgcolor color = widgets.getColor(color) if color: pf.canvas.setFgColor(color) def setSlColor(): """Change the highlighting color.""" color = pf.canvas.settings.slcolor color = widgets.getColor(color) if color: pf.canvas.setSlColor(color) def setLineWidth(): """Change the default line width.""" lw = pf.canvas.settings.linewidth itemlist = [_C('Line Width', lw, 'float')] res = widgets.InputDialog(itemlist,'Choose default line width').getResult() if res: pf.canvas.setLineWidth(res['Line Width']) def setCanvasSize(): """Save the current viewport size""" itemlist = [_I('w',pf.canvas.width()),_I('h',pf.canvas.height())] res = widgets.InputDialog(itemlist,'Set Canvas Size').getResult() if res: pf.canvas.resize(int(res['w']),int(res['h'])) def viewportSettings(): """Interactively set the viewport settings.""" mode = pf.canvas.rendermode modes = canvas.Canvas.rendermodes s = pf.canvas.settings if s.bgcolor2 is None: s.bgcolor2 = s.bgcolor itemlist = [_I('rendermode', mode, choices=modes), _I('linewidth', s.linewidth, itemtype='float'), _I('bgcolor', s.bgcolor, itemtype='color'), _I('bgcolor2', s.bgcolor2, itemtype='color'), _I('fgcolor', s.fgcolor, itemtype='color'), _I('slcolor', s.slcolor, itemtype='color'), _I('Store these settings as defaults', False), ] res = widgets.InputDialog(itemlist,'Config Dialog').getResult() if res: pf.debug(res) pf.canvas.setRenderMode(res['rendermode']) pf.canvas.settings.update(res,strict=False) #pf.canvas.clear() pf.canvas.redrawAll() pf.canvas.update() if res['Store these settings as defaults']: pf.cfg.update(pf.canvas.settings.__dict__,name='canvas') def viewportLayout(): """Set the viewport layout.""" directions = [ 'rowwise','columnwise' ] if pf.GUI.viewports.rowwise: current = directions[0] else: current = directions[1] itemlist = [_C('Number of viewports',len(pf.GUI.viewports.all)), _C('Viewport layout direction',current,'select',{'choices':directions}), _C('Number of viewports per row/column',pf.GUI.viewports.ncols), ] res = widgets.InputDialog(itemlist,'Config Dialog').getResult() if res: pf.debug(res) nvps = res['Number of viewports'] rowwise = res['Viewport layout direction'] == 'rowwise' ncols = res['Number of viewports per row/column'] if rowwise: nrows = None else: nrows = ncols ncols = None pf.GUI.viewports.changeLayout(nvps,ncols,nrows) # if res['Store these settings as defaults']: # pf.cfg.update() def canvasSettings(): dia = None def apply_(): dia.acceptData() set_near_clip(dia.results['near']) def close(): dia.close() def set_near_clip(v): dist = pf.canvas.camera.getDist() pf.canvas.camera.setClip(10**v*dist,10.*dist) pf.canvas.update() dia = widgets.InputDialog( caption='Canvas Settings', items=[ _C('near',-1.0,'slider',{'min':-100,'max':100,'scale':0.01,'func': set_near_clip,'text':'Near clipping plane'}), ], actions=[('Done',close),('Apply',apply_)] ) dia.show() def openglSettings(): dia = None def apply_(): dia.acceptData() canvas.glSettings(dia.results) def close(): dia.close() dia = widgets.InputDialog( caption='OpenGL Settings', items=[ _C('Line Smoothing','Off','radio',{'choices':['On','Off']}), _C('Polygon Mode',None,'radio',{'choices':['Fill','Line']}), _C('Polygon Fill',None,'radio',{'choices':['Front and Back','Front','Back']}), _C('Culling','Off','radio',{'choices':['On','Off']}), # These are currently set by the render mode # ('Shading',None,'radio',{'choices':['Smooth','Flat']}), # ('Lighting',None,'radio',{'choices':['On','Off']}), ], actions=[('Done',close),('Apply',apply_)] ) dia.show() def lineSmoothOn(): canvas.glLineSmooth(True) def lineSmoothOff(): canvas.glLineSmooth(False) def singleViewport(): draw.layout(1) def clearAll(): for vp in pf.GUI.viewports.all: vp.removeAll() vp.clear() vp.update() pf.GUI.processEvents() MenuData = [ (_('&Viewport'),[ (_('&Clear'),draw.clear), (_('&Clear All'),clearAll), (_('&Axes Triade'),setTriade), # (_('&Transparency'),setOpacity), (_('&Background Color'),setBgColor), (_('&Foreground Color'),setFgColor), (_('&Highlight Color'),setSlColor), (_('Line&Width'),setLineWidth), (_('&Canvas Size'),setCanvasSize), (_('&All Viewport Settings'),viewportSettings), (_('&Global Draw Options'),draw.askDrawOptions), (_('&Canvas Settings'),canvasSettings), (_('&OpenGL Settings'),openglSettings), ## ('&OpenGL Settings', ## [('&Flat',canvas.glFlat), ## ('&Smooth',canvas.glSmooth), ## ('&Culling',canvas.glCulling), ## ('&No Culling',canvas.glNoCulling), ## ('&Line Smoothing On',lineSmoothOn), ## ('&Line Smoothing Off',lineSmoothOff), ## ('&Polygon Line',canvas.glLine), ## ('&Polygon Fill',canvas.glFill), ## ('&Polygon Front Fill',canvas.glFrontFill), ## ('&Polygon Back Fill',canvas.glBackFill), ## ('&Polygon Front and Back Fill',canvas.glBothFill), ## ]), (_('&Redraw'),draw.redraw), (_('&Reset viewport'),draw.reset), (_('&Reset layout'),singleViewport), (_('&Change viewport layout'),viewportLayout), (_('&Add new viewport'),draw.addViewport), (_('&Remove last viewport'),draw.removeViewport), ]), ] # End pyformex-0.8.6/pyformex/gui/imagecolor.py0000644000211500021150000000413311705104656020370 0ustar benebene00000000000000# $Id: imagecolor.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Using bitmap images as colors. This module contains functions to use bitmap images as colors on a pyFormex geometry. """ from pyformex.arraytools import * from PyQt4.QtGui import QImage from imagearray import qimage2numpy def image2glcolor(im,flip=True): """Convert a bitmap image to corresponding OpenGL colors. im is a QImage or any data from which a QImage can be initialized. The image RGB colors are converted to OpenGL colors. The return value is a (w,h,3) shaped array of values in the range 0.0 to 1.0. By default the image is flipped upside-down because the vertical OpenGL axis points upwards, while bitmap images are stored downwards. """ im = QImage(im) c,t = qimage2numpy(im) if flip: c = flipud(c) if t is None: color = dstack([c['r'],c['g'],c['b']]).reshape(-1,3) # print(color.shape) return color.astype(Float)/255.,t else: return c,t # End pyformex-0.8.6/pyformex/gui/guimain.py0000644000211500021150000011411611705104656017703 0ustar benebene00000000000000# $Id: guimain.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Graphical User Interface for pyFormex.""" import pyformex as pf from pyformex.gui import signals import sys,utils if not ( utils.hasModule('numpy') and utils.hasModule('pyopengl') and utils.hasModule('pyqt4') ): sys.exit() import os.path from PyQt4 import QtCore, QtGui import menu import cameraMenu import fileMenu import scriptMenu import prefMenu import toolbar import canvas import viewport import script import draw import widgets import drawlock import camera import warnings import guifunc ############### General Qt utility functions ####### ## might go to a qtutils module def Size(widget): """Return the size of a widget as a tuple.""" s = widget.size() return s.width(),s.height() def Pos(widget): """Return the position of a widget as a tuple.""" p = widget.pos() return p.x(),p.y() def MaxSize(*args): """Return the maximum of a list of sizes""" return max([i[0] for i in args]),max([i[1] for i in args]) def MinSize(*args): """Return the maximum of a list of sizes""" return min([i[0] for i in args]),min([i[1] for i in args]) def printpos(w,t=None): print("%s %s x %s" % (t,w.x(),w.y())) def printsize(w,t=None): print("%s %s x %s" % (t,w.width(),w.height())) ################# Message Board ############### class Board(QtGui.QTextEdit): """Message board for displaying read-only plain text messages.""" def __init__(self,parent=None): """Construct the Message Board widget.""" QtGui.QTextEdit.__init__(self,parent) self.setReadOnly(True) self.setAcceptRichText(False) self.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Sunken) self.setMinimumSize(24,24) self.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding,QtGui.QSizePolicy.MinimumExpanding) self.cursor = self.textCursor() #self.buffer = '' font = QtGui.QFont("DejaVu Sans Mono") #font.setStyle(QtGui.QFont.StyleNormal) self.setFont(font) def write(self,s): """Write a string to the message board.""" # A single blank character seems to be generated by a print # instruction containing a comma: skip it if s == ' ': return #self.buffer += '[%s:%s]' % (len(s),s) s = s.rstrip('\n') if len(s) > 0: self.append(s) self.cursor.movePosition(QtGui.QTextCursor.End) self.setTextCursor(self.cursor) def save(self,filename): """Save the contents of the board to a file""" fil = open(filename,'w') fil.write(self.toPlainText()) fil.close() def flush(self): self.update() ##################################### ################# GUI ############### ##################################### class Gui(QtGui.QMainWindow): """Implements a GUI for pyformex.""" toolbar_area = { 'top': QtCore.Qt.TopToolBarArea, 'bottom': QtCore.Qt.BottomToolBarArea, 'left': QtCore.Qt.LeftToolBarArea, 'right': QtCore.Qt.RightToolBarArea, } def __init__(self,windowname,size=(800,600),pos=(0,0),bdsize=(0,0)): """Constructs the GUI. The GUI has a central canvas for drawing, a menubar and a toolbar on top, and a statusbar at the bottom. """ self.on_exit = [fileMenu.askCloseProject] QtGui.QMainWindow.__init__(self) self.setWindowTitle(windowname) # add widgets to the main window # The status bar self.statusbar = self.statusBar() self.curproj = widgets.ButtonBox('Project:',[('None',fileMenu.openProject)]) self.curfile = widgets.ButtonBox('Script:',[('None',fileMenu.openScript)]) self.curdir = widgets.ButtonBox('Cwd:',[('None',draw.askDirname)]) self.canPlay = False # The menu bar self.menu = menu.MenuBar('TopMenu') self.setMenuBar(self.menu) # The toolbar self.toolbar = self.addToolBar('Top ToolBar') self.editor = None # Create a box for the central widget self.box = QtGui.QWidget() self.setCentralWidget(self.box) self.boxlayout = QtGui.QVBoxLayout() self.box.setLayout(self.boxlayout) #self.box.setFrameStyle(qt.QFrame.Sunken | qt.QFrame.Panel) #self.box.setLineWidth(2) # Create a splitter self.splitter = QtGui.QSplitter() self.boxlayout.addWidget(self.splitter) self.splitter.setOrientation(QtCore.Qt.Vertical) self.splitter.show() # self.central is the complete central widget of the main window self.central = QtGui.QWidget() self.central.autoFillBackground() #self.central.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Sunken) self.central.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding,QtGui.QSizePolicy.MinimumExpanding) self.central.resize(*pf.cfg['gui/size']) self.viewports = viewport.MultiCanvas(parent=self.central) self.central.setLayout(self.viewports) # Create the message board self.board = Board() #self.board.setPlainText(pf.Version+' started') # Put everything together self.splitter.addWidget(self.central) self.splitter.addWidget(self.board) #self.splitter.setSizes([(800,200),(800,600)]) self.box.setLayout(self.boxlayout) # Create the top menu menudata = menu.createMenuData() self.menu.insertItems(menudata) # ... and the toolbar self.actions = toolbar.addActionButtons(self.toolbar) # timeout button toolbar.addTimeoutButton(self.toolbar) self.menu.show() # Define Toolbars self.camerabar = self.updateToolBar('camerabar','Camera ToolBar') self.modebar = self.updateToolBar('modebar','RenderMode ToolBar') self.viewbar = self.updateToolBar('viewbar','Views ToolBar') self.toolbars = [self.camerabar, self.modebar, self.viewbar] ############### CAMERA menu and toolbar ############# if self.camerabar: toolbar.addCameraButtons(self.camerabar) toolbar.addPerspectiveButton(self.camerabar) ############### RENDERMODE menu and toolbar ############# modes = [ 'wireframe', 'smooth', 'smoothwire', 'flat', 'flatwire' ] if pf.cfg['gui/modemenu']: mmenu = QtGui.QMenu('Render Mode') else: mmenu = None #menutext = '&' + name.capitalize() self.modebtns = menu.ActionList( modes,guifunc.renderMode,menu=mmenu,toolbar=self.modebar) # Add the toggle type buttons if self.modebar: toolbar.addTransparencyButton(self.modebar) if self.modebar and pf.cfg['gui/lightbutton']: toolbar.addLightButton(self.modebar) if self.modebar and pf.cfg['gui/normalsbutton']: toolbar.addNormalsButton(self.modebar) if self.modebar and pf.cfg['gui/shrinkbutton']: toolbar.addShrinkButton(self.modebar) if mmenu: # insert the mode menu in the viewport menu pmenu = self.menu.item('viewport') pmenu.insertMenu(pmenu.item('background color'),mmenu) ############### VIEWS menu ################ if pf.cfg['gui/viewmenu']: if pf.cfg['gui/viewmenu'] == 'main': parent = self.menu before = 'help' else: parent = self.menu.item('camera') before = parent.item('---') self.viewsMenu = menu.Menu('&Views',parent=parent,before=before) else: self.viewsMenu = None defviews = pf.cfg['gui/defviews'] views = [ v[0] for v in defviews ] viewicons = [ v[1] for v in defviews ] self.viewbtns = menu.ActionList( views,self.setView, menu=self.viewsMenu, toolbar=self.viewbar, icons = viewicons ) ## TESTING SAVE CURRENT VIEW ## self.saved_views = {} self.saved_views_name = utils.NameSequence('View') if self.viewsMenu: name = self.saved_views_name.next() self.menu.item('camera').addAction('Save View',self.saveView) # Restore previous pos/size self.resize(*size) self.move(*pos) self.board.resize(*bdsize) self.setcurfile() self.setcurdir() if pf.options.redirect: sys.stderr = self.board sys.stdout = self.board if pf.options.debug: printsize(self,'DEBUG: Main:') printsize(self.central,'DEBUG: Canvas:') printsize(self.board,'DEBUG: Board:') # Drawing lock self.drawwait = pf.cfg['draw/wait'] self.drawlock = drawlock.DrawLock() # Materials and Lights database self.materials = canvas.createMaterials() ## for m in self.materials: ## print self.materials[m] def saveView(self,name=None,addtogui=True): """Save the current view and optionally create a button for it. This saves the current viewport ModelView and Projection matrices under the specified name. It adds the view to the views Menu and Toolbar, if these exist and do not have the name yet. """ if name is None: name = self.saved_views_name.next() self.saved_views[name] = (pf.canvas.camera.m,None) if name not in self.viewbtns.names(): iconpath = os.path.join(pf.cfg['icondir'],'userview')+pf.cfg['gui/icontype'] self.viewbtns.add(name,iconpath) def applyView(self,name): """Apply a saved view to the current camera. """ m,p = self.saved_views.get(name,(None,None)) if m is not None: self.viewports.current.camera.loadModelView(m) def createView(self,name,angles): """Create a new view and add it to the list of predefined views. This creates a named view with specified angles or, if the name already exists, changes its angles to the new values. It adds the view to the views Menu and Toolbar, if these exist and do not have the name yet. """ if name not in self.viewbtns.names(): iconpath = os.path.join(pf.cfg['icondir'],'userview')+pf.cfg['gui/icontype'] self.viewbtns.add(name,iconpath) camera.view_angles[name] = angles def setView(self,view): """Change the view of the current GUI viewport, keeping the bbox. view is the name of one of the defined views. """ if view in self.saved_views: self.applyView(view) else: self.viewports.current.setCamera(angles=view) self.viewports.current.update() def updateToolBars(self): for t in ['camerabar','modebar','viewbar']: self.updateToolBar(t) def updateToolBar(self,shortname,fullname=None): """Add a toolbar or change its position. This function adds a toolbar to the GUI main window at the position specified in the configuration. If the toolbar already exists, it is moved from its previous location to the requested position. If the toolbar does not exist, it is created with the given fullname, or the shortname by default. The full name is the name as displayed to the user. The short name is the name as used in the config settings. The config setting for the toolbar determines its placement: - None: the toolbar is not created - 'left', 'right', 'top' or 'bottom': a separate toolbar is created - 'default': the default top toolbar is used and a separator is added. """ area = pf.cfg['gui/%s' % shortname] try: toolbar = getattr(self,shortname) except: toolbar = None if area: area = self.toolbar_area.get(area,4) # default is top # Add/reposition the toolbar if toolbar is None: if fullname is None: fullname = shortname toolbar = QtGui.QToolBar(fullname,self) self.addToolBar(area,toolbar) else: if toolbar is not None: self.removeToolBar(toolbar) toolbar = None return toolbar ## def activateToolBar(self,fullname,shortname): ## """Add a new toolbar to the GUI main window. ## The full name is the name as displayed to the user. ## The short name is the name as used in the config settings. ## The config setting for the toolbar determines its placement: ## - None: the toolbar is not created ## - 'left', 'right', 'top' or 'bottom': a separate toolbar is created ## - 'default': the default top toolbar is used and a separator is added. ## """ ## area = pf.cfg['gui/%s' % shortname] ## if area: ## area = self.toolbar_area.get(area,0) ## if area: ## toolbar = QtGui.QToolBar(fullname,self) ## self.addToolBar(area,toolbar) ## else: # default ## toolbar = self.toolbar ## self.toolbar.addSeparator() ## else: ## toolbar = None ## return toolbar def addStatusBarButtons(self): sbh = self.statusbar.height() #self.curproj.setFixedHeight(32) #self.curfile.setFixedHeight(32) self.statusbar.addWidget(self.curproj) self.statusbar.addWidget(self.curfile) self.statusbar.addWidget(self.curdir) def addInputBox(self): self.input = widgets.InputString('Input:','') self.statusbar.addWidget(self.input) def toggleInputBox(self,onoff=None): if onoff is None: onoff = self.input.isHidden() self.input.setVisible(onoff) def addCoordsTracker(self): self.coordsbox = widgets.CoordsBox() self.statusbar.addPermanentWidget(self.coordsbox) def toggleCoordsTracker(self,onoff=None): def track(x,y,z): X,Y,Z = pf.canvas.unProject(x,y,z,True) print "%s --> %s" % ((x,y,z),(X,Y,Z)) pf.GUI.coordsbox.setValues([X,Y,Z]) if onoff is None: onoff = self.coordsbox.isHidden() if onoff: func = track else: func = None for vp in self.viewports.all: vp.trackfunc = func self.coordsbox.setVisible(onoff) def resizeCanvas(self,wd,ht): """Resize the canvas.""" self.central.resize(wd,ht) self.box.resize(wd,ht+self.board.height()) self.adjustSize() print("RESIZED",Pos(self)) def showEditor(self): """Start the editor.""" if not hasattr(self,'editor'): self.editor = Editor(self,'Editor') self.editor.show() self.editor.setText("Hallo\n") def closeEditor(self): """Close the editor.""" if hasattr(self,'editor'): self.editor.close() self.editor = None def setcurproj(self,project=''): """Show the current project name.""" if project: project = os.path.basename(project) self.curproj.setText(project) def setcurfile(self,filename=''): """Set the current file and check whether it is a pyFormex script. The checking is done by the function is_pyFormex(). A file that is not a pyFormex script can be loaded in the editor, but it can not be played as a pyFormex script. """ if filename: # We always set it to be saved in the prefs pf.prefcfg['curfile'] = filename else: filename = pf.cfg['curfile'] if filename: self.canPlay = utils.is_pyFormex(filename) or filename.endswith('.pye') self.curfile.setText(os.path.basename(filename)) self.actions['Play'].setEnabled(self.canPlay) self.actions['Step'].setEnabled(self.canPlay) self.actions['Stop'].setEnabled(self.canPlay) if self.canPlay: icon = 'ok' else: icon = 'notok' self.curfile.setIcon(QtGui.QIcon(QtGui.QPixmap(os.path.join(pf.cfg['icondir'],icon)+pf.cfg['gui/icontype'])),0) def setcurdir(self): """Show the current workdir.""" dirname = os.getcwd() shortname = os.path.basename(dirname) self.curdir.setText(shortname) self.curdir.setToolTip(dirname) def setBusy(self,busy=True,force=False): if busy: pf.app.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor)) else: pf.app.restoreOverrideCursor() self.processEvents() def resetCursor(self): """Clear the override cursor stack. This will reset the application cursor to the initial default. """ while pf.app.overrideCursor(): pf.app.restoreOverrideCursor() self.processEvents() def keyPressEvent(self,e): """Top level key press event handler. Events get here if they are not handled by a lower level handler. Every key press arriving here generates a WAKEUP signal, and if a dedicated signal for the key was installed in the keypress table, that signal is emitted too. Finally, the event is removed. """ key = e.key() pf.debug('Key %s pressed' % key) self.emit(signals.WAKEUP,()) signal = signals.keypress_signal.get(key,None) if signal: self.emit(signal,()) e.ignore() def XPos(self): """Get the main window position from the xwininfo command. The (Py)Qt4 position does not get updated when changing the window size from the left. This substitute function will find the correct position from the xwininfo command output. """ res = xwininfo(self.winId()) ax,ay,rx,ry = [ int(res[key]) for key in [ 'Absolute upper-left X','Absolute upper-left Y', 'Relative upper-left X','Relative upper-left Y', ]] return ax-rx,ay-ry def writeSettings(self): """Store the GUI settings""" # FIX QT4 BUG # Make sure QT4 has position right self.move(*self.XPos()) # store the history and main window size/pos pf.prefcfg['gui/history'] = pf.GUI.history.files pf.prefcfg.update({'size':Size(pf.GUI), 'pos':Pos(pf.GUI), 'bdsize':Size(pf.GUI.board), },name='gui') def cleanup(self): """Cleanup the GUI (restore default state).""" pf.debug('GUI cleanup') self.drawlock.release() pf.canvas.cancel_selection() pf.canvas.cancel_draw() draw.clear_canvas() self.setBusy(False) def closeEvent(self,event): """Close Main Window Event Handler""" ## if draw.ack("Do you really want to quit?"): ## print("YES:EXIT") self.cleanup() pf.debug("Executing registered exit functions") for f in self.on_exit: pf.debug(f) f() self.writeSettings() event.accept() ## else: ## print("NO:STAY") ## event.ignore() def onExit(self,func): """Register a function for execution on exit""" self.on_exit.append(func) # THESE FUNCTION SHOULD BECOME app FUNCTIONS def currentStyle(self): return pf.app.style().metaObject().className()[1:-5] def getStyles(self): return map(str,QtGui.QStyleFactory().keys()) def setStyle(self,style): """Set the main application style.""" style = QtGui.QStyleFactory().create(style) pf.app.setStyle(style) self.update() def setFont(self,font): """Set the main application font. font is either a QFont or a string resulting from the QFont.toString() method """ if not isinstance(font,QtGui.QFont): f = QtGui.QFont() f.fromString(font) font = f pf.app.setFont(font) self.update() def setFontFamily(self,family): """Set the main application font family to the given family.""" font = pf.app.font() font.setFamily(family) self.setFont(font) def setFontSize(self,size): """Set the main application font size to the given point size.""" font = pf.app.font() font.setPointSize(int(size)) self.setFont(font) def setAppearence(self): """Set all the GUI appearence elements. Sets the GUI appearence from the current configuration values 'gui/style', 'gui/font', 'gui/fontfamily', 'gui/fontsize'. """ style = pf.cfg['gui/style'] font = pf.cfg['gui/font'] family = pf.cfg['gui/fontfamily'] size = pf.cfg['gui/fontsize'] if style: self.setStyle(style) if font: self.setFont(font) if family: self.setFontFamily(family) if size: self.setFontSize(size) def processEvents(self): """Process interactive GUI events.""" #saved = pf.canvas #print "SAVED script canvas %s" % pf.canvas #pf.canvas = self.viewports.current #print "SET script canvas %s" % pf.canvas if pf.app: pf.app.processEvents() #pf.canvas = saved #print "RESTORED script canvas %s" % pf.canvas def findDialog(self,name): """Find the InputDialog with the specified name. Returns the list with maching dialogs, possibly empty. """ return self.findChildren(widgets.InputDialog,str(name)) def closeDialog(self,name): """Close the InputDialog with the specified name. Closest all the InputDialogs with the specified caption owned by the GUI. """ for w in self.findDialog(name): w.close() def xwininfo(windowid=None,name=None): """Returns the X window info parsed as a dict. Either the windowid or the window name has to be specified. """ import re cmd = 'xwininfo %s 2> /dev/null' if windowid is not None: args = " -id %s" % windowid elif name is not None: args = " -name '%s'" % name else: raise ValueError,"Either windowid or name have to be specified" sta,out = utils.runCommand(cmd % args,RaiseError=False,quiet=True) res = {} if not sta: for line in out.split('\n'): s = line.split(':') if len(s) < 2: s = s[0].strip().split(' ') if len(s) < 2: continue elif len(s) > 2: if s[0] == 'xwininfo': s = s[-2:] # remove the xwininfo string t = s[1].split() s[1] = t[0] # windowid name = ' '.join(t[1:]).strip().strip('"') res['Window name'] = name if s[0][0] == '-': s[0] = s[0][1:] res[s[0].strip()] = s[1].strip() return res def pidofxwin(windowid): """Returns the PID of the process that has created the window. Remark: Not all processes store the PID information in the way it is retrieved here. In many cases (X over network) the PID can not be retrieved. However, the intent of this function is just to find a dangling pyFormex process, and should probably work on a normal desktop configuration. """ import re sta,out = utils.runCommand('xprop -id %s _NET_WM_PID' % windowid,quiet=True) m = re.match("_NET_WM_PID\(.*\)\s*=\s*(?P\d+)",out) if m: pid = m.group('pid') #print "Found PID %s" % pid return int(pid) return None def windowExists(windowname): """Check if a GUI window with the given name exists. On X-Window systems, we can use the xwininfo command to find out whether a window with the specified name exists. """ return not os.system('xwininfo -name "%s" > /dev/null 2>&1' % windowname) def findOldProcesses(max=16): """Find old pyFormex GUI processes still running. There is a maximum to the number of processes taht can be detected. 16 will suffice laregley, because there is no sane reason to open that many pyFormex GUI's on the same screen. Returns the next available main window name, and a list of running pyFormex GUI processes, if any. """ windowname = pf.Version count = 0 running = [] while count < max: info = xwininfo(name=windowname) if info: name = info['Window name'] windowid = info['Window id'] if name == windowname: pid = pidofxwin(windowid) else: pid = None # pid control needed for invisible windows on ubuntu if pid: running.append((windowid,name,pid)) count += 1 windowname = '%s (%s)' % (pf.Version,count) else: break else: break return windowname,running def killProcesses(pids): """Kill the processes in the pids list.""" warning = """.. Killing processes ----------------- I will now try to kill the following processes:: %s You can choose the signal to be sent to the processes: - KILL (9) - TERM (15) We advice you to first try the TERM(15) signal, and only if that does not seem to work, use the KILL(9) signal. """ % pids actions = ['Cancel the operation','KILL(9)','TERM(15)'] answer = draw.ask(warning,actions) if answer == 'TERM(15)': utils.killProcesses(pids,15) elif answer == 'KILL(9)': utils.killProcesses(pids,9) def quitGUI(): """Quit the GUI""" pf.debug("Quit GUI") sys.stderr = sys.__stderr__ sys.stdout = sys.__stdout__ #print "QUIT" pf.GUI.drawlock.free() draw.wakeup() if pf.options.gui: script.force_finish() if pf.app: pf.app.exit() pf.app = None def startGUI(args): """Create the QT4 application and GUI. A (possibly empty) list of command line options should be provided. QT4 wil remove the recognized QT4 and X11 options. """ # This seems to be the only way to make sure the numeric conversion is # always correct # QtCore.QLocale.setDefault(QtCore.QLocale.c()) # #pf.options.debug = -1 pf.debug("Arguments passed to the QApplication: %s" % args) pf.app = QtGui.QApplication(args) # pf.debug("Arguments left after constructing the QApplication: %s" % args) pf.debug("Arguments left after constructing the QApplication: %s" % pf.app.arguments().join('\n')) #pf.options.debug = 0 # As far as I have been testing this, the args passed to the Qt application are # NOT acknowledged and neither are they removed!! pf.debug("Setting application attributes") pf.app.setOrganizationName("pyformex.org") pf.app.setOrganizationDomain("pyformex.org") pf.app.setApplicationName("pyFormex") pf.app.setApplicationVersion(pf.__version__) ## pf.settings = QtCore.QSettings("pyformex.org", "pyFormex") ## pf.settings.setValue("testje","testvalue") #QtCore.QObject.connect(pf.app,QtCore.SIGNAL("lastWindowClosed()"),pf.app,QtCore.SLOT("quit()")) QtCore.QObject.connect(pf.app,QtCore.SIGNAL("lastWindowClosed()"),quitGUI) #QtCore.QObject.connect(pf.app,QtCore.SIGNAL("aboutToQuit()"),quitGUI) # Check if we have DRI pf.debug("Setting OpenGL format") viewport.setOpenGLFormat() dri = viewport.opengl_format.directRendering() # Check for existing pyFormex processes pf.debug("Checking for running pyFormex") if pf.X11: windowname,running = findOldProcesses() else: windowname,running = "UNKOWN",[] pf.debug("%s,%s" % (windowname,running)) while len(running) > 0: if len(running) >= 16: print("Too many open pyFormex windows --- bailing out") return -1 pids = [ i[2] for i in running if i[2] is not None ] warning = """.. pyFormex is already running on this screen ------------------------------------------ A main pyFormex window already exists on your screen. If you really intended to start another instance of pyFormex, you can just continue now. The window might however be a leftover from a previously crashed pyFormex session, in which case you might not even see the window anymore, nor be able to shut down that running process. In that case, you would better bail out now and try to fix the problem by killing the related process(es). If you think you have already killed those processes, you may check it by rerunning the tests. """ actions = ['Really Continue','Rerun the tests','Bail out and fix the problem'] if pids: warning += """ I have identified the process(es) by their PID as:: %s If you trust me enough, you can also have me kill this processes for you. """ % pids actions[2:2] = ['Kill the running processes'] if dri: answer = draw.ask(warning,actions) else: warning += """ I have detected that the Direct Rendering Infrastructure is not activated on your system. Continuing with a second instance of pyFormex may crash your XWindow system. You should seriously consider to bail out now!!! """ answer = draw.warning(warning,actions) if answer == 'Really Continue': break # OK, Go ahead elif answer == 'Rerun the tests': windowname,running = findOldProcesses() # try again elif answer == 'Kill the running processes': killProcesses(pids) windowname,running = findOldProcesses() # try again else: return -1 # I'm out of here! # Load the splash image pf.debug("Loading the splash image") splash = None if os.path.exists(pf.cfg['gui/splash']): pf.debug('Loading splash %s' % pf.cfg['gui/splash']) splashimage = QtGui.QPixmap(pf.cfg['gui/splash']) splash = QtGui.QSplashScreen(splashimage) splash.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) splash.setFont(QtGui.QFont("Helvetica",24)) splash.showMessage(pf.Version,QtCore.Qt.AlignHCenter,QtCore.Qt.red) splash.show() # create GUI, show it, run it pf.debug("Creating the GUI") desktop = pf.app.desktop() pf.maxsize = Size(desktop.availableGeometry()) size = pf.cfg.get('gui/size',(800,600)) pos = pf.cfg.get('gui/pos',(0,0)) bdsize = pf.cfg.get('gui/bdsize',(800,600)) size = MinSize(size,pf.maxsize) pf.GUI = Gui(windowname, pf.cfg.get('gui/size',(800,600)), pf.cfg.get('gui/pos',(0,0)), pf.cfg.get('gui/bdsize',(800,600)), ) # set the appearance pf.GUI.setAppearence() # setup the message board pf.board = pf.GUI.board pf.board.write("""%s (Rev. %s) (C) Benedict Verhegghe pyFormex comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the conditions of the GNU General Public License, version 3 or later. See Help->License or the file COPYING for details. """ % (pf.Version,pf.__revision__)) # Set interaction functions def show_warning(message,category,filename,lineno,file=None,line=None): """Replace the default warnings.showwarning We display the warnings using our interactive warning widget. This feature can be turned off by setting cfg['warnings/popup'] = False """ full_message = warnings.formatwarning(message,category,filename,lineno,line) ## from widgets import simpleInputItem as I ## res = draw.askItems([ ## I('message',message,itemtype='label',text='warning'), ## I('filter',False,text='Suppress this message in future sessions'), ## ],actions=[('OK',)],legacy=False) #print res pf.message(full_message) res,check = draw.showMessage(full_message,level='warning',check="Do not show this warning anymore in future sessions") if check[0]: oldfilters = pf.prefcfg['warnings/filters'] newfilters = oldfilters + [(str(message),)] pf.prefcfg.update({'filters':newfilters},name='warnings') ## def format_warning(message,category,filename,lineno,line=None): ## """Replace the default warnings.formatwarning ## We display the warnings using our interactive warning widget. ## This feature can be turned off by setting ## cfg['nice_warnings'] = False ## """ ## import messages ## message = messages.getMessage(message) ## message = """.. ## pyFormex Warning ## ================ ## %s ## `Called from:` %s `line:` %s ## """ % (message,filename,lineno) ## if line: ## message += "%s\n" % line ## return message ## if pf.cfg['warnings/nice']: ## warnings.formatwarning = format_warning if pf.cfg['warnings/popup']: warnings.showwarning = show_warning pf.message = draw.message pf.warning = draw.warning # setup the canvas pf.GUI.viewports.changeLayout(1) pf.GUI.viewports.setCurrent(0) pf.canvas = pf.GUI.viewports.current draw.reset() # setup the status bar pf.GUI.addInputBox() pf.GUI.toggleInputBox(False) pf.GUI.addCoordsTracker() pf.GUI.toggleCoordsTracker(pf.cfg.get('gui/coordsbox',False)) pf.debug("Using window name %s" % pf.GUI.windowTitle()) # Create additional menus (put them in a list to save) # History Menu pf.GUI.history = scriptMenu.ScriptMenu('History',files=pf.cfg['gui/history'],max=pf.cfg['gui/history_max']) if pf.cfg.get('gui/history_in_main_menu',False): before = pf.GUI.menu.item('help') pf.GUI.menu.insertMenu(before,pf.GUI.history) else: filemenu = pf.GUI.menu.item('file') before = filemenu.item('---1') filemenu.insertMenu(before,pf.GUI.history) # Scripts menu pf.GUI.scriptmenu = scriptMenu.createScriptMenu(pf.GUI.menu,before='help') # Create databases createDatabases() # Plugin menus import plugins filemenu = pf.GUI.menu.item('file') pf.gui.saveobj = plugins.create_plugin_menu(filemenu,before='History') # Load configured plugins, ignore if not found plugins.loadConfiguredPlugins() # Last minute menu modifications can go here # cleanup pf.GUI.setBusy(False) # HERE pf.GUI.addStatusBarButtons() if splash is not None: # remove the splash window splash.finish(pf.GUI) pf.GUI.setBusy(False) # OR HERE pf.debug("Showing the GUI") pf.GUI.show() pf.GUI.update() if pf.cfg['gui/fortune']: sta,out = utils.runCommand(pf.cfg['fortune']) if sta == 0: draw.showInfo(out) warnings.warn('warn_quadratic_drawing') pf.app_started = True pf.GUI.processEvents() return 0 def createDatabases(): """Create unified database objects for all menus.""" from plugins import objects from geometry import Geometry from formex import Formex from mesh import Mesh from plugins.trisurface import TriSurface from plugins.curve import PolyLine,BezierSpline from plugins.nurbs import NurbsCurve pf.GUI.database = objects.Objects() pf.GUI.drawable = objects.DrawableObjects() pf.GUI.selection = { 'geometry' : objects.DrawableObjects(clas=Geometry), 'formex' : objects.DrawableObjects(clas=Formex), 'mesh' : objects.DrawableObjects(clas=Mesh), 'surface' : objects.DrawableObjects(clas=TriSurface), 'polyline' : objects.DrawableObjects(clas=PolyLine), 'nurbs' : objects.DrawableObjects(clas=NurbsCurve), 'curve' : objects.DrawableObjects(clas=BezierSpline), } def runGUI(): """Go into interactive mode""" egg = pf.cfg.get('gui/easter_egg',None) pf.debug('EGG: %s' % str(egg)) if egg: pf.debug('EGG') if type(egg) is str: pye = egg.endswith('pye') egg = open(egg).read() else: pye = True egg = ''.join(egg) draw.playScript(egg,pye=True) if os.path.isdir(pf.cfg['workdir']): # Make the workdir the current dir os.chdir(pf.cfg['workdir']) pf.debug("Setting workdir to %s" % pf.cfg['workdir']) else: # Save the current dir as workdir prefMenu.updateSettings({'workdir':os.getcwd(),'Save changes':True}) pf.interactive = True pf.debug("Start main loop") #utils.procInfo('runGUI') #from multiprocessing import Process #p = Process(target=pf.app.exec_) #p.start() #res = p.join() res = pf.app.exec_() pf.debug("Exit main loop with value %s" % res) return res def classify_examples(): m = pf.GUI.menu.item('Examples') #### End pyformex-0.8.6/pyformex/gui/drawable.py0000644000211500021150000012446211705104656020040 0ustar benebene00000000000000# $Id: drawable.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """OpenGL drawing functions and base class for all drawable objects.""" import pyformex as pf from OpenGL import GL,GLU from colors import * from numpy import * from formex import * import geomtools import simple import utils import olist from lib import drawgl def glObjType(nplex): if nplex == 1: objtype = GL.GL_POINTS elif nplex == 2: objtype = GL.GL_LINES elif nplex == 3: objtype = GL.GL_TRIANGLES elif nplex == 4: objtype = GL.GL_QUADS else: objtype = GL.GL_POLYGON return objtype # A list of elements that can be drawn quadratically using NURBS _nurbs_elements = [ 'line3', 'quad4', 'quad8', 'quad9', 'hex20' ] ### Some drawing functions ############################################### def glColor(color,alpha=None): """Set the OpenGL color, possibly with transparency. color is a tuple of 3 or 4 real values. alpha is a single real value. All values are between 0.0 and 1.0 """ if color is not None: if len(color) == 3: if alpha is None: GL.glColor3fv(color) else: GL.glColor4fv(append(color,alpha)) else: GL.glColor4fv(color) # # Though all three functions drawPoints, drawLines and drawPolygons # call the same low level drawgl.draw_polygons function, we keep 3 separate # functions on the higher level, because of special characteristics # of nplex < 3: no computation of normals, marksize (nplex=1) # # DRAWPOINTS should also be modified to accept an (x,e) model # (Yes, it makes sense to create a Point mesh def drawPoints(x,color=None,alpha=1.0,size=None): """Draw a collection of points with default or given size and color. x is a (npoints,3) shaped array of coordinates. If size (float) is given, it specifies the point size. If color is given it is an (npoints,3) array of RGB values. """ x = x.astype(float32).reshape(-1,3) if color is not None: color = resize(color.astype(float32),x.shape) if size: GL.glPointSize(size) x = x.reshape(-1,1,3) drawgl.draw_polygons(x,None,color,alpha,-1) def drawPolygons(x,e,mode,color=None,alpha=1.0,normals=None,objtype=-1): """Draw a collection of polygon elements. This function is like drawPolygons, but the vertices of the polygons are specified by: coords (npts,3) : the coordinates of the points elems (nels,nplex): the connectivity of nels polygons of plexitude nplex color is either None or an RGB color array with shape (3,), (nels,3) or (nels,nplex,3). objtype sets the OpenGL drawing mode. The default (-1) will select the appropriate value depending on the plexitude of the elements: 1: point, 2: line, 3: triangle, 4: quad, >4: polygon. The value can be set to GL.GL_LINE_LOOP to draw the element's circumference independent from the drawing mode. """ pf.debug("drawPolygons") if e is None: nelems = x.shape[0] else: nelems = e.shape[0] n = None if mode.startswith('smooth') and objtype==-1: if normals is None: pf.debug("Computing normals") if mode == 'smooth_avg' and e is not None: n = interpolateNormals(x,e,treshold=pf.cfg['render/avgnormaltreshold']) mode = 'smooth' else: if e is None: n = geomtools.polygonNormals(x) else: n = geomtools.polygonNormals(x[e]) pf.debug("NORMALS:%s" % str(n.shape)) else: try: n = asarray(normals) if not (n.ndim in [2,3] and n.shape[0] == nelems and n.shape[-1] == 3): raise except: raise ValueError,"""Invalid normals specified""" # Sanitize data before calling library function x = x.astype(float32) if e is not None: e = e.astype(int32) if n is not None: n = n.astype(float32) if color is not None: color = color.astype(float32) pf.debug("COLORS:%s" % str(color.shape)) if color.shape[-1] != 3 or ( color.ndim > 1 and color.shape[0] != nelems) : pf.debug("INCOMPATIBLE COLOR SHAPE: %s, while nelems=%s" % (str(color.shape),nelems)) color = None # Call library function if e is None: drawgl.draw_polygons(x,n,color,alpha,objtype) else: drawgl.draw_polygon_elems(x,e,n,color,alpha,objtype) def drawPolyLines(x,e,color): """Draw the circumference of polygons.""" pf.debug("drawPolyLines") drawPolygons(x,e,mode='wireframe',color=color,alpha=1.0,objtype=GL.GL_LINE_LOOP) def drawLines(x,e,color): """Draw straight line segments.""" pf.debug("drawLines") drawPolygons(x,e,mode='wireframe',color=color,alpha=1.0) def drawBezier(x,color=None,objtype=GL.GL_LINE_STRIP,granularity=100): """Draw a collection of Bezier curves. x: (4,3,3) : control points color: (4,) or (4,4): colors """ GL.glMap1f(GL.GL_MAP1_VERTEX_3,0.0,1.0,x) GL.glEnable(GL.GL_MAP1_VERTEX_3) if color is not None and color.shape == (4,4): GL.glMap1f(GL.GL_MAP1_COLOR_4,0.0,1.0,color) GL.glEnable(GL.GL_MAP1_COLOR_4) u = arange(granularity+1) / float(granularity) if color is not None and color.shape == (4,): GL.glColor4fv(color) color = None GL.glBegin(objtype) for ui in u: # For multicolors, this will generate both a color and a vertex GL.glEvalCoord1f(ui) GL.glEnd() GL.glDisable(GL.GL_MAP1_VERTEX_3) if color is not None: GL.glDisable(GL.GL_MAP1_COLOR_4) def drawBezierPoints(x,color=None,granularity=100): drawBezier(x,color=None,objtype=GL.GL_POINTS,granularity=granularity) def drawNurbsCurves(x,knots,color=None,alpha=1.0,samplingTolerance=5.0): """Draw a collection of Nurbs curves. x: (nctrl,ndim) or (ncurve,nctrl,ndim) float array: control points, specifying either a single curve or ncurve curves defined by the same number of control points. ndim can be 3 or 4. If 4, the 4-th coordinate is interpreted as a weight for that point. knots: (nknots) or (ncurve,nknots) float array: knot vector, containing the parameter values to be used in the nurbs definition. Remark that nknots must be larger than nctrl. The order of the curve is nknots-nctrl and the degree of the curve is order-1. If a single knot vector is given, the same is used for all curves. Otherwise, the number of knot vectors must match the number of nurbs curves. If color is given it is an (ncurves,3) array of RGB values. """ nctrl,ndim = x.shape[-2:] nknots = asarray(knots).shape[-1] order = nknots-nctrl if order > 8: import warnings warnings.warn('Nurbs curves of degree > 7 can currently not be drawn! You can create some approximation by evaluating the curve at some points.') return if x.ndim == 2: x = x.reshape(-1,nctrl,ndim) if color is not None and color.ndim == 2: color = color.reshape(-1,nctrl,color.shape[-1]) if color is not None: pf.debug('Coords shape: %s' % str(x.shape)) pf.debug('Color shape: %s' % str(color.shape)) if color.ndim == 1: pf.debug('Single color') elif color.ndim == 2 and color.shape[0] == x.shape[0]: pf.debug('Element color: %s colors' % color.shape[0]) elif color.shape == x.shape[:-1] + (3,): pf.debug('Vertex color: %s colors' % str(color.shape[:-1])) else: raise ValueError,"Number of colors (%s) should equal 1 or the number of curves(%s) or the number of curves * number of vertices" % (color.shape[0],x.shape[0]) pf.debug("Color shape = %s" % str(color.shape)) if color.shape[-1] not in (3,4): raise ValueError,"Expected 3 or 4 color components" if color is not None: pf.debug("Final Color shape = %s" % str(color.shape)) nurb = GLU.gluNewNurbsRenderer() if not nurb: raise RuntimeError,"Could not create a new NURBS renderer" GLU.gluNurbsProperty(nurb,GLU.GLU_SAMPLING_TOLERANCE,samplingTolerance) mode = {3:GL.GL_MAP1_VERTEX_3, 4:GL.GL_MAP1_VERTEX_4}[ndim] if color is not None and color.ndim == 1: # Handle single color pf.debug('Set single color: OK') glColor(color) color = None ki = knots for i,xi in enumerate(x): if color is not None and color.ndim == 2: # Handle element color glColor(color[i]) if knots.ndim > 1: ki = knots[i] GLU.gluBeginCurve(nurb) if color is not None and color.ndim == 3: # Handle vertex color ci = color[i] if ci.shape[-1] == 3: # gluNurbs always wants 4 colors ci = growAxis(ci,1,axis=-1,fill=alpha) GLU.gluNurbsCurve(nurb,ki,ci,GL.GL_MAP1_COLOR_4) GLU.gluNurbsCurve(nurb,ki,xi,mode) GLU.gluEndCurve(nurb) GLU.gluDeleteNurbsRenderer(nurb) def drawQuadraticCurves(x,e=None,color=None,alpha=1.0): """Draw a collection of quadratic curves. The geometry is specified by x or (x,e). x or x[e] is a (nlines,3,3) shaped array of coordinates. For each element a quadratic curve through its 3 points is drawn. This uses the drawNurbsCurves function for the actual drawing. The difference between drawQuadraticCurves(x) and drawNurbsCurves(x) is that in the former, the middle point is laying on the curve, while in the latter case, the middle point defines the tangents in the end points. If color is given it is an (nlines,3) array of RGB values. """ pf.debug("drawQuadraticCurves") if e is None: nelems,nfaces,nplex = x.shape[:3] x = x.reshape(-1,nplex,3) else: nelems,nfaces,nplex = e.shape[:3] e = e.reshape(-1,nplex) if color is not None: if color.ndim == 2: pf.debug("COLOR SHAPE BEFORE MULTIPLEXING %s" % str(color.shape)) color = color_multiplex(color,nfaces) pf.debug("COLOR SHAPE AFTER MULTIPLEXING %s" % str(color.shape)) if color.ndim > 2: color = color.reshape((nelems*nfaces,) + color.shape[-2:]).squeeze() pf.debug("COLOR SHAPE AFTER RESHAPING %s" % str(color.shape)) if e is None: xx = x.copy() else: xx = x[e] #print xx.shape #print xx xx[...,1,:] = 2*xx[...,1,:] - 0.5*(xx[...,0,:] + xx[...,2,:]) #print xx.shape #print xx knots = array([0.,0.,0.,1.,1.,1.]) drawNurbsCurves(xx,knots,color=color,alpha=alpha) _nurbs_renderers_ = [] def drawNurbsSurfaces(x,sknots,tknots,color=None,alpha=1.0,normals='auto',samplingTolerance=20.0): """Draw a collection of Nurbs surfaces. x: (ns,nt,ndim) or (nsurf,ns,nt,ndim) float array: (ns,nt) shaped control points array, specifying either a single surface or nsurf surfaces defined by the same number of control points. ndim can be 3 or 4. If 4, the 4-th coordinate is interpreted as a weight for that point. sknots: (nsk) or (nsurf,nsk) float array: knot vector, containing the parameter values to be used in the s direction of the surface. Remark that nsk must be larger than ns. The order of the surface in s-direction is nsk-ns and the degree of the s-curves is nsk-ns-1. If a single sknot vector is given, the same is used for all surfaces. Otherwise, the number of sknot vectors must match the number of nurbs surfaces. tknots: (ntk) or (nsurf,ntk) float array: knot vector, containing the parameter values to be used in the t direction of the surface. Remark that ntk must be larger than nt. The order of the surface in t-direction is ntk-nt and the degree of the t-curves is ntk-nt-1. If a single sknot vector is given, the same is used for all surfaces. Otherwise, the number of sknot vectors must match the number of nurbs surfaces. If color is given it is an (nsurf,3) array of RGB values. """ import timer t = timer.Timer() ns,nt,ndim = x.shape[-3:] nsk = asarray(sknots).shape[-1] ntk = asarray(tknots).shape[-1] sorder = nsk-ns torder = ntk-nt if sorder > 8 or torder > 8: import warnings warnings.warn('Nurbs surfaces of degree > 7 can currently not be drawn! You can approximate the surface by a lower order surface.') return if x.ndim == 3: x = x.reshape(-1,ns,nt,ndim) if color is not None and color.ndim == 3: color = color.reshape(-1,ns,nt,color.shape[-1]) if color is not None: pf.debug('Coords shape: %s' % str(x.shape)) pf.debug('Color shape: %s' % str(color.shape)) if color.ndim == 1: pf.debug('Single color') elif color.ndim == 2 and color.shape[0] == x.shape[0]: pf.debug('Element color: %s' % color.shape[0]) elif color.shape == x.shape[:-1] + (3,): pf.debug('Vertex color: %s' % str(color.shape[:-1])) else: raise ValueError,"Number of colors (%s) should equal 1 or the number of faces(%s) or the number of faces * number of vertices" % (color.shape[0],x.shape[0]) pf.debug("Color shape = %s" % str(color.shape)) if color.shape[-1] not in (3,4): raise ValueError,"Expected 3 or 4 color components" if normals == 'auto': GL.glEnable(GL.GL_AUTO_NORMAL) else: GL.glDisable(GL.GL_AUTO_NORMAL) # The following uses: # x: (nsurf,ns,nt,4) # sknots: (nsknots) or (nsurf,nsknots) # tknots: (ntknots) or (nsurf,ntknots) # color: None or (4) or (nsurf,4) or (nsurf,ns,nt,4) # samplingTolerance if pf.options.fastnurbs: alpha=0.5 x = x.astype(float32) sknots = sknots.astype(float32) tknots = tknots.astype(float32) if color is not None: color = color.astype(float32) if color.shape[-1] == 3: # gluNurbs always wants 4 colors color = growAxis(color,3,axis=-1,fill=alpha) print "COLOR = %s" % color if color is not None: print color.dtype print color.shape print x.shape # color=array([1.,0.,0.,0.5],dtype=Float) nb = drawgl.draw_nurbs_surfaces(x,sknots,tknots,color,alpha,samplingTolerance) else: nurb = GLU.gluNewNurbsRenderer() if not nurb: raise RuntimeError,"Could not create a new NURBS renderer" GLU.gluNurbsProperty(nurb,GLU.GLU_SAMPLING_TOLERANCE,samplingTolerance) mode = {3:GL.GL_MAP2_VERTEX_3, 4:GL.GL_MAP2_VERTEX_4}[ndim] if color is not None and color.ndim == 1: # Handle single color pf.debug('Set single color: OK') glColor(color) color = None si = sknots ti = tknots for i,xi in enumerate(x): if color is not None and color.ndim == 2: # Handle element color glColor(color[i]) if sknots.ndim > 1: si = sknots[i] if tknots.ndim > 1: ti = tknots[i] GLU.gluBeginSurface(nurb) if color is not None and color.ndim == 4: # Handle vertex color ci = color[i] if ci.shape[-1] == 3: # gluNurbs always wants 4 colors ci = growAxis(ci,1,axis=-1,fill=alpha) GLU.gluNurbsSurface(nurb,si,ti,ci,GL.GL_MAP2_COLOR_4) GLU.gluNurbsSurface(nurb,si,ti,xi,mode) GLU.gluEndSurface(nurb) GLU.gluDeleteNurbsRenderer(nurb) print "drawNurbsSurfaces: %s seconds" % t.seconds() def quad4_quad8(x): """_Convert an array of quad4 surfaces to quad8""" y = roll(x,-1,axis=-2) x5_8 = (x+y)/2 return concatenate([x,x5_8],axis=-2) def quad8_quad9(x): """_Convert an array of quad8 surfaces to quad9""" x9 = x[...,:4,:].sum(axis=-2)/2 - x[...,4:,:].sum(axis=-2)/4 return concatenate([x,x9[...,newaxis,:]],axis=-2) def drawQuadraticSurfaces(x,e,color=None): """Draw a collection of quadratic surfaces. The geometry is specified by x or (x,e). x or x[e] is a (nsurf,nquad,3) shaped array of coordinates, where nquad is either 4,6,8 or 9 For each element a quadratic surface through its nquad points is drawn. This uses the drawNurbsSurfaces function for the actual drawing. The difference between drawQuadraticSurfaces(x) and drawNurbsSurfaces(x) is that in the former, the internal point is laying on the surface, while in the latter case, the middle point defines the tangents in the middle points of the sides. If color is given it is an (nsurf,3) array of RGB values. """ import timer t = timer.Timer() pf.debug("drawQuadraticSurfaces") if e is None: nelems,nfaces,nplex = x.shape[:3] x = x.reshape(-1,nplex,3) else: nelems,nfaces,nplex = e.shape[:3] e = e.reshape(-1,nplex) if color is not None: pf.debug('Color shape: %s' % str(color.shape)) if color.ndim == 2: pf.debug("COLOR SHAPE BEFORE MULTIPLEXING %s" % str(color.shape)) color = color_multiplex(color,nfaces) pf.debug("COLOR SHAPE AFTER MULTIPLEXING %s" % str(color.shape)) if color.ndim > 2: # BV REMOVED squeeze: may break some things color = color.reshape((nelems*nfaces,) + color.shape[-2:])#.squeeze() pf.debug("COLOR SHAPE AFTER RESHAPING %s" % str(color.shape)) if e is None: xx = x.copy() else: xx = x[e] # Draw quad4 as quad4 if xx.shape[-2] == 4: # Bilinear surface knots = array([0.,0.,1.,1.]) xx = xx[...,[0,3,1,2],:] xx = xx.reshape(-1,2,2,xx.shape[-1]) if color is not None and color.ndim > 2: color = color[...,[0,3,1,2],:] color = color.reshape(-1,2,2,color.shape[-1]) drawNurbsSurfaces(xx,knots,knots,color) return # Convert quad8 to quad9 if xx.shape[-2] == 8: xx = quad8_quad9(xx) # Convert quad9 to nurbs node order xx = xx[...,[0,7,3,4,8,6,1,5,2],:] xx = xx.reshape(-1,3,3,xx.shape[-1]) if color is not None and color.ndim > 2: pf.debug("INITIAL COLOR %s" % str(color.shape)) if color.shape[-2] == 8: color = quad8_quad9(color) color = color[...,[0,7,3,4,8,6,1,5,2],:] color = color.reshape(-1,3,3,color.shape[-1]) pf.debug("RESHAPED COLOR %s" % str(color.shape)) xx[...,1,:] = 2*xx[...,1,:] - 0.5*(xx[...,0,:] + xx[...,2,:]) xx[...,1,:,:] = 2*xx[...,1,:,:] - 0.5*(xx[...,0,:,:] + xx[...,2,:,:]) knots = array([0.,0.,0.,1.,1.,1.]) drawNurbsSurfaces(xx,knots,knots,color) print "drawQuadraticSurfaces: %s seconds" % t.seconds() def color_multiplex(color,nparts): """Multiplex a color array over nparts of the elements. This function will repeat the colors in an array a number of times so that all parts of the same element are colored the same. """ s = list(color.shape) s[1:1] = [1] color = color.reshape(*s).repeat(nparts,axis=1) s[1] = nparts # THIS APPEARS NOT TO BE DOING ANYTHING ? return color.reshape(-1,3) def draw_faces(x,e,mode,color=None,alpha=1.0): """Draw a collection of faces. (x,e) are one of: - x is a (nelems,nfaces,nplex,3) shaped coordinates and e is None, - x is a (ncoords,3) shaped coordinates and e is a (nelems,nfaces,nplex) connectivity array. Each of the nfaces sets of nplex points defines a polygon. If color is given it is either an (3,), (nelems,3), (nelems,faces,3) or (nelems,faces,nplex,3) array of RGB values. In the second case, this function will multiplex the colors, so that `nfaces` faces are drawn in the same color. This is e.g. convenient when drawing faces of a solid element. """ if e is None: nelems,nfaces,nplex = x.shape[:3] x = x.reshape(-1,nplex,3) else: nelems,nfaces,nplex = e.shape[:3] e = e.reshape(-1,nplex) if color is not None: if color.ndim == 2: pf.debug("COLOR SHAPE BEFORE MULTIPLEXING %s" % str(color.shape)) color = color_multiplex(color,nfaces) pf.debug("COLOR SHAPE AFTER MULTIPLEXING %s" % str(color.shape)) if color.ndim > 2: color = color.reshape((nelems*nfaces,) + color.shape[-2:]).squeeze() pf.debug("COLOR SHAPE AFTER RESHAPING %s" % str(color.shape)) drawPolygons(x,e,mode,color,alpha) def drawEdges(x,e,edges,eltype,color=None): """Draw the edges of a geometry. This function draws the edges of a geometry collection, usually of a higher dimensionality (i.c. a surface or a volume). The edges are identified by a constant indices into all element vertices. The geometry is specified by x or (x,e) The edges are specified by a list of lists. Each list defines a single edge of the solid, in local vertex numbers (0..nplex-1). If eltype is None, the edges are drawn as polygons. Other allowed values are: 'line3' """ pf.debug("drawEdges") # We may have edges with different plexitudes! # We collect them according to plexitude. # But first convert to a list, so that we can call this function # with an array too (in case of a single plexitude) edges = list(edges) for edg in olist.collectOnLength(edges).itervalues(): fa = asarray(edg) nplex = fa.shape[1] #print fa if e is None: coords = x[:,fa,:] elems = None else: coords = x elems = e[:,fa] pf.debug("COORDS SHAPE: %s" % str(coords.shape)) if elems is not None: pf.debug("ELEMS SHAPE: %s" % str(elems.shape)) if color is not None and color.ndim==3: pf.debug("COLOR SHAPE BEFORE EXTRACTING: %s" % str(color.shape)) # select the colors of the matching points color = color[:,fa,:] pf.debug("COLOR SHAPE AFTER EXTRACTING: %s" % str(color.shape)) if eltype == 'line3': drawQuadraticCurves(coords,elems,color) else: draw_faces(coords,elems,'wireframe',color,1.0) def drawFaces(x,e,faces,eltype,mode,color=None,alpha=1.0): """Draw the faces of a geometry. This function draws the faces of a geometry collection, usually of a higher dimensionality (i.c. a volume). The faces are identified by a constant indices into all element vertices. The geometry is specified by x or (x,e) The faces are specified by a list of lists. Each list defines a single face of the solid, in local vertex numbers (0..nplex-1). The faces are sorted and collected according to their plexitude before drawing them. """ pf.debug("drawFaces") # We may have faces with different plexitudes! # We collect them according to plexitude. # But first convert to a list, so that we can call this function # with an array too (in case of a single plexitude) faces = list(faces) for fac in olist.collectOnLength(faces).itervalues(): fa = asarray(fac) nplex = fa.shape[1] if e is None: coords = x[:,fa,:] elems = None else: coords = x elems = e[:,fa] pf.debug("COORDS SHAPE: %s" % str(coords.shape)) if elems is not None: pf.debug("ELEMS SHAPE: %s" % str(elems.shape)) if color is not None: pf.debug("COLOR SHAPE: %s" % str(color.shape)) # select the colors of the matching points if color.ndim==3: color = color[:,fa,:] pf.debug("COLOR SHAPE AFTER EXTRACTING: %s" % str(color.shape)) if eltype in pf.cfg['draw/quadsurf'] and eltype in _nurbs_elements: #print "USING QUADSURF" drawQuadraticSurfaces(coords,elems,color) else: #print "USING POLYGON" draw_faces(coords,elems,mode,color,alpha) def drawAtPoints(x,mark,color=None): """Draw a copy of a 3D actor mark at all points in x. x is a (npoints,3) shaped array of coordinates. mark is any 3D Actor having a display list attribute. If color is given it is an (npoints,3) array of RGB values. It only makes sense if the mark was created without color! """ for i,xi in enumerate(x): if color is not None: GL.glColor3fv(color[i]) GL.glPushMatrix() GL.glTranslatef(*xi) GL.glCallList(mark) GL.glPopMatrix() def Shape(a): """Return the shape of an array or None""" try: return a.shape except: return None # CANDIDATE FOR C LIBRARY def averageDirection(a,tol=0.5): """Averages vectors that have almost the same direction. a is a 2-dim array where rows are normalized directions. All vectors that have nearly the same direction are replaced by the average of these. tol is a value specifying how close the directions should be in order to be averaged. A vector b is considered to be the same direction as a if its projection on a is longer than the tolerance value. The default The default is to have a direction that is nearly the same. a is a 2-dim array This changes a inplace! """ if a.ndim != 2: raise ValueError,"array should be 2-dimensional!" nrow = a.shape[0] cnt = zeros(nrow,dtype=int32) while cnt.min() == 0: # select unhandled vectors w = where(cnt==0) nw = a[w] # find all those with direction close to the first wok = where(dotpr(nw[0],nw) >= tol) wi = w[0][wok[0]] # replace each with the sum and remember how many we have cnt[wi] = len(wi) a[wi] = a[wi].sum(axis=0) # divide by the sum to get average a /= cnt.reshape(-1,1) return a def averageDirectionsOneNode(d,wi,tol): k = d[wi] misc.averageDirection(k,tol) d[wi] = k # CANDIDATE FOR C LIBRARY def nodalSum2(val,elems,tol): """Compute the nodal sum of values defined on elements. val : (nelems,nplex,nval) values at points of elements. elems : (nelems,nplex) nodal ids of points of elements. work : a work space (unused) The return value is a tuple of two arrays: res: cnt On return each value is replaced with the sum of values at that node. """ print "!!!!nodalSum2!!!!" val[:] = normalize(val) import timer from pyformex.lib import misc t = timer.Timer() nodes = unique(elems) t.reset() [ averageDirectionsOneNode(val,where(elems==i),tol) for i in nodes ] ## for i in nodes: ## wi = where(elems==i) ## k = val[wi] ## #averageDirection(k,tol) ## misc.averageDirection(k,tol) ## val[wi] = k print "TIME %s \n" % t.seconds() def nodalSum(val,elems,avg=False,return_all=True,direction_treshold=None): """Compute the nodal sum of values defined on elements. val is a (nelems,nplex,nval) array of values defined at points of elements. elems is a (nelems,nplex) array with nodal ids of all points of elements. The return value is a (nelems,nplex,nval) array where each value is replaced with the sum of its value at that node. If avg=True, the values are replaced with the average instead. (DOES NOT WORK YET) If return_all==True(default), returns an array with shape (nelems,nplex,3), else, returns an array with shape (maxnodenr+1,3). In the latter case, nodes not occurring in elems will have all zero values. If a direction_tolerance is specified and nval > 1, values will only be summed if their direction is close (projection of one onto the other is higher than the specified tolerance). """ if val.ndim != 3: val.reshape(val.shape+(1,)) if elems.shape != val.shape[:2]: raise RuntimeError,"shape of val and elems does not match" val = val.astype(float32) elems = elems.astype(int32) if val.shape[2] > 1 and direction_treshold is not None: #nodalSum2(val,elems,direction_treshold) print "NEW NODALSUM" val = misc.nodalSum(val,elems,elems.max(),avg,return_all) else: print "NEW NODALSUM" val = misc.nodalSum(val,elems,elems.max(),avg,return_all) print val.shape return val def interpolateNormals(coords,elems,atNodes=False,treshold=None): """Interpolate normals in all points of elems. coords is a (ncoords,3) array of nodal coordinates. elems is an (nel,nplex) array of element connectivity. The default return value is an (nel,nplex,3) array with the averaged unit normals in all points of all elements. If atNodes == True, a more compact array with the unique averages at the nodes is returned. """ n = geomtools.polygonNormals(coords[elems]) n = nodalSum(n,elems,return_all=not atNodes,direction_treshold=treshold) return normalize(n) def drawCube(s,color=[red,cyan,green,magenta,blue,yellow]): """Draws a centered cube with side 2*s and colored faces. Colors are specified in the order [FRONT,BACK,RIGHT,LEFT,TOP,BOTTOM]. """ vertices = [[s,s,s],[-s,s,s],[-s,-s,s],[s,-s,s],[s,s,-s],[-s,s,-s],[-s,-s,-s],[s,-s,-s]] planes = [[0,1,2,3],[4,5,6,7],[0,3,7,4],[1,2,6,5],[0,1,5,4],[3,2,6,7]] GL.glBegin(GL.GL_QUADS) for i in range(6): #glNormal3d(0,1,0); GL.glColor(*color[i]) for j in planes[i]: GL.glVertex3f(*vertices[j]) GL.glEnd() def drawSphere(s,color=cyan,ndiv=8): """Draws a centered sphere with radius s in given color.""" quad = GLU.gluNewQuadric() GLU.gluQuadricNormals(quad, GLU.GLU_SMOOTH) GL.glColor(*color) GLU.gluSphere(quad,s,ndiv,ndiv) def drawGridLines(x0,x1,nx): """Draw a 3D rectangular grid of lines. A grid of lines parallel to the axes is drawn in the domain bounded by the rectangular box [x0,x1]. The grid has nx divisions in the axis directions, thus lines will be drawn at nx[i]+1 positions in direction i. If nx[i] == 0, lines are only drawn for the initial coordinate x0. Thus nx=(0,2,3) results in a grid of 3x4 lines in the plane // (y,z) at coordinate x=x0[0]. """ x0 = asarray(x0) x1 = asarray(x1) nx = asarray(nx) for i in range(3): if nx[i] > 0: axes = (asarray([1,2]) + i) % 3 base = simple.regularGrid(x0[axes],x1[axes],nx[axes]).reshape((-1,2)) x = zeros((base.shape[0],2,3)) x[:,0,axes] = base x[:,1,axes] = base x[:,0,i] = x0[i] x[:,1,i] = x1[i] GL.glBegin(GL.GL_LINES) for p in x.reshape((-1,3)): GL.glVertex3fv(p) GL.glEnd() def drawGridPlanes(x0,x1,nx): """Draw a 3D rectangular grid of planes. A grid of planes parallel to the axes is drawn in the domain bounded by the rectangular box [x0,x1]. The grid has nx divisions in the axis directions, thus planes will be drawn at nx[i]+1 positions in direction i. If nx[i] == 0, planes are only drawn for the initial coordinate x0. Thus nx=(0,2,3) results in a grid of 3x4 planes // x and one plane // (y,z) at coordinate x=x0[0]. """ x0 = asarray(x0) x1 = asarray(x1) nx = asarray(nx) for i in range(3): axes = (asarray([1,2]) + i) % 3 if all(nx[axes] > 0): j,k = axes base = simple.regularGrid(x0[i],x1[i],nx[i]).ravel() x = zeros((base.shape[0],4,3)) corners = array([x0[axes],[x1[j],x0[k]],x1[axes],[x0[j],x1[k]]]) for j in range(4): x[:,j,i] = base x[:,:,axes] = corners GL.glBegin(GL.GL_QUADS) for p in x.reshape((-1,3)): GL.glVertex3fv(p) GL.glEnd() ######################## Picking functions ######################## def pickPolygons(x,e=None,objtype=-1): """Mimics drawPolygons for picking purposes.""" x = x.astype(float32) if e is not None: e = e.astype(int32) if e is None: drawgl.pick_polygons(x,objtype) else: drawgl.pick_polygon_elems(x,e,objtype) def pickPolygonEdges(x,e,edg): warning("pickPolygonEdges IS NOT IMPLEMENTED YET!") def pickPoints(x): x = x.reshape((-1,1,3)) pickPolygons(x) ### Settings ############################################### # # These are not intended for users but to sanitize user input # def saneLineWidth(linewidth): """Return a sane value for the line width. A sane value is one that will be usable by the draw method. It can be either of the following: - a float value indicating the line width to be set by draw() - None: indicating that the default line width is to be used The line width is used in wireframe mode if plex > 1 and in rendering mode if plex==2. """ if linewidth is not None: linewidth = float(linewidth) return linewidth def saneLineStipple(stipple): """Return a sane line stipple tuple. A line stipple tuple is a tuple (factor,pattern) where pattern defines which pixels are on or off (maximum 16 bits), factor is a multiplier for each bit. """ try: stipple = map(int,stipple) except: stipple = None return stipple def saneColor(color=None): """Return a sane color array derived from the input color. A sane color is one that will be usable by the draw method. The input value of color can be either of the following: - None: indicates that the default color will be used, - a single color value in a format accepted by colors.GLColor, - a tuple or list of such colors, - an (3,) shaped array of RGB values, ranging from 0.0 to 1.0, - an (n,3) shaped array of RGB values, - an (n,) shaped array of integer color indices. The return value is one of the following: - None, indicating no color (current color will be used), - a float array with shape (3,), indicating a single color, - a float array with shape (n,3), holding a collection of colors, - an integer array with shape (n,), holding color index values. !! Note that a single color can not be specified as integer RGB values. A single list of integers will be interpreted as a color index ! Turning the single color into a list with one item will work though. [[ 0, 0, 255 ]] will be the same as [ 'blue' ], while [ 0,0,255 ] would be a color index with 3 values. """ if color is None: # no color: use canvas color return None # detect color index try: c = asarray(color) if c.dtype.kind == 'i': # We have a color index return c except: pass # not a color index: it must be colors try: color = GLColor(color) except ValueError: try: color = map(GLColor,color) except ValueError: pass # Convert to array try: # REMOVED THE SQUEEZE: MAY BREAK SOME THINGS !!! color = asarray(color)#.squeeze() if color.dtype.kind == 'f' and color.shape[-1] == 3: # Looks like we have a sane color array return color.astype(float32) except: pass return None def saneColorArray(color,shape): """Makes sure the shape of the color array is compatible with shape. shape is an (nelems,nplex) tuple A compatible color.shape is equal to shape or has either or both of its dimensions equal to 1. Compatibility is enforced in the following way: - if color.shape[1] != nplex and color.shape[1] != 1: take out first plane in direction 1 - if color.shape[0] != nelems and color.shape[0] != 1: repeat the plane in direction 0 nelems times """ color = asarray(color) if color.ndim == 1: return color if color.ndim == 3: if color.shape[1] > 1 and color.shape[1] != shape[1]: color = color[:,0] if color.shape[0] > 1 and color.shape[0] != shape[0]: color = resize(color,(shape[0],color.shape[1])) return color def saneColorSet(color=None,colormap=None,shape=(1,),canvas=None): """Return a sane set of colors. A sane set of colors is one that guarantees correct use by the draw functions. This means either - no color (None) - a single color - at least as many colors as the shape argument specifies - a color index and a color map with enough colors to satisfy the index. The return value is a tuple color,colormap. colormap will be None, unless color is an integer array, meaning a color index. """ if type(shape) == int: # make sure we get a tuple shape = (shape,) color = saneColor(color) if color is not None: pf.debug("SANECOLORSET: color %s, shape %s" % (color.shape,shape)) if color.dtype.kind == 'i': ncolors = color.max()+1 if colormap is None: if canvas: colormap = canvas.settings.colormap else: colormap = pf.cfg['canvas/colormap'] colormap = saneColor(colormap) colormap = saneColorArray(colormap,(ncolors,)) else: color = saneColorArray(color,shape) colormap = None pf.debug("SANECOLORSET RESULT: %s" % str(color.shape)) return color,colormap ### Drawable Objects ############################################### class Drawable(object): """A Drawable is anything that can be drawn on the OpenGL Canvas. This defines the interface for all drawbale objects, but does not implement any drawable objects. Drawable objects should be instantiated from the derived classes. Currently, we have the following derived classes: Actor: a 3-D object positioned and oriented in the 3D scene. Defined in actors.py. Mark: an object positioned in 3D scene but not undergoing the camera axis rotations and translations. It will always appear the same to the viewer, but will move over the screen according to its 3D position. Defined in marks.py. Decor: an object drawn in 2D viewport coordinates. It will unchangeably stick on the viewport until removed. Defined in decors.py. A Drawable subclass should minimally reimplement the following methods: bbox(): return the actors bounding box. nelems(): return the number of elements of the actor. drawGL(mode): to draw the object. Takes a mode argument so the drawing function can act differently depending on the rendering mode. There are currently 5 modes: wireframe, flat, smooth, flatwire, smoothwire. drawGL should only contain OpenGL calls that are allowed inside a display list. This may include calling the display list of another actor but NOT creating a new display list. """ def __init__(self,nolight=False,ontop=False,**kargs): self.list = None self.mode = None # stores mode of self.list: wireframe/smooth/flat self.trans = False self.nolight = nolight self.ontop = ontop self.extra = [] # list of dependent Drawables def drawGL(self,**kargs): """Perform the OpenGL drawing functions to display the actor.""" pass def pickGL(self,**kargs): """Mimick the OpenGL drawing functions to pick (from) the actor.""" pass def redraw(self,**kargs): self.draw(**kargs) def draw(self,**kargs): self.prepare_list(**kargs) self.use_list() def prepare_list(self,**kargs): if 'mode' in kargs: mode = kargs['mode'] else: canvas = kargs.get('canvas',pf.canvas) mode = canvas.rendermode if mode.endswith('wire'): mode = mode[:-4] if self.list is None or mode != self.mode: kargs['mode'] = mode self.delete_list() self.list = self.create_list(**kargs) self.mode = mode def use_list(self): if self.list: GL.glCallList(self.list) for i in self.extra: i.use_list() def create_list(self,**kargs): displist = GL.glGenLists(1) GL.glNewList(displist,GL.GL_COMPILE) ok = False try: if self.nolight: GL.glDisable(GL.GL_LIGHTING) if self.ontop: GL.glDepthFunc(GL.GL_ALWAYS) self.drawGL(**kargs) ok = True finally: if not ok: pf.debug("Error while creating a display list") displist = None GL.glEndList() return displist def delete_list(self): if self.list: GL.glDeleteLists(self.list,1) self.list = None def setLineWidth(self,linewidth): """Set the linewidth of the Actor.""" self.linewidth = saneLineWidth(linewidth) def setLineStipple(self,linestipple): """Set the linewidth of the Actor.""" self.linestipple = saneLineStipple(linestipple) def setColor(self,color=None,colormap=None,ncolors=1): """Set the color of the Drawable.""" self.color,self.colormap = saneColorSet(color,colormap,shape=(ncolors,)) ### End pyformex-0.8.6/pyformex/gui/colorscale.py0000644000211500021150000002024111705104656020373 0ustar benebene00000000000000#!/usr/bin/env python # $Id: colorscale.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Color mapping of a range of values.""" from colors import * from utils import stuur # predefined color palettes Palette = { 'RAINBOW' : [[-2.,0.,2.],[0.,2.,0.],[2.,0.,-2.]], 'IRAINBOW' : [[2.,0.,-2.],[0.,2.,0.],[-2.,0.,2.]], 'RGB' : [ red,green,blue ], 'BGR' : [ blue,green,red ], 'RWB' : [ red,white,blue ], 'BWR' : [ blue,white,red ], 'RWG' : [ red,white,green ], 'GWR' : [ green,white,red ], 'GWB' : [ green,white,blue ], 'BWG' : [ blue,white,green ], 'BW' : [ black,None,white ], 'WB' : [ white,grey(0.5),black ], } class ColorScale(object): """Mapping floating point values into colors. A colorscale maps floating point values within a certain range into colors and can be used to provide visual representation of numerical values. This is e.g. quite useful in Finite Element postprocessing (see the postproc plugin). The ColorLegend class provides a way to make the ColorScale visible on the canvas. """ def __init__(self,palet='RAINBOW',minval=0.,maxval=1.,midval=None,exp=1.0,exp2=None): """Create a colorscale to map a range of values into colors. The values range from minval to maxval (default 0.0..1.0). A midval may be specified to set the value corresponding to the midle of the color scale. It defaults to the middle value of the range. It is especially useful if the range extends over negative and positive values to set 0.0 as the middle value. The palet is a list of 3 colors, corresponding to the minval, midval and maxval respectively. The middle color may be given as None, in which case it will be set to the middle color between the first and last. The Palette variable provides some useful predefined palets. You will hardly ever need to define your own palets. The mapping function between numerical and color values is by default linear. Nonlinear mappings can be obtained by specifying an exponent 'exp' different from 1.0. Mapping is done with the 'stuur' function from the 'utils' module. If 2 exponents are given, mapping is done independently with exp in the range minval..midval and with exp2 in the range midval..maxval. """ if type(palet) == str: self.palet = Palette.get(palet.upper(),Palette['RGB']) else: self.palet = palet if self.palet[1] == None: self.palet[1] = [ 0.5*(p+q) for p,q in zip(self.palet[0],self.palet[2]) ] self.xmin = minval self.xmax = maxval if midval == None: self.x0 = 0.5*(minval+maxval) else: self.x0 = midval self.exp = exp self.exp2 = exp2 def scale(self,val): """Scale a value to the range -1...1. If the ColorScale has only one exponent, values in the range mival..maxval are scaled to the range -1..+1. If two exponents were specified, scaling is done independently in the intervals minval..midval and midval..maxval, mapped resp. using exp2 and exp onto the intevals -1..0 and 0..1. """ if self.exp2 == None: return stuur(val,[self.xmin,self.x0,self.xmax],[-1.,0.,1.],self.exp) if val < self.x0: return stuur(val,[self.xmin,(self.x0+self.xmin)/2,self.x0],[-1.,-0.5,0.],self.exp2) else: return stuur(val,[self.x0,(self.x0+self.xmax)/2,self.xmax],[0.,0.5,1.0],1./self.exp) def color(self,val): """Return the color representing a value val. The returned color is a tuple of three RGB values in the range 0-1. The color is obtained by first scaling the value to the -1..1 range using the 'scale' method, and then using that result to pick a color value from the palet. A palet specifies the three colors corresponding to the -1, 0 and 1 values. """ x = self.scale(val) c0 = self.palet[1] if x == 0.: return c0 if x < 0: c1 = self.palet[0] x = -x else: c1 = self.palet[2] return tuple( [ (1.-x)*p + x*q for p,q in zip(c0,c1) ] ) class ColorLegend(object): """A colorlegend divides a in a number of subranges. Parameters: - `colorscale`: a :class:`ColorScale` instance - `n`: a positive integer For a :class:`ColorScale` without ``midval``, the full range is divided in ``n`` subranges; for a scale with ``midval``, each of the two ranges is divided in ``n/2`` subranges. In each case the legend has ``n`` subranges limited by ``n+1`` values. The ``n`` colors of the legend correspond to the middle value of each subrange. """ def __init__(self,colorscale,n): """Initialize the color legend.""" self.cs = colorscale n = int(n) r = float(n)/2 m = (n+1)/2 vals = [ (self.cs.xmin*(r-i)+self.cs.x0*i)/r for i in range(m) ] val2 = [ (self.cs.xmax*(r-i)+self.cs.x0*i)/r for i in range(m) ] val2.reverse() if n % 2 == 0: vals += [ self.cs.x0 ] vals += val2 midvals = [ (vals[i] + vals[i+1])/2 for i in range(n) ] self.limits = vals self.colors = map(self.cs.color,midvals) self.underflowcolor = None self.overflowcolor = None def overflow(self,oflow=None): """Raise a runtime error if oflow == None, else return oflow.""" if oflow==None: raise RuntimeError, "Value outside colorscale range" else: return oflow def color(self,val): """Return the color representing a value val. The color is that of the subrange holding the value. If the value matches a subrange limit, the lower range color is returned. If the value falls outside the colorscale range, a runtime error is raised, unless the corresponding underflowcolor or overflowcolor attribute has been set, in which case this attirbute is returned. Though these attributes can be set to any not None value, it will usually be set to some color value, that will be used to show overflow values. The returned color is a tuple of three RGB values in the range 0-1. """ i = 0 while self.limits[i] < val: i += 1 if i >= len(self.limits): return self.overflow(self.overflowcolor) if i==0: return self.overflow(self.underflowcolor) return self.colors[i-1] if __name__ == "__main__": for palet in [ 'RGB', 'BW' ]: CS = ColorScale(palet,-50.,250.) for x in [ -50+10.*i for i in range(31) ]: print(x,": ",CS.color(x)) CS = ColorScale('RGB',-50.,250.,0.) CL = ColorLegend(CS,5) print(CL.limits) for x in [ -45+10.*i for i in range(30) ]: print(x,": ",CL.color(x)) CL.underflowcolor = black CL.overflowcolor = white print(CL.color(-55)) print(CL.color(255)) # End pyformex-0.8.6/pyformex/gui/__init__.py0000644000211500021150000000252111705104656020005 0ustar benebene00000000000000# $Id: __init__.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """pyFormex GUI module initialization. This file is mainly here to flag the gui directory as a Python package. Global variables for the gui package may be defined here. """ # End pyformex-0.8.6/pyformex/gui/actors.py0000644000211500021150000006101611705104656017545 0ustar benebene00000000000000# $Id: actors.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """OpenGL actors for populating the 3D scene.""" import pyformex as pf import sys from OpenGL import GL,GLU from drawable import * from formex import * from elements import elementType,elementName from mesh import Mesh from plugins.trisurface import TriSurface from plugins.nurbs import NurbsCurve,NurbsSurface from marks import TextMark import timer ### Actors ############################################### class Actor(Drawable): """An Actor is anything that can be drawn in an OpenGL 3D Scene. The visualisation of the Scene Actors is dependent on camera position and angles, clipping planes, rendering mode and lighting. An Actor subclass should minimally reimplement the following methods: - `bbox()`: return the actors bounding box. - `drawGL(mode)`: to draw the actor. Takes a mode argument so the drawing function can act differently depending on the mode. There are currently 5 modes: wireframe, flat, smooth, flatwire, smoothwire. drawGL should only contain OpenGL calls that are allowed inside a display list. This may include calling the display list of another actor but *not* creating a new display list. The interactive picking functionality requires the following methods, for which we porvide do-nothing defaults here: - `npoints()`: - `nelems()`: - `pickGL()`: """ def __init__(self,**kargs): Drawable.__init__(self,**kargs) def bbox(self): """Default implementation for bbox().""" try: return self.coords.bbox() except: raise ValueError,"No bbox() defined and no coords attribute" def npoints(self): return 0 def nelems(self): return 0 def pickGL(self,mode): pass class TranslatedActor(Actor): """An Actor translated to another position.""" def __init__(self,A,trl=(0.,0.,0.),**kargs): Actor.__init__(self,**kargs) self.actor = A self.trans = A.trans self.trl = asarray(trl) def bbox(self): return self.actor.bbox() + self.trl def redraw(self,mode,color=None): self.actor.redraw(mode=mode,color=color) Drawable.redraw(self,mode=mode,color=color) def drawGL(self,**kargs): GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPushMatrix() GL.glTranslate(*self.trl) self.actor.use_list() GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPopMatrix() class RotatedActor(Actor): """An Actor rotated to another position.""" def __init__(self,A,rot=(1.,0.,0.),twist=0.0,**kargs): """Created a new rotated actor. If rot is an array with shape (3,), the rotation is specified by the direction of the local 0 axis of the actor. If rot is an array with shape (4,4), the rotation is specified by the direction of the local 0, 1 and 2 axes of the actor. """ Actor.__init__(self,**kargs) self.actor = A self.trans = A.trans if shape(rot) == (3,): self.rot = rotMatrix(rot,n=4) else: self.rot = rot def bbox(self): return self.actor.bbox() # TODO : rotate the bbox ! def redraw(self,mode,color=None): self.actor.redraw(mode=mode,color=color) Drawable.redraw(self,mode=mode,color=color) def drawGL(self,**kargs): GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPushMatrix() GL.glMultMatrixf(self.rot) self.actor.use_list() GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPopMatrix() class CubeActor(Actor): """An OpenGL actor with cubic shape and 6 colored sides.""" def __init__(self,size=1.0,color=[red,cyan,green,magenta,blue,yellow],**kargs): Actor.__init__(self,**kargs) self.size = size self.color = color def bbox(self): return (0.5 * self.size) * array([[-1.,-1.,-1.],[1.,1.,1.]]) def drawGL(self,**kargs): """Draw the cube.""" drawCube(self.size,self.color) class SphereActor(Actor): """An OpenGL actor representing a sphere.""" def __init__(self,size=1.0,color=None,**kargs): Actor.__init__(self) self.size = size self.color = color def bbox(self): return (0.5 * self.size) * array([[-1.,-1.,-1.],[1.,1.,1.]]) def drawGL(self,**kargs): """Draw the cube.""" drawSphere(self.size,self.color) # This could be subclassed from GridActor class BboxActor(Actor): """Draws a bbox.""" def __init__(self,bbox,color=None,linewidth=None,**kargs): Actor.__init__(self,**kargs) self.color = color self.linewidth = linewidth self.bb = bbox Hex8 = elementType('hex8') self.vertices = Hex8.vertices * (bbox[1]-bbox[0]) + bbox[0] self.edges = Hex8.edges self.facets = Hex8.faces def bbox(self): return self.bb def drawGL(self,**kargs): """Always draws a wireframe model of the bbox.""" if self.linewidth is not None: GL.glLineWidth(self.linewidth) drawLines(self.vertices,self.edges,self.color) class AxesActor(Actor): """An actor showing the three axes of a CoordinateSystem. If no CoordinateSystem is specified, the global coordinate system is drawn. The default actor consists of three colored lines of unit length along the unit vectors of the axes and three colored triangles representing the coordinate planes. This can be modified by the following parameters: size: scale factor for the unit vectors. color: a set of three colors to use for x,y,z axes. colored_axes = False: draw black axes. draw_planes = False: do not draw the coordinate planes. """ def __init__(self,cs=None,size=1.0,color=[red,green,blue],colored_axes=True,draw_planes=False,linewidth=None,**kargs): Actor.__init__(self,**kargs) if cs is None: cs = CoordinateSystem() self.cs = cs self.color = saneColorArray(saneColor(color),(3,1)) self.colored_axes = colored_axes self.draw_planes = draw_planes self.linewidth = linewidth self.setSize(size) def bbox(self): origin = self.cs[3] return array([origin-self.size,origin+self.size]) def setSize(self,size): size = float(size) if size > 0.0: self.size = size self.delete_list() def drawGL(self,**kargs): """Draw the axes.""" x = self.cs.trl(-self.cs[3]).scale(self.size).trl(self.cs[3]) if self.draw_planes: e = array([[3,1,2],[3,2,0],[3,0,1]]) drawPolygons(x,e,'flat') e = array([[3,0],[3,1],[3,2]]) if self.colored_axes: c = self.color else: c = None if self.linewidth: GL.glLineWidth(self.linewidth) drawLines(x,e,c) class GridActor(Actor): """Draws a (set of) grid(s) in one of the coordinate planes.""" def __init__(self,nx=(1,1,1),ox=(0.0,0.0,0.0),dx=(1.0,1.0,1.0),linecolor=black,linewidth=None,planecolor=white,alpha=0.2,lines=True,planes=True,**kargs): Actor.__init__(self,**kargs) self.linecolor = saneColor(linecolor) self.planecolor = saneColor(planecolor) self.linewidth = linewidth self.alpha = alpha self.trans = True self.lines = lines self.planes = planes self.nx = asarray(nx) self.x0 = asarray(ox) self.x1 = self.x0 + self.nx * asarray(dx) def bbox(self): return array([self.x0,self.x1]) def drawGL(self,**kargs): """Draw the grid.""" if self.lines: if self.linewidth: GL.glLineWidth(self.linewidth) glColor(self.linecolor) drawGridLines(self.x0,self.x1,self.nx) if self.planes: glColor(self.planecolor,self.alpha) drawGridPlanes(self.x0,self.x1,self.nx) class CoordPlaneActor(Actor): """Draws a set of 3 coordinate planes.""" def __init__(self,nx=(1,1,1),ox=(0.0,0.0,0.0),dx=(1.0,1.0,1.0),linecolor=black,linewidth=None,planecolor=white,alpha=0.5,lines=True,planes=True,**kargs): Actor.__init__(self,**kargs) self.linecolor = saneColor(linecolor) self.planecolor = saneColor(planecolor) self.linewidth = linewidth self.alpha = alpha self.trans = True self.lines = lines self.planes = planes self.nx = asarray(nx) self.x0 = asarray(ox) self.x1 = self.x0 + self.nx * asarray(dx) def bbox(self): return array([self.x0,self.x1]) def drawGL(self,**kargs): """Draw the grid.""" for i in range(3): nx = self.nx.copy() nx[i] = 0 if self.lines: if self.linewidth: GL.glLineWidth(self.linewidth) glColor(self.linecolor) drawGridLines(self.x0,self.x1,nx) if self.planes: glColor(self.planecolor,self.alpha) drawGridPlanes(self.x0,self.x1,nx) class PlaneActor(Actor): """A plane in a 3D scene.""" def __init__(self,nx=(2,2,2),ox=(0.,0.,0.),size=((0.0,1.0,1.0),(0.0,1.0,1.0)),linecolor=black,linewidth=None,planecolor=white,alpha=0.5,lines=True,planes=True,**kargs): """A plane perpendicular to the x-axis at the origin.""" Actor.__init__(self,**kargs) self.linecolor = saneColor(linecolor) self.planecolor = saneColor(planecolor) self.linewidth = linewidth self.alpha = alpha self.trans = True self.lines = lines self.planes = planes self.nx = asarray(nx) ox = asarray(ox) sz = asarray(size) self.x0,self.x1 = ox-sz[0], ox+sz[1] def bbox(self): return array([self.x0,self.x1]) def drawGL(self,**kargs): """Draw the grid.""" for i in range(3): nx = self.nx.copy() nx[i] = 0 if self.lines: if self.linewidth is not None: GL.glLineWidth(self.linewidth) color = self.linecolor if color is None: color = canvas.settings.fgcolor glColor(color) drawGridLines(self.x0,self.x1,nx) if self.planes: glColor(self.planecolor,self.alpha) drawGridPlanes(self.x0,self.x1,nx) ########################################################################### class GeomActor(Actor): """An OpenGL actor representing a geometrical model. The model can either be in Formex or Mesh format. """ mark = False def __init__(self,data,elems=None,eltype=None,color=None,colormap=None,bkcolor=None,bkcolormap=None,alpha=1.0,mode=None,linewidth=None,linestipple=None,marksize=None,**kargs): """Create a geometry actor. The geometry is either in Formex model: a coordinate block with shape (nelems,nplex,3), or in Mesh format: a coordinate block with shape (npoints,3) and an elems block with shape (nelems,nplex). In both cases, an eltype may be specified if the default is not suitable. Default eltypes are Point for plexitude 1, Line for plexitude 2 and Triangle for plexitude 3 and Polygon for all higher plexitudes. Actually, Triangle is just a special case of Polygon. Here is a list of possible eltype values (which should match the corresponding plexitude): ========= =========== ============================================ plexitude `eltype` element type ========= =========== ============================================ 4 ``tet4`` a tetrahedron 6 ``wedge6`` a wedge (triangular prism) 8 ``hex8`` a hexahedron ========= =========== ============================================ The colors argument specifies a list of OpenGL colors for each of the property values in the Formex. If the list has less values than the PropSet, it is wrapped around. It can also be a single OpenGL color, which will be used for all elements. For surface type elements, a bkcolor color can be given for the backside of the surface. Default will be the same as the front color. The user can specify a linewidth to be used when drawing in wireframe mode. """ Actor.__init__(self,**kargs) # Store a reference to the drawn object self.object = data if isinstance(data,GeomActor) or isinstance(data,Mesh): self.coords = data.coords self.elems = data.elems self.eltype = data.eltype elif isinstance(data,Formex): self.coords = data.coords self.elems = None self.eltype = data.eltype else: self.coords = data self.elems = elems self.eltype = eltype self.mode = mode self.setLineWidth(linewidth) self.setLineStipple(linestipple) self.setColor(color,colormap) self.setBkColor(bkcolor,bkcolormap) self.setAlpha(alpha) self.marksize = marksize #print "GEOMACTOR: %s -> %s" % (color.shape,self.color.shape) def getType(self): return self.object.__class__ def nplex(self): return self.shape()[1] def nelems(self): return self.shape()[0] def shape(self): if self.elems is None: return self.coords.shape[:-1] else: return self.elems.shape def npoints(self): return self.vertices().shape[0] def nedges(self): # This is needed to be able to pick edges!! try: return self.object.nedges() except: try: return self.object.getEdges().shape[0] except: return 0 def vertices(self): """Return the vertives as a 2-dim array.""" return self.coords.reshape(-1,3) def setColor(self,color,colormap=None): """Set the color of the Actor.""" self.color,self.colormap = saneColorSet(color,colormap,self.shape()) def setBkColor(self,color,colormap=None): """Set the backside color of the Actor.""" self.bkcolor,self.bkcolormap = saneColorSet(color,colormap,self.shape()) def setAlpha(self,alpha): """Set the Actors alpha value.""" self.alpha = float(alpha) self.trans = self.alpha < 1.0 def bbox(self): return self.coords.bbox() def draw(self,**kargs): if 'mode' in kargs: mode = kargs['mode'] else: canvas = kargs.get('canvas',pf.canvas) mode = canvas.rendermode if mode.endswith('wire'): if not hasattr(self,'wire'): import copy wire = copy.copy(self) wire.nolight = True wire.ontop = False # True will make objects transparent for edges wire.list = None Drawable.prepare_list(wire,mode='wireframe',color=asarray(black)) self.wire = wire # Add the existing wire to the extra list, and then draw w/o wire if self.wire not in self.extra: self.extra.append(self.wire) # AVOID RECURSION self.wire.extra = [] mode = mode[:-4] else: if hasattr(self,'wire') and self.wire in self.extra: self.extra.remove(self.wire) if self.list is None or mode != self.mode: kargs['mode'] = mode self.delete_list() self.list = self.create_list(**kargs) self.mode = mode self.use_list() def drawGL(self,canvas=None,mode=None,color=None,**kargs): """Draw the geometry on the specified canvas. The drawing parameters not provided by the Actor itself, are derived from the canvas defaults. mode and color can be overridden for the sole purpose of allowing the recursive use for modes ending on 'wire' ('smoothwire' or 'flatwire'). In these cases, two drawing operations are done: one with mode='wireframe' and color=black, and one with mode=mode[:-4]. """ from canvas import glLineStipple if canvas is None: canvas = pf.canvas if mode is None: mode = self.mode if mode is None: mode = canvas.rendermode if mode.endswith('wire'): mode = mode[:-4] ############# set drawing attributes ######### alpha = self.alpha if alpha is None: alpha = canvas.settings.alpha if color is None: color,colormap = self.color,self.colormap bkcolor, bkcolormap = self.bkcolor,self.bkcolormap else: # THIS OPTION IS ONLY MEANT FOR OVERRIDING THE COLOR # WITH THE EDGECOLOR IN ..wire DRAWING MODES # SO NO NEED TO SET bkcolor color,colormap = saneColor(color),None bkcolor, bkcolormap = None,None # convert color index to full colors if color is not None and color.dtype.kind == 'i': color = colormap[color] if bkcolor is not None and bkcolor.dtype.kind == 'i': bkcolor = bkcolormap[bkcolor] linewidth = self.linewidth if linewidth is None: linewidth = canvas.settings.linewidth if self.linewidth is not None: GL.glLineWidth(self.linewidth) if self.linestipple is not None: glLineStipple(*self.linestipple) if mode.startswith('smooth'): if hasattr(self,'specular'): fill_mode = GL.GL_FRONT import colors if color is not None: spec = color * self.specular# * pf.canvas.specular spec = append(spec,1.) else: spec = colors.GREY(self.specular)# * pf.canvas.specular GL.glMaterialfv(fill_mode,GL.GL_SPECULAR,spec) GL.glMaterialfv(fill_mode,GL.GL_EMISSION,spec) GL.glMaterialfv(fill_mode,GL.GL_SHININESS,self.specular) ################## draw the geometry ################# nplex = self.nplex() if nplex == 1: marksize = self.marksize if marksize is None: marksize = canvas.settings.pointsize # THIS SHOULD GO INTO drawPoints if self.elems is None: coords = self.coords else: coords = self.coords[self.elems] drawPoints(coords,color,alpha,marksize) elif nplex == 2: drawLines(self.coords,self.elems,color) # beware: some Formex eltypes are strings and may not # represent a valid Mesh elementType # THis is only here for Formex type. # We can probably remove it if we avoid eltype 'curve' elif nplex == 3 and self.eltype in ['curve','line3']: drawQuadraticCurves(self.coords,self.elems,color) elif self.eltype is None: # polygons if mode=='wireframe' : drawPolyLines(self.coords,self.elems,color) else: if bkcolor is not None: GL.glEnable(GL.GL_CULL_FACE) GL.glCullFace(GL.GL_BACK) drawPolygons(self.coords,self.elems,mode,color,alpha) if bkcolor is not None: GL.glCullFace(GL.GL_FRONT) drawPolygons(self.coords,self.elems,mode,bkcolor,alpha) GL.glDisable(GL.GL_CULL_FACE) else: el = elementType(self.eltype) if mode=='wireframe' or el.ndim < 2: for edges in el.getDrawEdges(el.name() in pf.cfg['draw/quadline']): drawEdges(self.coords,self.elems,edges,edges.eltype,color) else: for faces in el.getDrawFaces(el.name() in pf.cfg['draw/quadsurf']): if bkcolor is not None: # Enable drawing front and back with different colors GL.glEnable(GL.GL_CULL_FACE) GL.glCullFace(GL.GL_BACK) # Draw the front sides drawFaces(self.coords,self.elems,faces,faces.eltype,mode,color,alpha) if bkcolor is not None: # Draw the back sides GL.glCullFace(GL.GL_FRONT) drawFaces(self.coords,self.elems,faces,faces.eltype,mode,bkcolor,alpha) GL.glDisable(GL.GL_CULL_FACE) def pickGL(self,mode): """ Allow picking of parts of the actor. mode can be 'element', 'edge' or 'point' """ if mode == 'element': pickPolygons(self.coords,self.elems) elif mode == 'edge': edges = self.object.getEdges() if edges is not None: pickPolygons(self.coords,edges) elif mode == 'point': pickPoints(self.coords) def select(self,sel): """Return a GeomActor with a selection of this actor's elements Currently, the resulting Actor will not inherit the properties of its parent, but the eltype will be retained. """ # This selection should be reworked to allow edge and point selections if self.elems is None: x = self.coords[sel] e = self.elems else: x = self.coords e = self.elems[sel] return GeomActor(x,e,eltype=self.eltype) class NurbsActor(Actor): def __init__(self,data,color=None,colormap=None,bkcolor=None,bkcolormap=None,**kargs): from gui.drawable import saneColor Actor.__init__(self,**kargs) self.object = data self.setColor(color,colormap) self.setBkColor(bkcolor,bkcolormap) if isinstance(self.object,NurbsCurve): self.samplingTolerance = 5.0 elif isinstance(self.object,NurbsSurface): self.samplingTolerance = 10.0 self.list = None def shape(self): return self.object.coords.shape[:-1] def setColor(self,color,colormap=None): """Set the color of the Actor.""" self.color,self.colormap = saneColorSet(color,colormap,self.shape()) def setBkColor(self,color,colormap=None): """Set the backside color of the Actor.""" self.bkcolor,self.bkcolormap = saneColorSet(color,colormap,self.shape()) def bbox(self): return self.object.bbox() def drawGL(self,canvas=None,**kargs): if canvas is None: canvas = pf.canvas mode = canvas.rendermode if mode.endswith('wire'): mode = mode[:-4] if isinstance(self.object,NurbsCurve): drawNurbsCurves(self.object.coords,self.object.knots,color=self.color,samplingTolerance=self.samplingTolerance) elif isinstance(self.object,NurbsSurface): if mode == 'wireframe': pass else: drawNurbsSurfaces(self.object.coords,self.object.vknots,self.object.uknots,color=self.color,normals='auto',samplingTolerance=self.samplingTolerance) # End pyformex-0.8.6/pyformex/gui/signals.py0000644000211500021150000000425011705104656017707 0ustar benebene00000000000000# $Id: signals.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """signals.py: Definition of our own signals used in the GUI communication. Signals are treated by the normal QT4 machine. They can be emitted from anywhere, causing attached functions to be executed. """ import pyformex as pf from PyQt4.QtCore import Qt,QObject,SIGNAL,QThread # signals CANCEL = SIGNAL("Cancel") # cancel the operation, undoing it DONE = SIGNAL("Done") # accept and finish the operation REDRAW = SIGNAL("Redraw") # redraw a preview state WAKEUP = SIGNAL("Wakeup") # wake up from a sleep state TIMEOUT = SIGNAL("Timeout") # terminate what was going on SAVE = SIGNAL("Save") # keypress_signal = { Qt.Key_F2: SAVE, } def onSignal(signal,function,widget=None): """Connect a function to a signal""" if widget is None: widget = pf.GUI QObject.connect(widget,signal,function) def offSignal(signal,function,widget=None): """Disconnect a function from a signal""" if widget is None: widget = pf.GUI QObject.disconnect(widget,signal,function) # End pyformex-0.8.6/pyformex/gui/gluttext.py0000644000211500021150000001354011705104656020131 0ustar benebene00000000000000# $Id: gluttext.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """2D text decorations using GLUT fonts This module provides the basic functions for using the GLUT library in the rendering of text on an OpenGL canvas. """ import OpenGL.GL as GL import OpenGL.GLU as GLU import OpenGL.GLUT as GLUT #from drawable import * # Needed to initialize the fonts GLUT.glutInit([]) import colors ### Some drawing functions ############################################### # These are the available GLUT fonts. GLUTFONTS = { '9x15' : GLUT.GLUT_BITMAP_9_BY_15, '8x13' : GLUT.GLUT_BITMAP_8_BY_13, 'tr10' : GLUT.GLUT_BITMAP_TIMES_ROMAN_10, 'tr24' : GLUT.GLUT_BITMAP_TIMES_ROMAN_24, 'hv10' : GLUT.GLUT_BITMAP_HELVETICA_10, 'hv12' : GLUT.GLUT_BITMAP_HELVETICA_12, 'hv18' : GLUT.GLUT_BITMAP_HELVETICA_18, } GLUTFONTALIAS = { 'fixed' : ('9x15','8x13'), 'serif' : ('times','tr10','tr24'), 'sans' : ('helvetica','hv10','hv12','hv18'), } def glutSelectFont(font=None,size=None): """Select one of the glut fonts using a font + size description. - font: 'fixed', 'serif' or 'sans' - size: an int that will be rounded to the nearest available size. The return value is a 4-character string representing one of the GLUT fonts. """ #pf.debug("INPUT %s,%s" % (font,size)) if size is None and font in GLUTFONTS: return font if font is None: font = 'sans' for k in GLUTFONTALIAS: if font in GLUTFONTALIAS[k]: font = k break if size is None or not size > 0: size = 14 if font == 'fixed': selector = [ (0,'8x13'), (14,'9x15') ] elif font == 'serif': selector = [ (0,'tr10'), (16,'tr24') ] else: selector = [ (0,'hv10'), (12,'hv12'), (16,'hv18') ] sel = selector[0] for s in selector[1:]: if s[0] <= size: sel = s return sel[1] def getFont(font,size): return glutFont(glutSelectFont(font,size)) def glutFont(font): """Return GLUT font designation for the named font. The recognized font names are: - fixed: '9x15', '8x13', - times-roman: 'tr10', 'tr24' - helvetica: 'hv10', 'hv12', 'hv18' If an unrecognized string is given, the default is 'hv18'. """ return GLUTFONTS.get(font,GLUTFONTS['hv18']) def glutFontHeight(font): """Return the height of the named glut font. This supposes that the last two characters of the name hold the font height. """ return int(font[-2:]) # # BV: !! gravity does not work yet! # def glutRenderText(text,font,gravity=''): """Draw a text in given font at the current rasterpoint. font should be one of the legal fonts returned by glutFont(). If text is not a string, it will be formatted to a string before drawing. After drawing, the rasterpos will have been updated! """ if type(font) == str: font = glutFont(font) if gravity: curpos = GL.glGetFloatv(GL.GL_CURRENT_RASTER_POSITION) #print curpos GL.glRasterPos2f(curpos[0]-20.,curpos[1]) for character in str(text): GLUT.glutBitmapCharacter(font, ord(character)) def glutBitmapLength(font, text): """ Compute the length in pixels of a text string in given font. We use our own function to calculate the length because the builtin has a bug. """ if type(font) == str: font = glutFont(font) len = 0 for c in text: len += GLUT.glutBitmapWidth(font, ord(c)) return len def glutDrawText(text,x,y,font='hv18',gravity='',spacing=1.0): """Draw a text at given 2D position in window. - text: a simple string, a multiline string or a list of strings. If it is a string, it will be splitted on the occurrence of '\\n' characters. - x,y: insertion position on the canvas - gravity: a string that determines the adjusting of the text with respect to the insert position. It can be a combination of one of the characters 'N or 'S' to specify the vertical positon, and 'W' or 'E' for the horizontal. The default(empty) string will center the text. """ if type(text) is str: text = text.split('\n') nlines = len(text) widths = [ glutBitmapLength(font, t) for t in text ] fontheight = glutFontHeight(font) spacing *= fontheight width = max(widths) height = spacing*nlines x,y = float(x),float(y) if 'S' in gravity: yi = y elif 'N' in gravity: yi = y + height else: yi = y + height/2 for t,w in zip(text,widths): if 'E' in gravity: xi = x elif 'W' in gravity: xi = x - w else: xi = x - w/2 yi -= spacing GL.glRasterPos2f(float(xi),float(yi)) glutRenderText(t,font) # End pyformex-0.8.6/pyformex/lib/0000755000211500021150000000000011705105304015645 5ustar benebene00000000000000pyformex-0.8.6/pyformex/lib/drawgl.py0000644000211500021150000002130511705104656017511 0ustar benebene00000000000000# $Id: drawgl.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Basic OpenGL drawing functions. The functions in this module should be exact emulations of the external functions in the compiled library lib.drawgl. These are low level functions that should normally not be used by the user. """ # There should be no other imports here than OpenGL and numpy from OpenGL import GL,GLU from numpy import * accelerated = False def get_version(): return 0 def glColor(color,alpha=1.0): """Set the OpenGL color, possibly with transparency. color is a tuple of 3 real values. alpha is a single real value. All values are between 0.0 and 1.0 """ if alpha == 1.0: GL.glColor3fv(color) else: GL.glColor4fv(append(color,alpha)) def glObjType(nplex): if nplex == 1: objtype = GL.GL_POINTS elif nplex == 2: objtype = GL.GL_LINES elif nplex == 3: objtype = GL.GL_TRIANGLES elif nplex == 4: objtype = GL.GL_QUADS else: objtype = GL.GL_POLYGON return objtype def draw_polygons(x,n,c,alpha,objtype): """Draw a collection of polygons. x : float (nel,nplex,3) : coordinates. n : float (nel,3) or (nel,nplex,3) : normals. c : float (nel,3) or (nel,nplex,3) : color(s) alpha : float objtype : GL Object type (-1 = auto) If nplex colors per element are given, and shading mode is flat, the last color will be used. """ x = x.astype(float32) nplex = x.shape[1] if n is not None: n = n.astype(float32) if c is not None: c = c.astype(float32) if objtype < 0: objtype = glObjType(nplex) if nplex <= 4 and glObjType(nplex) == objtype: GL.glBegin(objtype) if c is None or c.ndim == 1: # no or single color if c is not None: # single color glColor(c,alpha) if n is None: for xi in x.reshape((-1,3)): GL.glVertex3fv(xi) elif n.ndim == 2: for xi,ni in zip(x,n): GL.glNormal3fv(ni) for j in range(nplex): GL.glVertex3fv(xi[j]) elif n.ndim == 3: for i in range(x.shape[0]): for xij,nij in zip(x[i],n[i]): GL.glNormal3fv(nij) GL.glVertex3fv(xij) elif c.ndim == 2: if n is None: for xi,ci in zip(x,c): glColor(ci,alpha) for j in range(nplex): GL.glVertex3fv(xi[j]) elif n.ndim == 2: for xi,ni,ci in zip(x,n,c): glColor(ci,alpha) GL.glNormal3fv(ni) for j in range(nplex): GL.glVertex3fv(xi[j]) elif n.ndim == 3: for xi,ni,ci in zip(x,n,c): glColor(ci,alpha) for j in range(nplex): GL.glNormal3fv(ni[j]) GL.glVertex3fv(xi[j]) elif c.ndim == 3: if n is None: for xi,ci in zip(x.reshape((-1,3)),c.reshape((-1,3))): glColor(ci,alpha) GL.glVertex3fv(xi) elif n.ndim == 2: for xi,ni,ci in zip(x,n,c): GL.glNormal3fv(ni) for j in range(nplex): glColor(ci[j],alpha) GL.glVertex3fv(xi[j]) elif n.ndim == 3: for xi,ni,ci in zip(x.reshape((-1,3)),n.reshape((-1,3)),c.reshape((-1,3))): glColor(ci,alpha) GL.glNormal3fv(ni) GL.glVertex3fv(xi) GL.glEnd() else: if c is None: if n is None: for xi in x: GL.glBegin(objtype) for j in range(nplex): GL.glVertex3fv(xi[j]) GL.glEnd() elif n.ndim == 2: for xi,ni in zip(x,n): GL.glBegin(objtype) GL.glNormal3fv(ni) for j in range(nplex): GL.glVertex3fv(xi[j]) GL.glEnd() elif n.ndim == 3: for i in range(x.shape[0]): GL.glBegin(objtype) for xij,nij in zip(x[i],n[i]): GL.glNormal3fv(nij) GL.glVertex3fv(xij) GL.glEnd() elif c.ndim == 2: if n is None: for xi,ci in zip(x,c): GL.glBegin(objtype) glColor(ci,alpha) for j in range(nplex): GL.glVertex3fv(xi[j]) GL.glEnd() elif n.ndim == 2: for xi,ni,ci in zip(x,n,c): GL.glBegin(objtype) glColor(ci,alpha) GL.glNormal3fv(ni) for j in range(nplex): GL.glVertex3fv(xi[j]) GL.glEnd() elif n.ndim == 3: for xi,ni,ci in zip(x,n,c): GL.glBegin(objtype) glColor(ci,alpha) for j in range(nplex): GL.glNormal3fv(ni[j]) GL.glVertex3fv(xi[j]) GL.glEnd() elif c.ndim == 3: if n is None: for xi,ci in zip(x,c): GL.glBegin(objtype) glColor(ci,alpha) for j in range(nplex): glColor(ci[j],alpha) GL.glVertex3fv(xi[j]) GL.glEnd() elif n.ndim == 2: for xi,ni,ci in zip(x,n,c): GL.glBegin(objtype) GL.glNormal3fv(ni) for j in range(nplex): glColor(ci[j],alpha) GL.glVertex3fv(xi[j]) GL.glEnd() elif n.ndim == 3: for xi,ni,ci in zip(x,n,c): GL.glBegin(objtype) for j in range(nplex): glColor(ci[j],alpha) GL.glNormal3fv(ni[j]) GL.glVertex3fv(xi[j]) GL.glEnd() def pick_polygons(x,objtype): """Mimics draw_polygons for picking purposes. x : float (nel,nplex,3) : coordinates. objtype : GL Object type (-1 = auto) """ nplex = x.shape[1] if objtype < 0: objtype = glObjType(nplex) for i,xi in enumerate(x): GL.glPushName(i) GL.glBegin(objtype) for xij in xi: GL.glVertex3fv(xij) GL.glEnd() GL.glPopName() def draw_polygon_elems(x,e,n,c,alpha,objtype): """Draw a collection of polygon elements. This function is like draw_polygons, but the vertices of the polygons are specified by a (coords,elems) tuple. x : float (npts,3) : coordinates e : int32 (nel,nplex) : element connectivity n : float (nel,3) or (nel,nplex,3) normals. c : float (nel,3) or (nel,nplex,3) colors alpha : float objtype : GL Object type (-1 = auto) """ draw_polygons(x[e],n,c,alpha,objtype) def pick_polygon_elems(x,e,objtype): """Mimics draw_polygon_elems for picking purposes. x : float (npts,3) : coordinates e : int32 (nel,nplex) : element connectivity objtype : GL Object type (-1 = auto) """ pick_polygons(x[e],objtype) ### End pyformex-0.8.6/pyformex/lib/drawgl_module.c0000644000211500021150000005152111666766507020672 0ustar benebene00000000000000/* $Id: drawgl_module.c 2114 2011-12-04 21:59:35Z bverheg $ */ // // This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) // pyFormex is a tool for generating, manipulating and transforming 3D // geometrical models by sequences of mathematical operations. // Home page: http://pyformex.org // Project page: http://savannah.nongnu.org/projects/pyformex/ // Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) // Distributed under the GNU General Public License version 3 or later. // // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. // /* Low level drawing functions to speed up OpenGL calls on large arrays. The callers should make sure that the arguments are correct. Nasty crashes may result if not! */ #include #include #include #include static char __doc__[] = "drawgl_ module\n\ \n\ This module provides accelerated versions of the pyFormex basic\n\ OpenGL drawing functions.\n\ \n"; /************************ LIBRARY VERSION *******************/ /* Whenever a change is made to this library that causes pyFormex to be incompatible with the previous version, the version number should be bumped, and the new version number should also be set in the lib module initialization file __init__.py ## ACTUALLY NOT USED YET ## */ int version = 1; static PyObject * get_version(PyObject *dummy, PyObject *args) { return Py_BuildValue("i", version); } /****** INTERNAL FUNCTIONS (not callable from Python ********/ /********************************************** gl_color ****/ /* Set the OpenGL color, possibly with transparency. */ /* color is an array of 3 float values. alpha is a single float value. All values are between 0.0 and 1.0 */ void gl_color(float *color, float alpha) { if (alpha == 1.0) { glColor3fv(color); } else { glColor4f(color[0],color[1],color[2],alpha); } return; } /********************************************** gl_objtype ****/ /* Set the OpenGL object type from plexitude. */ /* nplex is an int >= 0. */ int gl_objtype(int nplex) { int objtype; if (nplex == 1) objtype = GL_POINTS; else if (nplex == 2) objtype = GL_LINES; else if (nplex == 3) objtype = GL_TRIANGLES; else if (nplex == 4) objtype = GL_QUADS; else objtype = GL_POLYGON; return objtype; } /********************************************** gl_map2_vertexmode ****/ /* Set the OpenGL MAP2 vertex mode from ndim. */ /* ndim is either 3 or 4. */ GLenum gl_map2_vertexmode(int ndim) { GLenum mode = 0; if (ndim == 3) mode = GL_MAP2_VERTEX_3; else if (ndim == 4) mode = GL_MAP2_VERTEX_4; return mode; } /****** EXTERNAL FUNCTIONS (callable from Python ********/ /********************************************** draw_polygons ****/ /* Draw polygons */ /* args: x x : float (nel,nplex,3) : coordinates n : float (nel,3) or (nel,nplex,3) normals. c : float (nel,3) or (nel,nplex,3) colors alpha : float objtype : GL Object type (-1 = auto) */ static PyObject * draw_polygons(PyObject *dummy, PyObject *args) { PyObject *arg1=NULL, *arg2=NULL, *arg3=NULL; PyObject *arr1=NULL, *arr2=NULL, *arr3=NULL; float *x, *n=NULL, *c=NULL, alpha; int objtype,nel,nplex,ndc=0,ndn=0,i,j; #ifdef DEBUG printf("** draw_polygons\n"); #endif if (!PyArg_ParseTuple(args,"OOOfi",&arg1,&arg2,&arg3,&alpha,&objtype)) return NULL; arr1 = PyArray_FROM_OTF(arg1,NPY_FLOAT,NPY_IN_ARRAY); if (arr1 == NULL) return NULL; x = (float *)PyArray_DATA(arr1); nel = PyArray_DIMS(arr1)[0]; nplex = PyArray_DIMS(arr1)[1]; #ifdef DEBUG printf("** nel = %d\n",nel); printf("** nplex = %d\n",nplex); #endif arr2 = PyArray_FROM_OTF(arg2, NPY_FLOAT, NPY_IN_ARRAY); if (arr2 != NULL) { ndn = PyArray_NDIM(arr2); n = (float *)PyArray_DATA(arr2); } arr3 = PyArray_FROM_OTF(arg3, NPY_FLOAT, NPY_IN_ARRAY); if (arr3 != NULL) { ndc = PyArray_NDIM(arr3); c = (float *)PyArray_DATA(arr3); } #ifdef DEBUG printf("** ndn = %d\n",ndn); printf("** ndc = %d\n",ndc); #endif if (objtype < 0) objtype = gl_objtype(nplex); if (nplex <= 4 && objtype == gl_objtype(nplex)) { /*********** Points, Lines, Triangles, Quads **************/ glBegin(objtype); if (ndc < 2) { /* no or single color */ if (ndc == 1) { /* single color */ gl_color(c,alpha); } if (ndn == 0) { for (i=0; i 0) { ncdim = PyArray_DIMS(arr4)[ndc-1]; c = (float *)PyArray_DATA(arr4); } } #ifdef DEBUG printf("** ndc = %d\n",ndc); printf("** ncdim = %d\n",ncdim); #endif nurb = gluNewNurbsRenderer(); #ifdef DEBUG printf("** nurb = %p\n",nurb); #endif if (nurb == NULL) goto cleanup; gluNurbsProperty(nurb,GLU_SAMPLING_TOLERANCE,sampling); mode = gl_map2_vertexmode(ndim); if (ndc == 4) cmode = GL_MAP2_COLOR_4; #ifdef DEBUG printf("** nurbs mode = %d\n",mode); printf("** nurbs cmode = %d\n",cmode); printf("** nurbs order = %d, %d\n",nsorder,ntorder); #endif if (ndc == 1) { /* single color */ gl_color(c,alpha); } for (i=0; i 1) s += nsknots; if (ndt > 1) t += ntknots; x += ns*nt*ndim; } cleanup: Py_INCREF(Py_None); retval = Py_None; /* common code for normal and failure exit */ fail: if (nurb) gluDeleteNurbsRenderer(nurb); Py_XDECREF(arr1); Py_XDECREF(arr2); Py_XDECREF(arr3); Py_XDECREF(arr4); return retval; } /***************** The methods defined in this module **************/ static PyMethodDef _methods_[] = { {"get_version", get_version, METH_VARARGS, "Return library version."}, {"draw_polygons", draw_polygons, METH_VARARGS, "Draw polygons."}, {"pick_polygons", pick_polygons, METH_VARARGS, "Pick polygons."}, {"draw_polygon_elems", draw_polygon_elems, METH_VARARGS, "Draw polygon elements."}, {"pick_polygon_elems", pick_polygon_elems, METH_VARARGS, "Pick polygon elements."}, {"draw_nurbs_surfaces", draw_nurbs_surfaces, METH_VARARGS, "Draw NURBS surfaces."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; /* Initialize the module */ PyMODINIT_FUNC initdrawgl_(void) { PyObject* module; module = Py_InitModule3("drawgl_", _methods_, __doc__); PyModule_AddIntConstant(module,"accelerated",1); import_array(); /* Get access to numpy array API */ } /* End */ pyformex-0.8.6/pyformex/lib/nurbs_module.c0000644000211500021150000015346011666766507020550 0ustar benebene00000000000000/* $Id: nurbs_module.c 2114 2011-12-04 21:59:35Z bverheg $ */ // // This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) // pyFormex is a tool for generating, manipulating and transforming 3D // geometrical models by sequences of mathematical operations. // Home page: http://pyformex.org // Project page: http://savannah.nongnu.org/projects/pyformex/ // Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) // Distributed under the GNU General Public License version 3 or later. // // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. // // // This module is partly inspired by the Nurbs toolbox Python port by // Runar Tenfjord (http://www.aria.uklinux.net/nurbs.php3) // #include "Python.h" #include "numpy/arrayobject.h" #include static char __doc__[] = "nurbs_ module\n\ \n\ This module provides accelerated versions of the pyFormex NURBS\n\ functions.\n\ \n"; /****** INTERNAL FUNCTIONS (not callable from Python ********/ int min(int a, int b) { if (b < a) a = b; return a; } int max(int a, int b) { if (b > a) a = b; return a; } /* Dot product of two vectors of length n */ /* ia and ib are the strides of the elements addressed starting from a, b */ static double dotprod(double *a, int ia, double *b, int ib, int n) { int i; double t; t = 0.0; for (i=0; i ntop) { ++ntop; a[ntop] = _gammaln(ntop+1.0); } return a[n]; } static double _binomial(int n, int k) { return floor(0.5+exp(_factln(n)-_factln(k)-_factln(n-k))); } /* _horner Compute the value of a polynomial using Horner's rule. Input: - a: double(n+1), coefficients of the polynomial, starting from lowest degree - n: int, degree of the polynomial - u: double, parametric value where the polynomial is evaluated Returns: double, the value of the polynomial Algorithm A1.1 from 'The NURBS Book' p7. */ static double _horner(double *a, int n, double u) { double c = a[n]; int i; for (i = n-1; i>=0; --i) c = c * u + a[i]; return c; } /* /\* _horner */ /* Compute the value of a polynom using Horner's rule. */ /* Input: */ /* - a: double(n+1,nd), nd-dimensional coefficients of the polynom, starting */ /* from lowest degree */ /* - n: int, degree of the polynom */ /* - nd: int, number of dimensions */ /* - u: double(nu), parametric values where the polynom is evaluated */ /* Output: */ /* - c: double(nu,nd), nd-dimensional values of the polynom */ /* *\/ */ /* static void _horner(double *a, int n, int nd, double *u, int nu) */ /* { */ /* int i,j,k; */ /* double c; */ /* for (i=0; i=0; --k) c = c * u[i] + a[k,j]; */ /* u[i,j] = c; */ /* } */ /* } */ /* } */ /* static char horner_doc[] = */ /* "Evaluate a polynom using Horner's rule.\n\ */ /* \n\ */ /* Params:\n\ */ /* - a: double(n+1,nd), nd-dimensional coefficients of the polynom of degree n,\n\ */ /* starting from lowest degree\n\ */ /* - u: double(nu), parametric values where the polynom is evaluated\n\ */ /* \n\ */ /* Returns:\n\ */ /* - p: double(nu,3), nu nd-dimensonal points\n\ */ /* \n\ */ /* Extended algorithm A1.1 from 'The NURBS Book' p7.\n\ */ /* \n"; */ /* static PyObject * nurbs_horner(PyObject *self, PyObject *args) */ /* { */ /* int n, nd, nu; */ /* npy_intp *a_dim, *u_dim, dim[2]; */ /* double *a, *u, *pnt; */ /* PyObject *arg1, *arg2; */ /* PyObject *arr1=NULL, *arr2=NULL, *ret=NULL; */ /* if (!PyArg_ParseTuple(args, "OO", &arg1, &arg2)) */ /* return NULL; */ /* arr1 = PyArray_FROM_OTF(arg1, NPY_DOUBLE, NPY_IN_ARRAY); */ /* if(arr1 == NULL) */ /* return NULL; */ /* arr2 = PyArray_FROM_OTF(arg2, NPY_DOUBLE, NPY_IN_ARRAY); */ /* if(arr2 == NULL) */ /* goto fail; */ /* /\* We suppose the dimensions are correct*\/ */ /* a_dim = PyArray_DIMS(arr1); */ /* u_dim = PyArray_DIMS(arr2); */ /* n = a_dim[0]; */ /* nd = a_dim[1]; */ /* nu = u_dim[0]; */ /* a = (double *)PyArray_DATA(arr1); */ /* u = (double *)PyArray_DATA(arr2); */ /* /\* Create the return array *\/ */ /* dim[0] = nu; */ /* dim[1] = nd; */ /* ret = PyArray_SimpleNew(2,dim, NPY_DOUBLE); */ /* pnt = (double *)PyArray_DATA(ret); */ /* /\* Compute *\/ */ /* int i,j; */ /* for (i=0; i= U[mid+1]) { if (u < U[mid]) high = mid; else low = mid; mid = (low + high) / 2; cnt ++; if (cnt > 20) break; } return(mid); } /* basis_funs */ /* Compute the nonvanishing B-spline basis functions for index span i. Input: - U: knot sequence: U[0] .. U[m] - u: parametric value: U[0] <= u <= U[m] - p: degree of the B-spline basis functions - i: index of the knot span for value u (from find_span()) Output: - N: (p+1) values of nonzero basis functions at u Algorithm A2.2 from 'The NURBS Book' pg70. */ static void basis_funs(double *U, double u, int p, int i, double *N) { int j,r; double saved, temp; // work space double *left = (double*) malloc((p+1)*sizeof(double)); double *right = (double*) malloc((p+1)*sizeof(double)); N[0] = 1.0; for (j = 1; j <= p; j++) { left[j] = u - U[i+1-j]; right[j] = U[i+j] - u; saved = 0.0; for (r = 0; r < j; r++) { temp = N[r] / (right[r+1] + left[j-r]); N[r] = saved + right[r+1] * temp; saved = left[j-r] * temp; } N[j] = saved; } free(left); free(right); } /* basis_derivs */ /* Compute the nonvanishing B-spline basis functions and their derivatives. Input: - U: knot sequence: U[0] .. U[m] - u: parametric value: U[0] <= u <= U[m] - p: degree of the B-spline basis functions - i: index of the knot span for value u (from find_span()) - n: number of derivatives to compute (n <= p) Output: - dN: (n+1,p+1) values of the nonzero basis functions and their first n derivatives at u Algorithm A2.3 from 'The NURBS Book' pg72. */ static void basis_derivs(double *U, double u, int p, int i, int n, double *dN) { int j,k,r,s1,s2,rk,pk,j1,j2; double temp, saved, der; double **ndu, *a, *left, *right; ndu = newmatrix(p+1, p+1); a = (double *) malloc(2*(p+1)*sizeof(double)); left = (double *) malloc((p+1)*sizeof(double)); right = (double *) malloc((p+1)*sizeof(double)); ndu[0][0] = 1.0; for (j=1; j<=p; j++) { left[j] = u - U[i+1-j]; right[j] = U[i+j]-u; saved = 0.0; for (r=0; r= k) { a[s2] = a[s1] / ndu[pk+1][rk]; der = a[s2] * ndu[rk][pk]; } if (rk >= -1) j1 = 1; else j1 = -rk; if (r-1 <= pk) j2 = k-1; else j2 = p-r; for (j=j1; j<=j2; j++) { a[s2+j] = (a[s1+j] - a[s1+j-1]) / ndu[pk+1][rk+j]; der += a[s2+j] * ndu[rk+j][pk]; } if (r <= pk) { a[s2+k] = -a[s1+k-1] / ndu[pk+1][r]; der += a[s2+k] * ndu[r][pk]; } dN[k*(p+1)+r] = der; /* Switch rows */ j = s1; s1 = s2; s2 = j; } } /* Multiply by the correct factors */ r = p; for (k=1; k<=n; k++) { for (j=0; j<=p; j++) dN[k*(p+1)+j] *= r; r *= (p-k); } freematrix(ndu); free(a); free(left); free(right); } /********************************************************/ /************************ CURVE *************************/ /********************************************************/ /* curve_points */ /* Compute points on a B-spline curve. Input: - P: control points P(nc,nd) - nc: number of control points - nd: dimension of the points (3 or 4) - U: knot sequence: U[0] .. U[m] - nk: number of knot values = m+1 - u: parametric values: U[0] <= ui <= U[m] - nu: number of parametric values Output: - pnt: (nu,nd) points on the B-spline Modified algorithm A3.1 from 'The NURBS Book' pg82. */ static void curve_points(double *P, int nc, int nd, double *U, int nk, double *u, int nu, double *pnt) { int i, j, p, s, t; /* degree of the spline */ p = nk - nc - 1; /* space for the basis functions */ double *N = (double*) malloc((p+1)*sizeof(double)); /* for each parametric point j */ for (j=0; j= 0; j--) { while (u[j] <= U[i] && i > a) { for (q=0; q mult; k--) alfa[k-mult-1] = numer / (U[a+k]-U[a]); /* Insert knot U[b] r times */ r = p - mult; for (j = 1; j <= r; j++) { save = r - j; s = mult + j; /* Number of new points */ for (k = p; k >= s; k--) { alpha = alfa[k-s]; //printf("alpha = %f\n",alpha); for (ii = 0; ii < nd; ii++) { newP[(nb+k)*nd+ii] = alpha*newP[(nb+k)*nd+ii] + (1.0-alpha)*newP[(nb+k-1)*nd+ii]; //printf("Setting element %d to %f\n",(nb+k)*nd+ii,newP[(nb+k)*nd+ii]); } } if (b < m) /* Control point of next segment */ for (ii = 0; ii < nd; ii++) { newP[(nb+p+save)*nd+ii] = newP[(nb+p)*nd+ii]; //printf("Copying element %d to %f\n",(nb+p+save)*nd+ii,newP[(nb+p+save)*nd+ii]); } } } /* Bezier segment completed */ nb += p; if (b < m) { /* Initialize for next segment */ for (i = r; i <= p; i++) for (ii = 0; ii < nd; ii++) { newP[(nb+i)*nd+ii] = P[(b-p+i)*nd+ii]; //printf("Initializing element %d to %f\n",(nb+i)*nd+ii,newP[(nb+i)*nd+ii]); } a = b; b++; } } free(alfa); } /* curve_knot_remove */ /* Refine curve knot vector. Input: - p: degree of the B-spline - P: control points P(nc,nd) - nc: number of control points = n+1 - nd: dimension of the points (3 or 4) - U: knot sequence: U[0] .. U[m] m = n+p+1 = nc+p - u: knot value to remove: U[0] <= u <= U[m] - num: number of times to remove u - tol: allowable tolerance for deviation of the curve. See NURBS book, p. 185 Output: - t: actual number of times that u was removed P and U are replaced with the new control points and knot vector Modified algorithm A5.8 from 'The NURBS Book' pg185. */ static int curve_knot_remove(double *P, int nc, int nd, double *U, int nk, double u, int num, double tol) { int n,m,p,ord,fout,last,first,t,off,k,i,j,ii,jj,remflag,r,s,kk; double alfi,alfj; n = nc - 1; m = nk - 1; p = m - n - 1; double *temp = (double*) malloc((2*p+1)*nd*sizeof(double)); double *xtemp = (double*) malloc(nd*sizeof(double)); r = find_last_occurrence(U,u); s = find_multiplicity(U,u,r); ord = p+1; fout = (2*r-s-p)/2; /* First control point out */ last = r-s; first = r-p; for (t=0; t t) { /* Compute new control points for onr removeal step */ alfi = (u-U[i])/(U[i+ord+t]-U[i]); alfj = (u-U[j-t])/(U[j+ord]-U[j-t]); for (k=0; k t) { for (k=0; k 0) */ /* lbz = (oldr+2) / 2; */ /* else */ /* lbz = 1; */ /* if (r > 0) */ /* rbz = ph - (r+1)/2; */ /* else */ /* rbz = ph; */ /* if (r > 0) */ /* { */ /* // insert knot to get bezier segment */ /* numer = ub - ua; */ /* for (q = p; q > mult; q--) */ /* alfa[q-mult-1] = numer / (U[a+q]-ua); */ /* for (j = 1; j <= r; j++) */ /* { */ /* save = r - j; */ /* s = mult + j; */ /* for (q = p; q >= s; q--) */ /* for (ii = 0; ii < nd; ii++) */ /* bpts[ii][q] = alfa[q-s]*bpts[ii][q]+(1.0-alfa[q-s])*bpts[ii][q-1]; */ /* for (ii = 0; ii < nd; ii++) */ /* Nextbpts[ii][save] = bpts[ii][p]; */ /* } */ /* } */ /* // end of insert knot */ /* // degree elevate bezier */ /* for (i = lbz; i <= ph; i++) */ /* { */ /* for (ii = 0; ii < nd; ii++) */ /* ebpts[ii][i] = 0.0; */ /* mpi = min(p, i); */ /* for (j = max(0,i-t); j <= mpi; j++) */ /* for (ii = 0; ii < nd; ii++) */ /* ebpts[ii][i] = ebpts[ii][i] + bezalfa[j][i]*bpts[ii][j]; */ /* } */ /* // end of degree elevating bezier */ /* if (oldr > 1) */ /* { */ /* // must remove knot u=U[a] oldr times */ /* first = kind - 2; */ /* last = kind; */ /* den = ub - ua; */ /* bet = (ub-newU[kind-1]) / den; */ /* // knot removal loop */ /* for (tr = 1; tr < oldr; tr++) */ /* { */ /* i = first; */ /* j = last; */ /* kj = j - kind + 1; */ /* while (j - i > tr) */ /* { */ /* // loop and compute the new control points */ /* // for one removal step */ /* if (i < cind) */ /* { */ /* alf = (ub-newU[i])/(ua-newU[i]); */ /* for (ii = 0; ii < nd; ii++) */ /* newP[ii][i] = alf * newP[ii][i] + (1.0-alf) * newP[ii][i-1]; */ /* } */ /* if (j >= lbz) */ /* { */ /* if (j-tr <= kind-ph+oldr) */ /* { */ /* gam = (ub-newU[j-tr]) / den; */ /* for (ii = 0; ii < nd; ii++) */ /* ebpts[ii][kj] = gam*ebpts[ii][kj] + (1.0-gam)*ebpts[ii][kj+1]; */ /* } */ /* else */ /* { */ /* for (ii = 0; ii < nd; ii++) */ /* ebpts[ii][kj] = bet*ebpts[ii][kj] + (1.0-bet)*ebpts[ii][kj+1]; */ /* } */ /* } */ /* i++; */ /* j--; */ /* kj--; */ /* } */ /* first--; */ /* last++; */ /* } */ /* } */ /* // end of removing knot n=U[a] */ /* // load the knot ua */ /* if (a != p) */ /* for (i = 0; i < ph-oldr; i++) */ /* { */ /* newU[kind] = ua; */ /* kind++; */ /* } */ /* // load ctrl pts into ic */ /* for (j = lbz; j <= rbz; j++) */ /* { */ /* for (ii = 0; ii < nd; ii++) */ /* newP[ii][cind] = ebpts[ii][j]; */ /* cind++; */ /* } */ /* if (b < m) */ /* { */ /* // setup for next pass thru loop */ /* for (j = 0; j < r; j++) */ /* for (ii = 0; ii < nd; ii++) */ /* bpts[ii][j] = Nextbpts[ii][j]; */ /* for (j = r; j <= p; j++) */ /* for (ii = 0; ii < nd; ii++) */ /* bpts[ii][j] = P[ii][b-p+j]; */ /* a = b; */ /* b++; */ /* ua = ub; */ /* } */ /* else */ /* // end knot */ /* for (i = 0; i <= ph; i++) */ /* newU[kind+i] = ub; */ /* } */ /* // end while loop */ /* *nh = mh - ph - 1; */ /* freematrix(bezalfa); */ /* freematrix(bpts); */ /* freematrix(ebpts); */ /* freematrix(Nextbpts); */ /* free(alfa); */ /* } */ /* curve_global_interp_mat */ /* Compute the global curve interpolation matrix. Input: - p: degree of the B-spline - Q: points through which the curve should pass (nc,nd) - nc: number of points = number of control points = n+1 - nd: dimension of the points (3 or 4) - u: parameter values at the points (nc) strategies: 0 : equally spaced (not recommended) 1 : chord length 2 : centripetal (recommended) Output: - P: control points P(nc,nd) - U: knot sequence: U[0] .. U[m] m = n+p+1 = nc+p - A: coefficient matrix (nc,nc) Modified algorithm A9.1 from 'The NURBS Book' pg369. */ static void curve_global_interp_mat(int p, double *Q, int nc, int nd, double *u, double *U, double *A) { int n,m,i,j,s; n = nc - 1; m = nc + p; /* Compute the knot vector U by averaging (9.8) */ for (i=0; i mult; k--) */ /* alfa[k-mult-1] = numer / (U[a+k]-U[a]); */ /* /\* Insert knot U[b] r times *\/ */ /* r = p - mult; */ /* for (j = 1; j <= r; j++) { */ /* save = r - j; */ /* s = mult + j; /\* Number of new points *\/ */ /* for (k = p; k >= s; k--) { */ /* alpha = alfa[k-s]; */ /* printf("alpha = %f\n",alpha); */ /* for (ii = 0; ii < nd; ii++) { */ /* newP[(nb+k)*nd+ii] = alpha*newP[(nb+k)*nd+ii] + (1.0-alpha)*newP[(nb+k-1)*nd+ii]; */ /* printf("Setting element %d to %f\n",(nb+k)*nd+ii,newP[(nb+k)*nd+ii]); */ /* } */ /* } */ /* if (b < m) */ /* /\* Control point of next segment *\/ */ /* for (ii = 0; ii < nd; ii++) { */ /* newP[(nb+p+save)*nd+ii] = newP[(nb+p)*nd+ii]; */ /* printf("Copying element %d to %f\n",(nb+p+save)*nd+ii,newP[(nb+p+save)*nd+ii]); */ /* } */ /* } */ /* } */ /* /\* Bezier segment completed *\/ */ /* nb += p; */ /* if (b < m) { */ /* /\* Initialize for next segment *\/ */ /* for (i = r; i <= p; i++) */ /* for (ii = 0; ii < nd; ii++) { */ /* newP[(nb+i)*nd+ii] = P[(b-p+i)*nd+ii]; */ /* printf("Initializing element %d to %f\n",(nb+i)*nd+ii,newP[(nb+i)*nd+ii]); */ /* } */ /* a = b; */ /* b++; */ /* } */ /* } */ /* free(alfa); */ /* } */ /********************************************************/ /****** EXPORTED FUNCTIONS (callable from Python ********/ /********************************************************/ static char _doc_[] = "nurbs_ module. Version 0.1\n\ \n\ This module implements low level NURBS functions for pyFormex.\n\ \n"; static char binomial_doc[] = "Computes the binomial coefficient.\n\ \n\ ( n ) n!\n\ ( ) = --------\n\ ( k ) k!(n-k)!\n\ \n\ Algorithm from 'Numerical Recipes in C, 2nd Edition' pg215.\n"; static PyObject * binomial(PyObject *self, PyObject *args) { int n, k; double ret; if(!PyArg_ParseTuple(args, "ii", &n, &k)) return NULL; ret = _binomial(n, k); return Py_BuildValue("d",ret); } static char allBernstein_doc[] = "Compute the value of all n-th degree Bernstein polynomials.\n\ \n\ Input:\n\ - n: int, degree of the polynomials\n\ - u: double, parametric value where the polynomials are evaluated\n\ \n\ Returns:\n\ double(n+1), the value of all n-th degree Bernstein polynomials B(i,n)\n\ at parameter value u.\n\ \n\ Algorithm A1.3 from The NURBS Book.\n"; static PyObject * allBernstein(PyObject *self, PyObject *args) { int n; npy_intp dim[1]; double u, *B; PyObject *ret=NULL; if (!PyArg_ParseTuple(args, "id", &n, &u)) return NULL; /* Create the return array */ dim[0] = n+1; ret = PyArray_SimpleNew(1,dim, NPY_DOUBLE); B = (double *)PyArray_DATA(ret); /* Compute */ all_bernstein(n,u,B); /* Return */ return ret; } static char curvePoints_doc[] = "Compute a point on a B-spline curve.\n\ \n\ Input:\n\ \n\ - p: degree of the B-spline\n\ - P: control points P(nc,nd)\n\ - nc: number of control points\n\ - nd: dimension of the points (3 or 4)\n\ - U: knot sequence: U[0] .. U[m]\n\ - u: parametric values: U[0] <= ui <= U[m]\n\ - nu: number of parametric values\n\ \n\ Output:\n\ - pnt: (nu,nd) points on the B-spline\n\ \n\ Modified algorithm A3.1 from 'The NURBS Book' pg82.\n\ \n"; static PyObject * curvePoints(PyObject *self, PyObject *args) { int nd, nc, nk, nu; npy_intp *P_dim, *U_dim, *u_dim, dim[2]; double *P, *U, *u, *pnt; PyObject *a1, *a2, *a3; PyObject *arr1=NULL, *arr2=NULL, *arr3=NULL, *ret=NULL; if (!PyArg_ParseTuple(args, "OOO", &a1, &a2, &a3)) return NULL; arr1 = PyArray_FROM_OTF(a1, NPY_DOUBLE, NPY_IN_ARRAY); if(arr1 == NULL) return NULL; arr2 = PyArray_FROM_OTF(a2, NPY_DOUBLE, NPY_IN_ARRAY); if(arr2 == NULL) goto fail; arr3 = PyArray_FROM_OTF(a3, NPY_DOUBLE, NPY_IN_ARRAY); if(arr3 == NULL) goto fail; P_dim = PyArray_DIMS(arr1); U_dim = PyArray_DIMS(arr2); u_dim = PyArray_DIMS(arr3); nc = P_dim[0]; nd = P_dim[1]; nk = U_dim[0]; nu = u_dim[0]; P = (double *)PyArray_DATA(arr1); U = (double *)PyArray_DATA(arr2); u = (double *)PyArray_DATA(arr3); /* Create the return array */ dim[0] = nu; dim[1] = nd; ret = PyArray_SimpleNew(2,dim, NPY_DOUBLE); pnt = (double *)PyArray_DATA(ret); /* Compute */ curve_points(P, nc, nd, U, nk, u, nu, pnt); /* Clean up and return */ Py_DECREF(arr1); Py_DECREF(arr2); Py_DECREF(arr3); return ret; fail: //printf("error cleanup and return\n"); Py_XDECREF(arr1); Py_XDECREF(arr2); Py_XDECREF(arr3); return NULL; } static char curveDerivs_doc[] = "Compute derivatives of a B-spline curve.\n\ \n\ Input:\n\ \n\ - p: degree of the B-spline\n\ - P: control points P(nc,nd)\n\ - nc: number of control points\n\ - nd: dimension of the points (3 or 4)\n\ - U: knot sequence: U[0] .. U[m]\n\ - u: parametric values: U[0] <= ui <= U[m]\n\ - nu: number of parametric values\n\ - n: number of derivatives to compute\n\ \n\ Output:\n\ - pnt: (n+1,nu,nd) points and derivatives on the B-spline\n\ \n\ Modified algorithm A3.2 from 'The NURBS Book' pg93.\n\ \n"; static PyObject * curveDerivs(PyObject *self, PyObject *args) { int nc, nd, nk, nu, n; npy_intp *P_dim, *U_dim, *u_dim, dim[3]; double *P, *U, *u, *pnt; PyObject *a1, *a2, *a3; PyObject *arr1=NULL, *arr2=NULL, *arr3=NULL, *ret=NULL; if(!PyArg_ParseTuple(args, "OOOi", &a1, &a2, &a3, &n)) return NULL; arr1 = PyArray_FROM_OTF(a1, NPY_DOUBLE, NPY_IN_ARRAY); if(arr1 == NULL) return NULL; arr2 = PyArray_FROM_OTF(a2, NPY_DOUBLE, NPY_IN_ARRAY); if(arr2 == NULL) goto fail; arr3 = PyArray_FROM_OTF(a3, NPY_DOUBLE, NPY_IN_ARRAY); if(arr3 == NULL) goto fail; P_dim = PyArray_DIMS(arr1); U_dim = PyArray_DIMS(arr2); u_dim = PyArray_DIMS(arr3); nc = P_dim[0]; nd = P_dim[1]; nk = U_dim[0]; nu = u_dim[0]; P = (double *)PyArray_DATA(arr1); U = (double *)PyArray_DATA(arr2); u = (double *)PyArray_DATA(arr3); /* Create the return array */ dim[0] = n+1; dim[1] = nu; dim[2] = nd; ret = PyArray_SimpleNew(3,dim, NPY_DOUBLE); pnt = (double *)PyArray_DATA(ret); /* Compute */ curve_derivs(n, P, nc, nd, U, nk, u, nu, pnt); /* Clean up and return */ Py_DECREF(arr1); Py_DECREF(arr2); Py_DECREF(arr3); return ret; fail: //printf("error cleanup and return\n"); Py_XDECREF(arr1); Py_XDECREF(arr2); Py_XDECREF(arr3); return NULL; } static char curveKnotRefine_doc[] = "Refine curve knot vector.\n\ \n\ Input:\n\ \n\ - P: control points P(nc,nd)\n\ - U: knot sequence: U(nk) (nk = nc+p+1)\n\ - u: (nu) parametric values of new knots: U[0] <= u[i] <= U[m]\n\ \n\ Output:\n\ - newP: (nc+nu,nd) new control points\n\ - newU: (m+nu) new knot vector\n\ \n\ Modified algorithm A5.1 from 'The NURBS Book' pg164.\n\ \n"; static PyObject * curveKnotRefine(PyObject *self, PyObject *args) { int nd, nc, nk, nu; npy_intp *P_dim, *U_dim, *u_dim, dim[2]; double *P, *U, *u, *newP, *newU; PyObject *a1, *a2, *a3; PyObject *arr1=NULL, *arr2=NULL, *arr3=NULL, *ret1=NULL, *ret2=NULL; if(!PyArg_ParseTuple(args, "OOO", &a1, &a2, &a3)) return NULL; arr1 = PyArray_FROM_OTF(a1, NPY_DOUBLE, NPY_IN_ARRAY); if(arr1 == NULL) return NULL; arr2 = PyArray_FROM_OTF(a2, NPY_DOUBLE, NPY_IN_ARRAY); if(arr2 == NULL) goto fail; arr3 = PyArray_FROM_OTF(a3, NPY_DOUBLE, NPY_IN_ARRAY); if(arr3 == NULL) goto fail; P_dim = PyArray_DIMS(arr1); U_dim = PyArray_DIMS(arr2); u_dim = PyArray_DIMS(arr3); nc = P_dim[0]; nd = P_dim[1]; nk = U_dim[0]; nu = u_dim[0]; P = (double *)PyArray_DATA(arr1); U = (double *)PyArray_DATA(arr2); u = (double *)PyArray_DATA(arr3); /* Create the return arrays */ dim[0] = nc+nu; dim[1] = nd; ret1 = PyArray_SimpleNew(2,dim, NPY_DOUBLE); newP = (double *)PyArray_DATA(ret1); dim[0] = nk+nu; ret2 = PyArray_SimpleNew(1,dim, NPY_DOUBLE); newU = (double *)PyArray_DATA(ret2); /* Compute */ curve_knot_refine(P, nc, nd, U, nk, u, nu, newP, newU); /* Clean up and return */ Py_DECREF(arr1); Py_DECREF(arr2); Py_DECREF(arr3); //return ret1; return Py_BuildValue("(OO)", ret1, ret2); fail: //printf("error cleanup and return\n"); Py_XDECREF(arr1); Py_XDECREF(arr2); Py_XDECREF(arr3); return NULL; } static char curveDecompose_doc[] = "Decompose a Nurbs curve in Bezier segments.\n\ \n\ Input:\n\ \n\ - P: control points P(nc,nd)\n\ - nc: number of control points = n+1\n\ - nd: dimension of the points (3 or 4)\n\ - U: knot sequence U(nk) with nk = nc+p+1 = nc+p\n\ \n\ Returns:\n\ \n\ - newP: (nb*p+1,nd) new control points defining nb Bezier segments\n\ \n\ Modified algorithm A5.6 from 'The NURBS Book' pg173.\n\ \n"; static PyObject * curveDecompose(PyObject *self, PyObject *args) { int nd, nc, nk; npy_intp *P_dim, *U_dim, dim[2]; double *P, *U, *newP; PyObject *a1, *a2; PyObject *arr1=NULL, *arr2=NULL, *ret=NULL; if(!PyArg_ParseTuple(args, "OO", &a1, &a2)) return NULL; arr1 = PyArray_FROM_OTF(a1, NPY_DOUBLE, NPY_IN_ARRAY); if(arr1 == NULL) return NULL; arr2 = PyArray_FROM_OTF(a2, NPY_DOUBLE, NPY_IN_ARRAY); if(arr2 == NULL) goto fail; P_dim = PyArray_DIMS(arr1); U_dim = PyArray_DIMS(arr2); nc = P_dim[0]; nd = P_dim[1]; nk = U_dim[0]; P = (double *)PyArray_DATA(arr1); U = (double *)PyArray_DATA(arr2); /* Compute number of knots to insert */ int count = 0; int m = nk - 1; int p = nk - nc - 1; int b = p + 1; int i,mult; //printf("nc, nk, n, m, p = %d, %d, %d, %d, %d\n",nc,nk,nc-1,m,p); while (b < m) { i = b; while (b < m && U[b] == U[b+1]) b++; mult = b-i+1; //printf("b, i, mult = %d, %d, %d\n",b,i,mult); if (mult < p) { count += (p-mult); //printf("Count: %d\n",count); } b++; } /* Create the return arrays */ dim[0] = nc+count; dim[1] = nd; ret = PyArray_SimpleNew(2,dim, NPY_DOUBLE); newP = (double *)PyArray_DATA(ret); /* Compute */ curve_decompose(P, nc, nd, U, nk, newP); print_mat(newP,nc+count,nd); /* Clean up and return */ Py_DECREF(arr1); Py_DECREF(arr2); return ret; fail: //printf("error cleanup and return\n"); Py_XDECREF(arr1); Py_XDECREF(arr2); return NULL; } static char curveKnotRemove_doc[] = "Refine curve knot vector.\n\ \n\ Input:\n\ \n\ - P: control points P(nc,nd)\n\ - U: knot sequence: U[0] .. U[m] m = n+p+1 = nc+p\n\ - u: knot value to remove: U[0] <= u <= U[m]\n\ - num: number of times to remove u\n\ - tol: allowable tolerance for deviation of the curve. See NURBS book, p. 185\n\ \n\ Output:\n\ - t: actual number of times that u was removed\n\ P and U are replaced with the new control points and knot vector\n\ \n\ Modified algorithm A5.8 from 'The NURBS Book' pg185.\n\ \n"; static PyObject * curveKnotRemove(PyObject *self, PyObject *args) { int nd, nc, nk, num, t, i; npy_intp *P_dim, *U_dim, dim[2]; double *P, *U, u, tol, *newP; PyObject *a1, *a2; PyObject *arr1=NULL, *arr2=NULL, *ret=NULL; if(!PyArg_ParseTuple(args, "OOfif", &a1, &a2, &u, &num, &tol)) return NULL; arr1 = PyArray_FROM_OTF(a1, NPY_DOUBLE, NPY_IN_ARRAY); if(arr1 == NULL) return NULL; arr2 = PyArray_FROM_OTF(a2, NPY_DOUBLE, NPY_IN_ARRAY); if(arr2 == NULL) goto fail; P_dim = PyArray_DIMS(arr1); U_dim = PyArray_DIMS(arr2); nc = P_dim[0]; nd = P_dim[1]; nk = U_dim[0]; P = (double *)PyArray_DATA(arr1); U = (double *)PyArray_DATA(arr2); /* Compute */ t = curve_knot_remove(P, nc, nd, U, nk, u, num, tol); print_mat(P,nc-t,nd); /* Create the return arrays */ dim[0] = nc-t; dim[1] = nd; ret = PyArray_SimpleNew(2,dim, NPY_DOUBLE); newP = (double *)PyArray_DATA(ret); for (i=0; idimensions[0]; */ /* nc = ctrl->dimensions[1]; */ /* nk = k->dimensions[0]; */ /* dim[0] = nd; */ /* dim[1] = nc*(t + 1); */ /* ic = (PyArrayObject *) PyArray_FromDims(2, dim, PyArray_DOUBLE); */ /* ctrlmat = vec2mat(ctrl->data, nd, nc); */ /* icmat = vec2mat(ic->data, nd, nc*(t + 1)); */ /* dim[0] = (t + 1)*nk; */ /* ik = (PyArrayObject *) PyArray_FromDims(1, dim, PyArray_DOUBLE); */ /* _bspdegelev(p, ctrlmat, nd, nc, (double *)k->data, nk, t, &nh, icmat, (double *)ik->data); */ /* free(icmat); */ /* free(ctrlmat); */ /* Py_DECREF(ctrl); */ /* Py_DECREF(k); */ /* return Py_BuildValue("(OOi)", (PyObject *)ic, (PyObject *)ik, nh); */ /* } */ static char curveGlobalInterpolationMatrix_doc[] = "Compute the global curve interpolation matrix.\n\ \n\ Input:\n\ \n\ - Q: points through which the curve should pass (nc,nd), where\n\ nc is the number of points = number of control points = n+1 and\n\ nd is the dimension of the points (3 or 4)\n\ - u: parameter values at the points (nc)\n\ - p: degree of the B-spline\n\ strategies:\n\ 0 : equally spaced (not recommended)\n\ 1 : chord length\n\ 2 : centripetal (recommended)\n\ \n\ Output:\n\ - P: control points P(nc,nd)\n\ - U: knot sequence: U[0] .. U[m] m = n+p+1 = nc+p\n\ - A: coefficient matrix (nc,nc)\n\ \n\ Modified algorithm A9.1 from 'The NURBS Book' pg369.\n\ \n"; static PyObject * curveGlobalInterpolationMatrix(PyObject *self, PyObject *args) { int p,nc,nd,nu; npy_intp *Q_dim, *u_dim, dim[2]; double *Q, *u, *U, *A; PyObject *a1, *a2; PyObject *arr1=NULL, *arr2=NULL, *ret1=NULL, *ret2=NULL; if (!PyArg_ParseTuple(args, "OOi", &a1, &a2, &p)) return NULL; arr1 = PyArray_FROM_OTF(a1, NPY_DOUBLE, NPY_IN_ARRAY); if(arr1 == NULL) return NULL; arr2 = PyArray_FROM_OTF(a2, NPY_DOUBLE, NPY_IN_ARRAY); if(arr2 == NULL) goto fail; Q_dim = PyArray_DIMS(arr1); u_dim = PyArray_DIMS(arr2); nc = Q_dim[0]; nd = Q_dim[1]; nu = u_dim[0]; if (nu != nc) goto fail; Q = (double *)PyArray_DATA(arr1); u = (double *)PyArray_DATA(arr2); /* Create the return arrays */ dim[0] = nc+p+1; ret1 = PyArray_SimpleNew(1,dim, NPY_DOUBLE); U = (double *)PyArray_DATA(ret1); dim[0] = nc; dim[1] = nc; ret2 = PyArray_SimpleNew(2,dim, NPY_DOUBLE); A = (double *)PyArray_DATA(ret2); /* Compute */ curve_global_interp_mat(p, Q, nc, nd, u, U, A); /* Clean up and return */ Py_DECREF(arr1); Py_DECREF(arr2); //return ret1; return Py_BuildValue("(OO)", ret1, ret2); fail: //printf("error cleanup and return\n"); Py_XDECREF(arr1); Py_XDECREF(arr2); return NULL; } static char surfacePoints_doc[] = "Compute points on a B-spline surface.\n\ \n\ Input:\n\ \n\ - P: control points P(ns,nt,nd)\n\ - ns,nt: number of control points\n\ - nd: dimension of the points (3 or 4)\n\ - U: knot sequence: U[0] .. U[m]\n\ - nU: number of knot values U = m+1\n\ - V: knot sequence: V[0] .. V[n]\n\ - nV: number of knot values V = n+1\n\ - u: parametric values (nu,2): U[0] <= ui[0] <= U[m], V[0] <= ui[1] <= V[m]\n\ - nu: number of parametric values\n\ \n\ Output:\n\ - pnt: (nu,nd) points on the B-spline\n\ \n\ Modified algorithm A3.5 from 'The NURBS Book' pg103.\n\ \n"; static PyObject * surfacePoints(PyObject *self, PyObject *args) { int ns,nt,nd,nU,nV,nu; npy_intp *P_dim, *U_dim, *V_dim, *u_dim, dim[2]; double *P, *U, *V, *u, *pnt; PyObject *a1, *a2, *a3, *a4; PyObject *arr1=NULL, *arr2=NULL, *arr3=NULL, *arr4=NULL, *ret=NULL; if (!PyArg_ParseTuple(args, "OOOO", &a1, &a2, &a3, &a4)) return NULL; arr1 = PyArray_FROM_OTF(a1, NPY_DOUBLE, NPY_IN_ARRAY); if(arr1 == NULL) return NULL; arr2 = PyArray_FROM_OTF(a2, NPY_DOUBLE, NPY_IN_ARRAY); if(arr2 == NULL) goto fail; arr3 = PyArray_FROM_OTF(a3, NPY_DOUBLE, NPY_IN_ARRAY); if(arr3 == NULL) goto fail; arr4 = PyArray_FROM_OTF(a4, NPY_DOUBLE, NPY_IN_ARRAY); if(arr4 == NULL) goto fail; P_dim = PyArray_DIMS(arr1); U_dim = PyArray_DIMS(arr2); V_dim = PyArray_DIMS(arr3); u_dim = PyArray_DIMS(arr4); ns = P_dim[0]; nt = P_dim[1]; nd = P_dim[2]; nU = U_dim[0]; nV = V_dim[0]; nu = u_dim[0]; P = (double *)PyArray_DATA(arr1); U = (double *)PyArray_DATA(arr2); V = (double *)PyArray_DATA(arr3); u = (double *)PyArray_DATA(arr4); /* Create the return array */ dim[0] = nu; dim[1] = nd; ret = PyArray_SimpleNew(2,dim, NPY_DOUBLE); pnt = (double *)PyArray_DATA(ret); /* Compute */ surface_points(P,ns,nt,nd,U,nU,V,nV,u,nu,pnt); /* Clean up and return */ Py_DECREF(arr1); Py_DECREF(arr2); Py_DECREF(arr3); Py_DECREF(arr4); return ret; fail: //printf("error cleanup and return\n"); Py_XDECREF(arr1); Py_XDECREF(arr2); Py_XDECREF(arr3); Py_XDECREF(arr4); return NULL; } static char surfaceDerivs_doc[] = "Compute derivatives of a B-spline surface.\n\ \n\ Input:\n\ \n\ - n: number of derivatives to compute\n\ - P: control points P(ns,nt,nd)\n\ - ns,nt: number of control points\n\ - nd: dimension of the points (3 or 4)\n\ - U: knot sequence: U[0] .. U[m]\n\ - nU: number of knot values U = m+1\n\ - V: knot sequence: V[0] .. V[n]\n\ - nV: number of knot values V = n+1\n\ - u: parametric values (nu,2): U[0] <= ui[0] <= U[m], V[0] <= ui[1] <= V[m]\n\ - nu: number of parametric values\n\ \n\ Output:\n\ - pnt: (n+1,nu,nd) points and derivatives on the B-spline surface\n\ \n\ Modified algorithm A3.6 from 'The NURBS Book' pg111.\n\ \n"; static PyObject * surfaceDerivs(PyObject *self, PyObject *args) { int nc, nd, nk, nu, n; npy_intp *P_dim, *U_dim, *u_dim, dim[3]; double *P, *U, *u, *pnt; PyObject *a1, *a2, *a3; PyObject *arr1=NULL, *arr2=NULL, *arr3=NULL, *ret=NULL; if(!PyArg_ParseTuple(args, "OOOi", &a1, &a2, &a3, &n)) return NULL; arr1 = PyArray_FROM_OTF(a1, NPY_DOUBLE, NPY_IN_ARRAY); if(arr1 == NULL) return NULL; arr2 = PyArray_FROM_OTF(a2, NPY_DOUBLE, NPY_IN_ARRAY); if(arr2 == NULL) goto fail; arr3 = PyArray_FROM_OTF(a3, NPY_DOUBLE, NPY_IN_ARRAY); if(arr3 == NULL) goto fail; P_dim = PyArray_DIMS(arr1); U_dim = PyArray_DIMS(arr2); u_dim = PyArray_DIMS(arr3); nc = P_dim[0]; nd = P_dim[1]; nk = U_dim[0]; nu = u_dim[0]; P = (double *)PyArray_DATA(arr1); U = (double *)PyArray_DATA(arr2); u = (double *)PyArray_DATA(arr3); /* Create the return array */ dim[0] = n+1; dim[1] = nu; dim[2] = nd; ret = PyArray_SimpleNew(3,dim, NPY_DOUBLE); pnt = (double *)PyArray_DATA(ret); /* Compute */ curve_derivs(n, P, nc, nd, U, nk, u, nu, pnt); /* Clean up and return */ Py_DECREF(arr1); Py_DECREF(arr2); Py_DECREF(arr3); return ret; fail: //printf("error cleanup and return\n"); Py_XDECREF(arr1); Py_XDECREF(arr2); Py_XDECREF(arr3); return NULL; } static PyMethodDef _methods_[] = { {"binomial", binomial, METH_VARARGS, binomial_doc}, {"allBernstein", allBernstein, METH_VARARGS, allBernstein_doc}, {"curvePoints", curvePoints, METH_VARARGS, curvePoints_doc}, {"curveDerivs", curveDerivs, METH_VARARGS, curveDerivs_doc}, {"curveKnotRefine", curveKnotRefine, METH_VARARGS, curveKnotRefine_doc}, {"curveDecompose", curveDecompose, METH_VARARGS, curveDecompose_doc}, {"curveKnotRemove", curveKnotRemove, METH_VARARGS, curveKnotRemove_doc}, /* {"bspdegelev", nurbs_bspdegelev, METH_VARARGS, bspdegelev_doc}, */ {"curveGlobalInterpolationMatrix", curveGlobalInterpolationMatrix, METH_VARARGS, curveGlobalInterpolationMatrix_doc}, {"surfacePoints", surfacePoints, METH_VARARGS, surfacePoints_doc}, {NULL, NULL} }; /* Initialize the module */ PyMODINIT_FUNC initnurbs_(void) { PyObject* module; module = Py_InitModule3("nurbs_", _methods_, __doc__); PyModule_AddIntConstant(module,"accelerated",1); import_array(); /* Get access to numpy array API */ } /* End */ pyformex-0.8.6/pyformex/lib/misc.py0000644000211500021150000000573511705104656017175 0ustar benebene00000000000000# $Id: misc.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """Python equivalents of the functions in lib.misc The functions in this module should be exact emulations of the external functions in the compiled library. """ # There should be no other imports here than numpy import numpy as np accelerated = False def _fuse(x,val,flag,sel,tol): """Fusing nodes. This is a low level function performing the internal loop of the fuse operation. It is not intended to be called by the user. """ nnod = val.shape[0] nexti = 1 for i in range(1,nnod): j = i-1 while j>=0 and val[i]==val[j]: if abs(x[i]-x[j]).max() < tol: # node i is same as previous node j flag[i] = 0 sel[i] = sel[j] break j = j-1 if flag[i]: # node i is a new node sel[i] = nexti nexti += 1 def nodalSum(val,elems,work,avg): """Compute the nodal sum of values defined on elements. val : (nelems,nplex,nval) values at points of elements. elems : (nelems,nplex) nodal ids of points of elements. work : (nnod,nval) returns the summed values at the nodes On return each value is replaced with the sum of values at that node. If avg=True, the values are replaced with the average instead. The summation is done inplace, so there is no return value! """ nodes = np.unique(elems) for i in nodes: wi = where(elems==i) vi = val[wi] if avg: raise RuntimeError,"THIS DOES NOT WORK!!!!" vi = vi.sum(axis=0)/vi.shape[0] else: vi = vi.sum(axis=0) work[i] = vi val[wi] = vi def tofile_int32(val,fil,fmt): fmt = fmt * val.shape[-1] for row in val: fil.write(fmt % tuple(row)) fil.write('\n') tofile_float32 = tofile_int32 # End pyformex-0.8.6/pyformex/lib/misc_module.c0000644000211500021150000004034011666766507020342 0ustar benebene00000000000000/* $Id: misc_module.c 2114 2011-12-04 21:59:35Z bverheg $ */ // // This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) // pyFormex is a tool for generating, manipulating and transforming 3D // geometrical models by sequences of mathematical operations. // Home page: http://pyformex.org // Project page: http://savannah.nongnu.org/projects/pyformex/ // Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) // Distributed under the GNU General Public License version 3 or later. // // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. // #include #include static char __doc__[] = "misc_ module\n\ \n\ This module provides accelerated versions of miscellaneous pyFormex functions.\n\ \n"; /* Dot product of two vectors of length n */ /* ia and ib are the strides of the elements addressed starting from a, b */ static float dotprod(float *a, int ia, float *b, int ib, int n) { int i; float t; t = 0.0; for (i=0; i= 0 && val[i]==val[j]) { kj = 3*j; if ( fabs(x[ki]-x[kj]) < tol && fabs(x[ki+1]-x[kj+1]) < tol && fabs(x[ki+2]-x[kj+2]) < tol ) { flag[i] = 0; sel[i] = sel[j]; break; } --j; } if (flag[i]) { sel[i] = nexti; ++nexti; } } /* Clean up and return */ Py_DECREF(arr1); Py_DECREF(arr2); Py_DECREF(arr3); Py_DECREF(arr4); Py_INCREF(Py_None); return Py_None; fail: Py_XDECREF(arr1); Py_XDECREF(arr2); Py_XDECREF(arr3); Py_XDECREF(arr4); return NULL; } /**************************************************** nodal_sum ****/ /* Nodal sum of values defined on elements */ /* args: val, elems, avg val : (nelems,nplex,nval) values defined at points of elements. elems : (nelems,nplex) nodal ids of points of elements. work : (elems.max()+1,nval) : workspace, should be zero on entry avg : 0/1 out : (nelems,nplex,nval) return values where each value is replaced with the sum of its value at that node. If avg=True, the values are replaced with the average instead. (CURRENTLY NOT IMPLEMENTED) The operations are can be done in-place by specifying the same array for val and out. */ static void nodal_sum(float *val, int *elems, float *out, int nelems, int nplex, int nval, int maxnod, int avg, int all) { int i,k,n,nnod=maxnod+1; int *cnt = (int *) malloc(nnod*sizeof(int)); float *work; if (all) work = (float *) malloc(nnod*nval*sizeof(float)); else work = out; for (i=0; i 0) for (k=0; k= tol) { //printf("setting %d = %d\n",i,j); par[i] = j; } } /* average the close directions */ cnt = 1; for (i=j+1; i= tol) { //printf("setting %d = %d\n",i,j); par[i] = j; } } /* average the close directions */ cnt = 1; for (i=j+1; i>> print [ binomial(4,i) for i in range(5) ] [1, 4, 6, 4, 1] """ f = factorial return f(n) / f(k) / f(n-k) def allBernstein(n,u): """Compute the value of all n-th degree Bernstein polynomials. Parameters: - `n`: int, degree of the polynomials - `u`: float, parametric value where the polynomials are evaluated Returns: an (n+1,) shaped float array with the value of all n-th degree Bernstein polynomials B(i,n) at parameter value u. Algorithm A1.3 from 'The NURBS Book' p20. """ # THIS IS NOT OPTIMIZED FOR PYTHON. B = zeros(n+1) B[0] = 1.0 u1 = 1.0-u for j in range(1,n+1): saved = 0.0 for k in range(j): temp = B[k] B[k] = saved + u1*temp saved = u * temp B[j] = saved return B # End pyformex-0.8.6/pyformex/data/0000755000211500021150000000000011705105304016010 5ustar benebene00000000000000pyformex-0.8.6/pyformex/data/blippo.pgf0000644000211500021150000000143611451703073020004 0ustar benebene00000000000000# pyFormex Geometry File (http://pyformex.org) version='1.5'; sep=' ' # objtype='BezierSpline'; ncoords=41; closed=True; sep=' '; degree=2 92.0 47.0 0.0 192.0 8.0 0.0 277.0 -10.5 0.0 362.0 -29.0 0.0 440.0 -29.0 0.0 710.0 -29.0 0.0 883.5 116.0 0.0 1057.0 261.0 0.0 1057.0 485.0 0.0 1057.0 695.0 0.0 921.5 827.0 0.0 786.0 959.0 0.0 559.0 969.0 0.0 567.0 1005.5 0.0 575.0 1042.0 0.0 786.0 1042.0 0.0 997.0 1042.0 0.0 997.0 1251.0 0.0 997.0 1460.0 0.0 631.5 1460.0 0.0 266.0 1460.0 0.0 179.0 1081.0 0.0 92.0 702.0 0.0 162.0 716.0 0.0 212.0 721.5 0.0 262.0 727.0 0.0 305.0 727.0 0.0 449.0 727.0 0.0 535.0 671.5 0.0 621.0 616.0 0.0 621.0 524.0 0.0 621.0 444.0 0.0 553.5 393.0 0.0 486.0 342.0 0.0 377.0 342.0 0.0 320.0 342.0 0.0 249.5 357.5 0.0 179.0 373.0 0.0 92.0 406.0 0.0 92.0 226.5 0.0 92.0 47.0 0.0 pyformex-0.8.6/pyformex/data/supershape.txt0000644000211500021150000001651611307126177020752 0ustar benebene00000000000000{'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 1.0, 'grid_skewness': 0.0, 'east_west': 1.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-0', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-0'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 2.0, 'grid_skewness': 0.0, 'east_west': 1.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-0', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-0'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 3.0, 'grid_skewness': 0.0, 'east_west': 1.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-1', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-1'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 5.0, 'grid_skewness': 0.0, 'east_west': 1.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-2', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-2'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 5.0, 'grid_skewness': 0.0, 'east_west': 2.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-3', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-3'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 5.0, 'grid_skewness': 0.0, 'east_west': 3.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-4', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-4'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 3.0, 'grid_skewness': 0.0, 'east_west': 5.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-5', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-5'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 2.0, 'grid_skewness': 0.0, 'east_west': 5.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-6', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-6'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 1.0, 'grid_skewness': 0.0, 'east_west': 5.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-7', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-7'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 0.40000000000000002, 'grid_skewness': 0.0, 'east_west': 5.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-8', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-8'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 0.10000000000000001, 'grid_skewness': 0.0, 'east_west': 5.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-9', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-9'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 0.10000000000000001, 'grid_skewness': 0.0, 'east_west': 3.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-10', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-10'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 0.10000000000000001, 'grid_skewness': 0.0, 'east_west': 1.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-11', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-11'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 0.10000000000000001, 'grid_skewness': 0.0, 'east_west': 0.5, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-12', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-12'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.0, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 0.10000000000000001, 'grid_skewness': 0.0, 'east_west': 0.20000000000000001, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-13', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-13'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.20000000000000001, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 0.20000000000000001, 'grid_skewness': 0.0, 'east_west': 0.20000000000000001, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-14', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-14'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.29999999999999999, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 0.29999999999999999, 'grid_skewness': 0.0, 'east_west': 0.29999999999999999, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-15', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-15'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.5, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 0.5, 'grid_skewness': 0.0, 'east_west': 0.5, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-16', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-16'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.5, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 1.0, 'grid_skewness': 0.0, 'east_west': 1.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-17', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-17'} {'x_range': (-180.0, 180.0), 'scale': [1.0, 1.0, 1.0], 'grid_size': [24, 12], 'eggness': 0.20000000000000001, 'color': 'red', 'grid_base': 'quad', 'grid_color': 'blue', 'north_south': 1.0, 'grid_skewness': 0.0, 'east_west': 1.0, 'x_clip': (-360.0, 360.0), 'grid_name': 'Grid-18', 'y_clip': (-90.0, 90.0), 'post': '', 'y_range': (-90.0, 90.0), 'grid_bias': 0.0, 'name': 'Shape-18'} pyformex-0.8.6/pyformex/data/sections.db0000644000211500021150000000341611705104656020163 0ustar benebene00000000000000# $Id: sections.db 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # section name=koker cross_section=500 moment_inertia_11=1256 moment_inertia_12=695 moment_inertia_22=365499 torsional_rigidity=635256 endsection section name=IPEA100 cross_section=878 moment_inertia_11=1412000 moment_inertia_12=1254 moment_inertia_22=1140000 torsional_rigidity=1542 endsection # The following style could be used with units section name=IPE80 height = 80 mm width = 46 mm web thickness = 3.8 mm flange thickness = 5.2 mm fillet radius = 5 mm specific mass = 6 kg/m A = 764.3 mm^2 I11 = 80.1 cm^4 W1 = 20.0 cm^3 I22 = 8.49 cm^4 W2 = 3.69 cm^3 endsection pyformex-0.8.6/pyformex/data/README0000644000211500021150000000221111666766507016714 0ustar benebene00000000000000# $Id: README 2114 2011-12-04 21:59:35Z bverheg $ ## ## The files in this directory are part of the pyFormex project. ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # pyformex-0.8.6/pyformex/data/butterfly.png0000644000211500021150000060436411256152777020574 0ustar benebene00000000000000PNG  IHDR+ pHYsHHFk> vpAg`XIDATxד-Iz'}="L}(ZF/K\lg^8?/|7>Іkf5pgtխZԙ眈plj<"ot]'?gZcIUUUUD.p4_9tUkj~`V=x1~fYכNB0k[^OYك8y1Sߚ :"0'nAO6M񡹛qv1f%4.;D< 9Ь'icLC|jyNߟ vk}q恸41.FC!"k8fiKcqrl&SxMT:bE9sV&(SDwͷCܓOW{tgs,qOv:nښS9glTRLPvGPd#5sDc&;󖇊@' včl*hU!6nLkE8i3ωLqؖ|t59i͛O5b&01.f{fRS?̖7O+Nߙ6#2|$ҼxI7k`6MvoS1eyz4 !DM6kFD22z@<z&شf9bb3ӣPiv8D #2Ģ9B =sħI}jǏ_X4w׼q /cc~eb5{~ͼn";80W O|YD?I 7S5ׁ#r&8_<ǯ~88{lzOq3ӯZ2 PG1c>ӜwhGy45LR.'U@82 ,H&#)zT'hhzOMhc*3e-f}饷s(0^O P2_̖{=}14K]MYՑ,ӌwT8Ey_|Lj~~|n5cryGgYgwL 1b/3#9]^v;O@8۰!z*KU?]  6'K:zfD\kbbȚV꽤i;}t_R;u+G|_F~gb}7^vB#(;8eܯSE ol4/;evh{ZDXe'ڮ8:v/G,T;(O5=ygj9'AFݿ2M7-~ {2=s};N${!"".{I<3y`ǜzyqn}]דtU?8N+smGYݘx}j s}2;4;f3 a<צ_צ;}zx<=,6{WZoQ9Ơj hǀQ1 kb}a:× O']fߞ2EmylvyG78Ty\F1OUÝC~w.J֧ˉ3+U7q'@SBA]Й߱2tm=-~%79 rμtEhO*"g>|4MG9>fǜc]';cIf.-,3 adP?O\*bGNDQA{"X5=ݠDG+N"kMR˄.v3Zy:I]͊?Z9l,Ngi L)+xhOy8a12΁]#^, SqϕQX2AM1) B }mޯԢ>U'>kI¸f kXAկt ̡Şǁ&:d0Et ,f &&#?qv-ǭ۬PUÇ#`l?YA TΛ bmMbPE{.>LD"BDL+qi?Vu,TҊ"2"eD|PU 4ZDD~(7hII=xtA 8Kf7&EL Yu)Kj"e{6H4A"cpT#g)s*!@{F͓͐ 7U9yP:BQq[$8I倈LHâ@'ǯV-@2Z$ *Տ"PEaܥ6>-kmSSp3UNZ=Byyri1kSs:&4ЀJc(V y G VU=DligE ̬aL~|9ĿɓVt8;`|DxUU pc b$hu eE0Q&κzyB;7DJ'Z~Spw4vփRU_zm%:m8B)oiӸAU%P<j$Dj<4&uǀxzzLK!"gl14@@i_WUq*21;M4(&UU!PP `@d0PRG=W2 1"9v‖L`ĕFߘJfJbJ8nOqµ4̚J8̄99!]7wm$E{pnNY2im?g6Z"bxZDP<pT  0-0 bU F XLE ̘DQ JjJ@U{nd"q>p? !(a/ojK11hODB92yADً*5G<ǂۗ҅if1wuz1en#-ZfjBx !Jl8-"/'ݺsέMʋc" CD|ggѣGnʲ,I|.,,g>1hXZiQJvs1(X@ Ï3)][23ê@ѝDDUZz ;޾?t݅啕Ν;wԩN[>̈́dXU ! &A P)(!r= ]xO(8u xx#e3ѿNQݴM:4V Ҹn>6S*1Yǂ r1jP 24dd )srFOݻ};mB -a @RDTA *+}>ݭqi^gvКTEt^Bl04?\e**k%XP_/Sƽgn~tY+HTd"bRfibUd(szGEYe@jnj~KkgΝ]ZZb"b)!:&M;>-F͵8s"R!C7g.C1QCX34S?k[tN$1j~89 cꀝ7Gql*S<ݼQx!(g̲ZpEB0LZ/r7vcp(!aM-S%QI( !D`ǖ;S kgtK$BB0X[P?eY6,QTC;5$΍^\W_A'l!BD,(=6̑rQR۽奅3˫YR0D @͚;!3v3}9[v3a覺IA&Eتemx4ΦN٣=Ś0w|@*W1 ` ) YJ37Y߼s';[Ýݭbw0ek-jØwlM0Ȫ*N0k%I\z3 DoL)C L vsX=f6T[w}zM`wߗ.eR(^D!fkm-˲,KQlnn>z8`lڹKO9}Lub&眳4M~ydj,lq gVk`'d)vg'W6vo[=E=TQGyJMQ""!|[$&ij9ys6׷6!jjuӖ$@wtvs;6z0>Joܸpkho .ɲ >ؑjeOG [.eNpT%{IM+!xlń:Aa0h)E*%8QU$ : [$Ɍ1$1DGa4wmuv WϜ//Zv%֋82!ǔ|gMCscb7˫0,Zj#eE8n>!THDP%eݻw<_H*!MXX^ITmeB1^s 2ʋ&V QQ1F ;htzV^L$6s)4Mً0AY{BjȗefΫu#- kLMD76$$NUHQ""$I%4W,:ŋVVz0Ǭ9keSRX9OZ|M9TͲuݣ_/}=v VĚ,r$x`kY!,YTK!Ȥbb"T8 QTh#84aSO(|$!IE zhBHƥjZt7QYf  (af b,zhkY[GpTF[www˲df/,.=z ,J,DqEͫ*;N4d5%GsSBo~Bj2E\Yj1r J{d`֕o?G,[e&6Pr@0f  .`!&kO1S4<#V©$k+ ;_;VRӪATSvZ+"R^,PcxTVsSJW8j9Wғ$a@f,b{{;/WVή>r[̲l;AAbc K i(k4QY%˲fznInz̙vE@c0U/l,VU{I3+dC$^v0ۿ{yDU4v\E)ιVj7VVW-..e]*#bF ?M}3in|g'uH+x 2~VϜv_t:D<Jh7no(m( l{]OJb=IHED n>\"κdx\v̈́83`UU?Uяy,`5ZQj̓6SMT-OQ2wPD@W @UH40 Sn)pw_As$`s=M  VDxYoԲ(NA @4S 5c.Mӳ ,΃'׳N`i VҘ,X[kOvrF?km,ѣGw?֖1fmm-oiƥS`0{a]XZ<{™s16Bs.DB{Βe֗n0CݬG?r0Ȍ=wܙ3gΜ:$6MSAZ[~ww~1Z[[֎Fҕ6IE^VZͮ>ͤ8.VQU="MnC (n߻_ːV^gUJye7{bpUqTV5mD(`D9i}(D-? 0dcP DDH!3ϿzS 2P ʤLV >1b!%EU}ի<``IJ ^E24&:cLFTjOPnj jRD(J"@@@Dh2QᲠM#dK/FQ2;fV>p[|-aQT4X D'$"Mou˴bm2ь8by!"*p΅(&l ZE n>sΓ]tլ36J`%P8v4ɺH,j v"qQYqҥ3g\|ԩSv#[k,[ZZ{Kٿ|rKt(2T*pH^|@= &m&+̲,"/Z&YZZJb_ݼvΝ[$nZm)G{{{joJZb cs뉬fu 1䀪gR,CH4Ip8r;*Hv!AӴYKlo>0J)(" TL#VG!QAAH@@<"#2"!1Vn)@Hph8+XlĹ:a-ĮK 6~{+xnBoST=@FQX h{TsXnc 0IDvo]|啕Sl bPω!!8xMVϞ;A9vf"##0 t!~6I/|u6-p`eV[lF[n|ˏ>`?˯lVUGPNq@&e.ޕE&$@,M 'CyĻy)h(R QT$ATkuQ_ί>_i(ihTN֊nHBs?QR`$R)D6<=k:C gW48\h'Zzc]GcFcLpҥK>+²̅TD%r0 !Fk&n=ߴ4ǍF5JDIF2!x]|PI([ׯ__|yŕskOe>3KDdb0-9qqbyB(˒^y啐Kg~| ڈ'I'`0gΜ9|;ׯ^zjQ~W_?~|ΝݻwO9ӟ7ߪ}(MT)&IA6tA=0sΫZ-j2 ;7+_쵓$AR])(LCpL,x؆Rb,Nd$=vƢ7AUq((˯ϟ{<LM) Bi ayPTsH@AK@nimϳvo, sL;alߝ2s>'!R*3y3A |W_|ѣW|}W@2x6@AuXhqI=F (5FBhNJNSy ^\r)1%Kb{gڵׯoܻAIn~8 3Ɍጙ -8rrh Z]n6pLX8XլML܋L$Ʌ :֧~_r4>@D7B(0zI)9J$ hx7tZ'9)sN t'6W\:nzT܍$S|VK{$߿+w]zkxpF|$D=%LT+.ɂ`5)eN3_lv"8F3,3Q=Jwuee,1,;P@%VN 1bԞ*s.nܰ(Ј\tH+Tq:W+tXt~lMUĈH]@ BÁM{VhnkӴ[?zX;շ/^Z 0bS",sǠfTtiiWfc٨ #)\HKgO}~sk{=iDݨ~meAKm !$EJSk}ѓΞ=߻t钤/ZP)CĴՉ _42UW޵ɌE龪 vx8P OmW+Y+ o?ۺwk+`o:Ws$igO(`UTL `4j`_n}TPǒxC94b'!InDR"beDdk{#nZ pR3,h4Q :ns53O=0#bQMU39iA{*("{@b&$f=̚,mZiyg}u7޼Kv:M̃Q)O"H^U#\q`HD"Cc`0hɇ}EQ"Mygh?n ޿ʕ+_~s~WזUՅ`%"=NQ~DRqt[RK1iC}Sn'nʵayyyyyYϳ7,Lp@+voo/ȌF~g}+++VK:Ѹ;U&x[aW0nݺ_޽s?/_,Nm`4M /ajyxZiCC<'U`ʕ+?՝[ gOkYܿ&cZIx1nf-*O1+v6VF!ǯ0{ԄVPѼRvf{yXxqKYL^V-uXv(Jh#?.f@"Hdʲ@ӅG P%EDD\f> c JDve\STF" xi Ab~})eѣGwR>{?,_|k2o$ B҈v{0yPLJIhXB5{v98vϩ,J+w?`0xG?O>Pp=؋+`Q))b |+^FbVb朵 iZ[{𣍍?:ujqu*3[U=^j fruG;4VqТڣPPRH;-yY䫝>vw\4ϿeVss(l$if,2 h6: hX%>W""26( U|Qب3%Q3U#8s|G D T"@vE^~=N6Z=qeow5Meͣ""U,![}xo 3zAQf`2~E@C2jR~Xk cA#U=Wj2-TV@b*3BQE4"bVז?\w\ZXRv ˋW͑laٴzhVCI%DcݝN28`B+,p#a3lbJL9ׇS}<֣GDH;Iei;l秹.htUOcMDDՋUj87nQQѥ˯,..$<3rub\i$lƺ@O ՊERjl0  "kk-QұozR]|ӧr:ȵ;;Hª1߻q+MS7lLYlLnl@Dp*@E`<#a#&5*Sd,!N9AdÑk Ѓ!d@ ./]pܹ}jFk!W ߸7[Ⱥ,Iԣ,/Տv]~U` @@ +M3 ?Idyvqqg0q :v ȠJc>J10fnκ 9ڋIj+^E{R ` BooF~oVOH)gin^=A|3 XA^5WD@B$%$N[0 G$f3}ijn:|'K6U7L3jwl$!c-ICqQDK*}!"i Hdb 5eTvX Q37W5W2a5PEի#lioVV P>6 X"Zyж8ocE4#fcF ` -c5&Sá/Xw)mC &4 5&!2DFX@bEx_ۭ}3;;;V0l [~'OKvv`0HN/_G%ZG]s`gƹ>gvU[ְQJu"@S= *(M3gWW~s捖SKN"S^j{fGUDTU*vl[:8 L4@W38^EDc ""*ǾIaFsz0J:ġwݕ H(c%+C"d08s{B͇w6wud0ΠYӨuVvÇ Űe%7LhHUEBȌUJ8@ DUDE@E@EKp!0AG@S" AEW AB # `{@HrU@PBE$j>uej9fcӠ12BhZ`Oɐ%IU\k,͘bt)AHT:00GT5 `0I$S641"⋲$n{kow# ln">~tì^;{nC}b cǚc0y_NMẀ+c8<=z4BK*vC}»vZ/w@+IMb a_;>fD""nv8/m)=h:UE<;ڔim|dcp +K??ٍ+߮.-?}ʀZ/_z]~҅Ο?zF.Uccb(qBS'ā|}kƻ4cpmw<\M]%4ѿ!5ΝpDXx/@PQT@ZUSغy& +Yk0 >HHh]cc@T 4X(1=3RBl$B40(J4XSJѰ1VТvbR&)M X k/?w,o?~`{sN` o?G~¥^ߦ4~V$ (+Rz:5FFi)Y t]CboܩS[@$A<yQ`V~݀"1"֐2TFǿ䓝ono!ѩ3vv()18tysB0VH"E4#TNI^o#\\ʛb;w.\.|W_UR D>@p53:_ooizҥIvQ{ @HIO6:K+˧uz|cE Bt=)^)J}Y.o?yԩS KݪI`7E)+^;vS@ձ ,Kk-Pܿ{믿eY;mUU9F=Rfl`7񺪆.ؔq>??naN[owN] D"nB5#G0s47W ?iT{_eYHNZ]N롨"!L*QRT, = 34dvDܤ@FfNׯ^à|& DUi) McQETT4֘/jBCA6Il@$%fPg҄B<йqp&R,|iwBrshP;Yv4*(YLw\3хSkI [4Rg*dV c5侞ls4DV+/ _N;CvvvΟ;&DD(1 S6C 5rRfhy3GkmV swn藿/ϝ;]X$Ҭ%F"ApKhefuĀc@3B"49KŨOYނt4ˏ~?/E5}u}D?[=- {>C^IGA Xd1) qh XeYEQ8E;< ]*=X ΋ Dш+‰Zk}yQErzׅL hDDU4BmaW9܌Ic1D,MJdbG5T]a !'!([<ȁ%7=*MS!j M2Z#Iօovn.oO[s;N9+F!$E~s}?  NeݻE>4/Hx6F{Z eW_E_\v?wV=`00z'(/#Y1 0;꯯|pNm+FՄ J{k |9BpۿiPkm|%^L>ܼz&IJoeLDhuP fbQC yYHTKuqy<*!rΉx4¥KrEAdƘk;5(!0t̥/T5k +JT*sqXU1AEU4M-U\@ &ƳʫLb/ p!2$"b!xC4$Nw©[g+\;Dne%Aܖ_\%5 }ٖre3=h+}fP˂4muFEa ("db<_cgyM)heu_ڷ^vieԹijA0U6o<J8 fŇL܍{w\2yq DLsŔa0=&abʂ3pL)ў-6PJY!,d-W珮|vm٤IB퍻ׯz~Z{y^(67oݼyVWz= qEamUD0^ c)Sp!M i]^Bf:"E7U2QW t"Ρc "26 j-...,-1Eႀ5uF=ڥ @ SvèUAI'Ư7M ?GDFD#Ho \-qY}40 FIC $ITB.v٥&}UqaaBȋ,9s崻t,U6ei*zhgg{ީ 2csZ5lL>C9\a16Ͳp?˲O~dͥ^o4z'-H=<$M[~uAEB TofuSDg*rgJU/̠Nk1;u,3ooo?ػu뷸 o֥SJݺ'{f*#t!=8P kZoFSݩJ!;jV8 5bGB戋0e[cLYT,`@;sXqIQSJd|@fbģ*h%ceDd*D^ 4B\ED 0!fqi]6 6䃖{p5RLB+a2AV;yW޼raA[ *xXwK&ýp|* xс s\SklVZ~r-+&6gݮ{QaA:I۩/|j3UJG}}?sj/='*3ev'-%#DՀB$G۷}gpuh5 ˭B%q|M"ׄ1c+т5mWկ>|jMbA.:},wobg!2޷Ѩ W4pZh.Bd "٥ikl,}Gre^ Tz k#&!s _j IBu{{{0DRc$"7ivC3ϜZ{PeN)Qq6LGl괳a40o_~s;/;,J0/_y/_ D$Ud@0190KU_L,2fֻGG_~FJڭG^}cM8򻻻Ϟ_[[3>1fy啮AB^Z?s ong acLj QiMM(j,! 2yb`빈_{bܽVjh0,2'RFD͓jbbjr?(Fyb,AD Q?<UVD1> (++DKScF%fr&9<cS6$U8`{Rm:ĠрY0䠥'PB!"'q͝m^A׀@PItoܺ tv`LMu@bcSJЏcH5P]yݷַ>|xͷ$@7HMJF(Qloj;`y86RL+5Ltn-kLcfPq`ko޵aY"+u(v WzƂ|앻hSF#Zl]N& ˩ovg{ <*AsR- ٚHjVWE5T`C| h02Y^6EqdN:Ufދ B8O j:*X4XNuU C'ѡ5c__!\ Z &Q "b w.x( ֩SkOanOJ7,b86Ҟv-Bʋor?ejs{ 3'I̜XK,y;Lv:N{oo/--Anlx UiBFj4hۃI:s#"J$χKKK/\z…[[[Ncs>Cr͛7/^JG~DrMMSjlZ2"_֕ݽ5%b1ZDJЦ6a 6IܻlZņ|Bh:io_=hkc0,r$LPĕ a4YMr Ýz?B'q LA5T9802T+3j.)IPU`5xB1ud?}挵O>Yd?xݍG~oo{n([2&hL5x lo$WFP yVNIYAN^P [׮/y-zN}HTn%IH UĎnѡn,q*!4( ( bP 0FvJA@TYTQC DԂ 0G'&g.[R-bggg"pd 2yFhHDL4_h1!V[4Y+Wr$oenҤ"aN LH11Lbqz .<~/<vgYEY&IbD_}5g_1B-^Fo޼_ܹsgϞqr$MȆiyrֆ+@Yyۄ;~>v'wՇȇ14Fb!flG }C$II#b$-и1Vk6V+]w%Zk `,KCjx%n8ΑFLvOe<!EUql(f8q<t:Z0MHUu.55_[9]sU~Xdk v"RXWo?hbM*ᆅa j(-,1%ԆN7YZ.WN/b mX4pBwq_Qy(JQ 'ްth-R%TAED@``"cS' +oAuT\xӭΠ(^&d,*(SEW֊U]4qH;yD4MĹraa_}g.^~YzN-u؞(|E }',Ld}}??ѿ:w|s/"l ܩ9ݦPG#WQH$ESxѡ[) ޹'{Tfbd|+%TbBһeޣ[+e[- a"}/$MmS@d&Yf(cC "Ikg2Ejƀ26 =MFrĊ1*WUU[lQ!(TÊ$RfAC^sQѥ?6DĉmV*65+kX&=l&$5USn#":3ϟvNk|9 dNgIU TD *.ͭVT T/J*ulUDBVVV6ׯ9k~n5u~QB4<&)PjgϞxc{{k/ ˋ(4M_iwQީj$ι۷o?>|ۭ?'"щ}.H2>i !c, сA1"l_Nj!#jPaNz"t0xΜ]+itC(0sŅ&p+^rjayyjIᶷwrTUҎtqx \am%6 6PD1Z8|s1>$[<9R%ۮMH"L+3+sgW++S=֗O6%:;a)~HI4^1>s;2iP(•e4Q=(Q%5MQz%Nݻ㍷{Ky) f!a!- cx0)S &6Mͷ߾s'_|AuSG.(,箩7f}N1a`DPfVϾopn| YNBBFJ`A $\TNe{$_կoݺe~:1&BfteB 2 "GUE"* %PEn?yAHSA2KL1dno?5\|˝|`VWܽ0G!UP"TDTqw VTP"ĪU%CDĠs P ;jc7eU-zKM)E~qw`ԷIG Vt @sʕ+7\^{mk/,iU ^MGbwwwwwjۯ.]m_KA ɩSa4mJkw js62*Bgބ0SY"vӧOcSV1=i{Dw[!tW`"LA}DKq887/bxQb1D(I =zD$[]np#XbHD st$wK%!0 %ꆅߓn'cqE($ψ < !bA!XKBT~ d-R;,G7|~hSlx?x5 q8d02wudrF%T٦gμ7ڕ5Ϸ$xU!hH'3}N6F%A3$Rzʲ,i4$, &`YAxHbhmf}q7~|oΝSY;Iz_99l 0(lD"RP8 j_BD,ьZe"e!"٢-F{[x\lAI L >h eUH"`0Ⱥmkl|e~w/|BDxp;~֊1?x}s?\)|?Z/ F!H;[~}+C᳒s H P3}N1B Ċib9׶dP[o]:O;= (@0h8EQP ਱`dxT A(/Wt}}ѣG(%T 3DK&צ@)p3 ""1}eEU,NpW^/uâH,Rc)ƛåݙ#Ԟ6 %IB &ˡc%n+_|y;^XIo'[ VB / PU{ >aJ .speHjT"4 ^@S)SSvޞl5weʊ~_ٮxIO ͭM"ZX\X\Z6+uX jRT84k4ǽ~A&QQ5HBm}3$I1Əe2FLoG]RA Ծuq`IyijAB!C1_{W_H >$vӠʲ1C*>s. x䉆$/`U]ADMDnjH IDD*J^!0u$fVԢ(ҹs~s~';yßwK(awDPqW䲳+o\^Ilk8&K?bY^gJ\QP2۵;5/L#?$)"`Ͳ D 6}7~?O~o|v @-IRo(=$d{/hB("bdk{qujva1!=,z[zmg{dk;~sEQ$)"C`ep@wx6V\!'UӵaM۽;kv+C ART51 5LOTC 6lǏU"2. "bd9QUT##bFDRD`PT@¨ET^yaWWF7x|ͷ^W^SQ*1*M  #L{k׮~W{6 *HDfpz*̖-ct_QTFTxɽ[?/`F.D򢍾VG8 &MBD4* aޯydzݻG|hMf1]@&9p<;`wss3.ˑ%hb)OU(\9HM8^yR{X맖{gN-,-N-e=@,t.v!vfYg/,,6ܿh?/"iu`m̸3c1멌%:S5cw:` GR=c4Xcms2N ϋ/>y+&204uxv#?TiE*H !֭[}⛯][:w:˲AZ"ƈ"$x766n޺w'F3h(yGB4f+"!ĈdAf#"F34FDHqss chQ_O+++9>^?245{p@$Iʼn# ,jkSIҽmc믿e駟~zmxw,K$ ״ա:6y_}S %`Q_ܾ}n_|%Nj.v'4Т( bwgƽNX wB׮]{cLv;iF2mzn޹Cfb*p%(&D$*"F? maccoX:v5kVι})Zk(!(~jEDpeܳ5 ըzvXˣ^$79b1@vd /\xG7*as6JUJC7umJ+9H& E%Z䰇rcwn{W.FێyC)Z ]^lÇ;{-fI{@bd,B1F] "-D$(S2.J"(ApڽO덍d% n  +zVZIˢ ˢ A8~rx|DT$8Xd1[8gZ]$-T־߾uڵkd… +Fjr M(Z$;o~p`6B$Qfub)4e BhGo/~ϖ'bgh{}u ;-f0AX2t];{D bŠ@4Sx̐T@8p#T)4*#tMԟZK}jv%6y(|ƦtoT1R^ crf(==&!Hf}8Xxサ7o)6uBF0!$c:Y}?=8 c*QRm敕 +g!8b߶bYm)cljNZ|Çr%tHb"LE+M<-"h5JG"EoQ@,)(Z Jqi-.2g4i3 p1RlӎI{p{WKMszm;m71Qx1H1;^l ιn>.77媾 ڎou0^bnoo;ϟe&M(h}m yAzikʠ$ ݹsyϡ|o7uɷ[?_{緮CxKLV7KVUs̹3gz} i$Ǐ?SQG5uދ:QSOJh)K-ݽunw.|jIQE = (oݽThd~:W)"ˆjSh[M8$DZ<6ch!\- KQY+*]qyAD[;Iv .?z ! (U(m>`QA}Tq\BvQ`|bs(nP"4itΝ;,M HԵH|aXHeY|owɍknܸ;cL 2%D@AP aJQ CWctj^j$̢//ﯮڿ\[;j4M;yK0b+ɺ\['*h4bfkjN>裏^nl@SNZkJ8nqG?QveY2%O\o2t:>ytf?~h{ssjizִ\FQBĥ.GAc  !vEy[!Tga"JKhm9Z"~_'-8+![5N1!*r_}_Jl& & !,ro^c>:ނ2 -z؞@ Ve%pʲL,&Pn8`N˲=@CIb56"cfk"@+10D΅$nB@ W-a*@,e{XrYVT`/%e~Aswpv~^qq8OD J/)Msb PBpeZ O ֢``6& B(g48q(hʶ"Y;S@# k`^peo~FʗVz9?޿+g_~'~90-Z8(sSL,H+j?qZ6g( ]X~٧7W^]ITUT(J^  jz{an`ʿTUbk!d]U5QBbo~I: ./nܷ tRo6ㄘJ !<-q>J.tξB a5*3fƘкl n'-A]^"V%^k\ ;S]bw4ë(p_Mȶ,C-}eW5S,KdD\֜%Ĭcx( vI a8r-jd<S*wu]39a*R/w^}xf(cd.E|y8%TdOFVGDquQj2k-N5.Io_|G,,ʲXC;yV;'\tiwq=ދ*"^zmO=x{Ž{O^?klivbk{sVքBL `Dw8Mc7whw-j )8@U@YY9g޼aﵖ9'oe dYY7{5$Q1@ F3F3/h=^\5sDIѳ 5 as46jc 8P3Z% <_̗'w$ؔRMN?|IDATŗ^J>uicyӢ3#BwD@ 󽟼fn!ݢ ~;_o֯}c4 ;G;fv?̎R߷1`dELܴhl$,3,1]43##nP@B)iwl~VwV 4#CF0fe0t0=ӟgVRL `&C#03gȩC@$k\ ;GU`\F=\3 eYuhg7OCXLb7nXA\ K+aX^}7߬շ?W߿ag:Io'/.Iy6M3L ]!k ~ȹ4SD:cEHX**(e:RoO ;-(ABcΙCIӃ2tâbEXE!QO Aӷ>|Hb 8Lb%%1g F(AlIjH¦ƔR*|9miX\9'#gCj p놙!>MgPlXbQFIx$.fmY_ oo|Oɝd7>fuy77~W/\1d4_(s,i:Dzm~T_~dɽ{׾{'`FUQ"X~xn\.+dc.EWU&+`z}/; 6 HI5GOnne/=[.O5,ǦqT:ij < &AIP29\+iLkT5NI3_&: 0TB!61j-bhְt@jM[ɑG_z_bO}kҬ89άEbfQa5GD8.@@LY$^ q42t *iKp`H9V:)T;/#"w;om{{/Y8>9-2X~2|ᇋ^^|/}eX'g\\|7tx. C_4}=_>y$sPUޫbAhIWz^$1F13$,uN*UUQs&Y4c 5 @K` "d^d\m߿?;wio8{Dt(l|ީA}lc"=32i-x\DPח?\잹Sq{3Ѥ` f PhJLf D`%O`X =9x|xBbA86ALJ{w:C}12Q>l#@йPSgs͛7o!X( '3`Awr^{n h"[7?>9WFSu.CC0 L.]x{;[zSq@fv٩KAV"!NSUsжnlVd6u\ܿEb+# ="󔀽/RUyUd_4s 9/hf_WtmGaWY`H2Ǣ!mDB(l6j4LB{PQUȬ Y2]t??n|SQ%jX1|>{_Z_g[ ‚2xb7ϋz۷as/jt?;;;: Ml@s y,,YsKH]*w0/gzQA&kkɧr/t|ti`j`l"`]- !Q@Fg G Cpec"\(WqEܿ3ϧ3NlMJ`LFNɛRu)c\$XL`ιdon/t͍j0)Z4zCh\1s©'꽿C܆BK4p h3 H$SH{?!l!c%1zww}x||PspPXwxdݛru\UȈDhK'10o s:4@DDO1&%܏}yEUU*@#v>g.Sϳ?B:˟~EQg*AȘݟAΎ ^;޸4?|~oȢf( 39w'';%&.m "c{kuk/LpbL$BE31"ywWOx{,ZUW.zڀ6fL{I%33)*zL^|Ӆ7NJԁuա#f& D -H0H`K c(=xLcxr6I]>AQ$s|V@D|f.1"3tCwOc0Cp}CrFwi 0`7ol:[,Z_l.0m5$Vz XFG8f枨ظ .(+I"Vg;Ϝl 窔#ʋϏvAo * LѲL T$z/S 6ch;ќsȀAY:Mq:Dm(٬ygLJXxʻ H,\9 g`F`Ws"U wvGw(W(Ĝ@ǀlFQLG7'/ /"(檽R.a(jdCږS8.^xݏڶ? 7O[zM6v/d_{un6KALO7YiS=e!^y޿;9$7|ƥdR <%9A8Sx@76 66TT4 Bf„)`O`b pjf~#=O>9ĈH++x&ghD@%9B4pLQIV˶+'sܫ)vhj%ޕe\u2ޛ-/X sF.<|>3Q}e$D 'p8̠%e=gܚ_Dfd4qoo==06A]xO<~|;Ṣ.w W.% A#iRIW$(Va>wSJf)iO7(97;;ESJPsa-fT~6AubYYsb{GRbȾ(j׫cJM3?99L&ZeLEG/n$}x<&.?[|v8QA@cp9"s&8L$+2*he64KV F"LWc>-0<8^x7zqVәyΝ{O&S8!f4k)ڒؑ=F \HډW;>ʫe]MOE&LcOk<3[@mUz>_ܶD;; nߪu1t}?m%B"Lj+IԇWCD`NSt bQ8ƠW[% @ db&@{fw/\Z&oHv)%GB #F044>M"))z-yc 3Be]?::8*`P[sGΩj\ bʜY ;Kx8h:BDYdV*|\f.Z<8ޛBtX5GP4 <^Y AW,00.y&IzDL IuE>6N3OᜫYn32p0 l8[+:\xq8<1f3*w]!snQZ"H=YҸ>OL+W6fDPUXsGo8۶Fzrpۗ/_><8"xvv;kcRY֚ҝ;wgךW .7iEB3 /gggggggM\%a_t&%p !d2{n!ZV/ˡDUAڿ_{߯Lcɹ,^plJ QtleafIZM`|J)Cfp!aA2Hj|30)"_\S;jR h&_ 㲪*yPAiR)|>L'QXYyBx,mьbzjNeYnoog COa=yr=jơ1~x8V#"RҵGd`rMblH`z$p`GLbZ,}ɣGNؕk Ǭ@Fń`"xVPR+J4 z$;lwF.bv$'^t5\ͤ$"ჭh4,{>^,.M5@$aC3`& nml6ulg3s/>_/_51ڤ}/rmSU77~7 <XU >]MT uww瞻}`0s:wFφuJ%*Ꙛw箉>wzrTاHsg{^B%0crt? {[Ã?<|{g{]LOdhcS`gʩ+bq i;塀KjO#'[;6=51Cߕ4@hq>i (:qRxضm{ 9`d 0aԈ "QbID5jza;lE'm׸<#ք)&W˺RJ`^?3BFVդOU{\~PEhLjkj~i~p0YPsCx4]׷#e/,1r,YJ4FBLzDfqݳY/A9Wa0llllêFk!IcMHQD 3#Ģ(@Cv^E!%#<$$(sFNkĪ΂z|]V͇Ví__?vż !P9Xt1T Fx1{w/E 1su/\pIɿ{̺<ܼ~Jrܶm5Kt|&-"[<^ Q뻓{l<&F̙a}7P1Dd-`#%8(X8,A]s( UF:' .n?EƸq&F@H'fLmgK l)iuZ+ GG=G?>;w_ -BwΡ%P`]٘AD0eBγeX,D+{{gc0Q5׈e Ƌ-B]l6c4_>Y,UJ5yt4X+063zq!>SLIT˜(gf%X!IP-bӿAu{/]׿oVCy??K>q ) gEΆ؄m|-PO;!/(L`-aP0tތ*`ΐ!RM`D,fb]:" Ňso=/jzrnqUUb-DPͬD 6J)Xw5seuc~OmMtafwA(*$N)ei'UmdDdYі(tRyOOۆVsA zvι˵z,GL9`/[ Y ёٴ~- ["9TY* .YJ)WE2Q9%"D %< $X,}(˲"tm!zys*H  jnLZΘ@INjPND:"*9Cʢ*ò_|Ų w>d2ool6;999>>>::O݅^rqtދhbQ@,gT OӢp8NONNJ;.r_&ºWU%ElǏy,fFy"-cߢ(D0(! d%f#[`wwιk׮m߼S /GGefhh>ofYUg~ @Dh@$X%}s+v2j?81~1ZYq~0n֡/}/:Ĩᏸ̬FYMHpFFaSlwCW^/Mi|t8=9k{]s8kfZkDoY6~Ʈ:WT=ٕBzH**5BQ1UTLLQ PY2fpyh!iB *=v j*ö@ gȔv BFgK .da8H^o_`ggփ'7o!L;}ko[˥.3;tğӶ]]х ?~/=sqE9hfF9IFsssg׋)^'9Dk'\"j[?99Wn>}'w毼v 1κi("*b)Ip;?I};*R1va hEZrWeoG<8 XU2\QD}xGQ5Y 1F˿(`<]y@^bĥ1X D麮iܸ^5.8%JoRe~KOwqgjymY9?E_JfEKP0u}~yj1>8[G%E@ΑEQdJ @c.TexU"c Nl`=.iFbBj@ 3%GAC JÅƦEkQ IwlUkt`akLLzuB\ϐzp /J`4t4Vedi3 ܄XP\낀A2JstVqP n>_F=ڼw[7?vZ) ܿ㆛WFO,^=8γK},Ϻxs|wQ]ɒƞ1jg78RѢkw/]mm&PF& euOf*AҒc,ݽɛ;Q2>CL,hs`X4iުYt>;}0PT& bG"u9BCDgT4^ 4!,|K{3fY j'fĀT FіѩG]{5d%2ss@h 01UZv@;n NxjígR S'E*aѥHU:i O<@r!TR=ަ 1 {43YVB2sa +ٺ.\r1ƥy8ZJ.Y$ 073`5YMLc<>>NYK3o }hn:NӔƀkifdtQt|:opxމBCmtiˮx<[l%GP֤}'NR@iYįff* B$bsN0dҗEIDc ۶}G?z?]Ⱦ?۹t?g)ͯ/l Dt@|P3\D8H_,ŋ;;;{; щ>>7{zv#ܥK@D4d僟—s2q]]{Ν;u}h>?<: o޸qBOgZ6{"鴛(f&Nf)Xr*rhӼ\Һ{:BĬE۳Be#^M;f Lgrε< ءcAC%X?v]CS9;m[Ї3qOΚXNf6OȈ83\s1-K5 "~kk+[$Zu~3 ymcjcC(q6ۛW^y۷O&ǵZJp "g432!C\j|@.GmZ`X\U]IggM P]d246pʍ[ FTISP(Yk noʲ&=xj64Bmvr^'79*ʖI Y!!"PC"Bop*ϣ %@Aǘ.a :r|a4G0̦M a'E'Ѻ༤mxt |8㋻[[D4HB^DJ_>PL U*z4_y;:8oloooG؟72$n/}ÓҥKݝ4L_r jP0$?9?wGgpw~kp탷~=8y>Sdq*9i3s#ocPMbs3/C::V$$$dlI,`% 'lϕT` ]t1*"tT 2'#RLƣ$<>F+ ogO DwlY`)yB`IzT54EsYbx;N9w}}4M}<BА!ÖLp6Ne3\+/tҕnQnS}|9jW3sc4M`!0#*d|VJ(C,kN,ɼ4Č9\"bVqH)хW7IQWڶ+ .gRۿ䴽t:]׽dzW!@(sȐ?.CSq]ݼׯ߾wm۶mC&uCDcO+h<9=;<<4pX,( -!ǰV$)"+<@-{͛̚7׵(6ڵGwo5|k_+KH]wzz8ُV9/b* xG7wr|#ںqT>|4;:$c䲹 X"©j.@ r!@wv=z6~4)8 j1 !E>Ipb6>r5*:.AV3Q2Ā̀bTu&-hr:(aX_|p)6m'd}Y:싹H/KsC/hw-?xլgk@"`84H]Ii sa)kCC48LLJ H[ @gOHEē#fKﺺpuU9<;>$/?~;{íV?Yx(hñ/\\-CZ_f] ?=;2 fC% <[\|mGE;'*.\K~JV5Gww.^):̢6hh+Ժnfۊf_!C8Th?y|k4ᓍ]tysTۮ`hzx0]ߍNt0Ȣ.lr[% _LFsH#+!rh>F7S Nb'ИE.jK \XbCV,PRyp_s_cl'M]b(\ ڴ]f RlD@j֫Ҿ274yj2d{HbbgҨ(GNߢTH}gݣOD tH2RcɌK6$].]|w~5_ss@Dx-ɸysOgϽFp{?IYZA^ahKRQ]b ̔Spf?}d6ɢ; !ώs9h 6c.Նq}Vd9%w ʲ{I{AX͏1&s S2LfVTEp*T,=)G5}O ̐+VH -]cҳxlGݽh' -#nݺ5ăލڶ y[׃ga }&[e J!'D?h4soNGk0x<.˒y=6>~͵\嗚u]=|͏>~MTea4sx{_yW{NΈ*d6Xv/=ⲷrMU#@QHstX5s3[""bV#nαVڶUd D뚾zb bQUp8ye2T (.)^!LDlvOVud :LNOggMa`\IBIl> )Mw׮^9G8tj`_ԓO<>-s˳yf4\/IE,6.U[(Ce(gőtuL{4躮`ۻrʃqgvKR7#JLqcqŋ&=V-t>#b)kا?rгڴ޻Pz=,hXh7 υGǙϻxKos` !dhm]m+" bf_KzI9CXrL䉈0! lLrv̰d139)|2">~t2?j},t]JvttރooC%'{u)R>pggOOO/}5)%] 8GD뺽666w OyJ|06Gu\NZ?~?t`[8H)ƨWʍ~iIJ|>7ao"[\I[h";neWU!P f =9.1233"}jU*٬H1Ɣ@c#D0f/̚BfM)Fqv;Z `l]duZ &]PIS =KD $:0JvբZRHHLhA,jB%D({bjys8 Ů*wm;?ͯ;OG*T͢?|կ^?яW?|~6ȅ3~.e ,ł0+E}哓'^z9Id9kr-B.6G'i:֖+6Io:^f |=eogg3G_?zׯ\}txgMWo= O_=zlC *9`HqC `4ؚCV90' ")*C sdq"Q'e]0ۛsB,HHb,TQ# :GJĢ]t 0RƶE`xz2n^#J&p(]+t7P5dH@L{DD1  _kv-*HP|)?e.m~\C&SvniRuGGGw1B۶2|Hd9{UE,&XDti윫1xx믿/cỳãNAYygIMr$3F&F`\QeYxDUPhQrTA%?{gJfk&, 1gu{ Z٬{bf7B!OڃٓR$!ii.̐ }mS͸ FD$Dd:s"*Rbl{ !+"}eD3swfI$V/č2H ] (i߿۴Tbl{*XAь3É1!(U9>yR;Ԏck"xraA`& MD0̃}G\:?CWXa X)>`+.Wl+c_1S/"*„;Tw f/XgwI yVpbu4{EdcSLɉ(8D4E%0Ls@zaڃEDHNEueYHܽ}o޷jctoo[w?; |_N\xN):Äϴ=;\9>Nas1?8z|vvva{=B+۶=æfĘ7n?ӇwOOO@QPmiu@(=DltnDxCՠbߧb0/CGf\5$;v 8;xdU 6[Hώjpͯox~x=$=8m"K7^(PpQ- {q m;:UUJ=D JCԕv H PUS,xsQJdLPTU p!`*k@fpWcRtic-^2GIhsPWEA̡D$rf qL`-a2&ecb:瞻Zmi;t4 m$+eTCdZ+HL>:qľo դ1iRL"Dy`b NsϽΧ{1F2;88hFE3m+p.؁>3];n|p4|2Esi;|.mۦ$Ks> S󿓈-h4UY{=htj+ (IX4 <ASs9&1Q+!w}>`  E$.J$YmTpP,"ιbhH?&NIM~ƾ:^wsК|{.b̭(uL̖g*;[;7n\ti,a6dӳI)&K`{eff3^:jGE ޯf; 0!RZ@3]Ր":efK$Ϧźf("& ` 9srgM24{Hdasy67qSARA!z\faiY%z n ]2R@Ecagg'[[.?_UU۶un5 {c4@mx'Uqn~4KYONNlkk+ONU8Cv~>"[(#Xvv$?l0f2ce`!JpN'?I;>rccu]7qQLa61>|q3(S3zY'u]&5 jAX.xclkր;\PL4.v1w\")jI=7 )O{hnzﷷO&gMlXTDZ2^i&qk!`t8;5۹RD뺜D{bȄgz8YT4gv9_aX/ v_4˘ (rkB&ضi}ɪBSBDd 5 XP4# &*}QWըirx#v)U8;f2Mc,?WE]ۛs 'Өmr.aᝏ^znf]0zХ"O !0m[RՠAAO*` pcdX=ir5GV8,snwڶI^RuQEY<`|tгTA4f{7=̺6h03#]d+?0B\bl5]7]pkMEJM(#iUuP(ʲ\Ê*qr?K~ز}gzv]׭s԰F) XnQ6q-\;;̔R:9ܿl;G>,Ԉ8W>-d=_EYU@\vDdli$1HUU(Qn v]wNb2U@OK]eGH*kƒR.Nh\:EQyQq!T>&͢fۥd[5VO)EQ˧sMtI/)lvvvffGGG_7i]jS%-d{IM,.x<~эŢF鳬i]fU>h˗˲dfY Sj}*׺zUչiP C{gpgg׿P sjmޣjmQ1F-C()r.(fl27x1k5:Yvƈ.NӾ+_lnnnU(prrѦ̌ ;JFz:O `8au7QՏ?P$@G;!TEY;.Ȕ~@TR 3SEU!ҺVBDe7z|>ZK$K P$'W`Ȯ("FIyUz/>97K}wzO[U1ڻ57Q[U!}?ꂀDLh΅ ޥˇ'O+,$}ѳHbj_v;:8dvvՑUϔۃ՘3Xm؊lPv+ߟo-õˣ|~+W.xm (j"B`P#b6 76 NOO.UU,!t+'g?֋ c/}F/캪3k;#B6,X1$l_~u Ja55dM/='m~{ w(*G,+% X%EX'sE (%Y{&Ί@!KmM<걌bSCX0$}ױjYXRJ5ZF#*; 6  n;.98`U$lT ٮ>yƻ?LcW@! ;;;w?|_^lr9 Ď) +Tޱ#,ŔReYeY!}b?ƥ}EU!8BCD]ΤP̤eRn0`P!BL-bLLss@$>#U""./~jiPֵhcR2<2;˶=2 WFgtzxҗe9ǃzSHRot:rԉI1"rD1tR"~TBa4ﺎSo+9,'"prrwA5w%+%T̚nRUΎtv64Ŭ'?I)aB< 'En=2qu͢g5" *))%@""&zu`vt:}DD gl>h{NR47e箪$eK$ѽWɏ8oE4l+s 4s#]ϑe_nImdܛaRϽ@vwchm4+Cy4(PY?_0/G3(b8~y!a').3TtP9bJD+ol=[(7$]B-V92֎@M1K-L!!9@(DVM2ư ! Y6"@v II' a{>ӻ/ڶC >99,WC#f KGK>b*BV_vxr}7.Onv.ԳrԠMQ cc}O1 흝zKGr0SJ4isz  IW>#.0 /l^~ziPb?~.ի/]_Goッ]7W'} uDMJI A ԃy׋j1ܘ4 LE]b6^CZ}RUUvr()yPRJiMPC<ʕK4܀)4 DTGRxh=` 44Q@,RJK{xI`W\ƺr8Pm|I5 0g6af ҧsURFPk@(IzcɲYu] "BH`,*`Din"Rc);PCOX;SOD(K j`, dru]X/Y'GAt%m`jhyW#q 92NF/?}ʕ'+AnRƈp1F$B̜֢(RJߺz9#N;$RJn*b٠gO\ka!dsџɟ ].^m뿹ľA9WU:_ #3TUbAQmalRYVsH'7FW] ɣߞL8 2:< /zOǙgO\,eM >8\j{J!azCf jgNQ>p1O<3- ղ*&5sRIODffq4 k.] !`By / v~ùZu+/{tz  DCL&* E3WLspwDa [;7\8\x a KTI- . "NU(4Z2/k$ٕarι/e, @]= {Fb[!Cf2a4xuXd3 L,#"A[, !!هͧ0##N"J=TČȎ2vDљQW3Cĝ;`fhÏ}ݗONW{ݏ+_xK]q|~LfF@! bjn(Hػj:^~о{5Q/C4(,!Go|+pV>D1|7sQEHfx}^.^/]Ң,/Ϳ7ýQ:}G}1PDZE3"y]+0LL SO'f~ݠ+%34ɪ( ߺ.43]Qp3'nVd~ʔs*GxR2"@TATb#2`7[fS5USRw7#ъI]5ŲJ8☷OШ[O:ޛ\۝C~uȈPc*f!\/pfu}p݁ύ#ٕv}߆*W@m:޼yWnD6fZ(E9#7Q\W+"2z;I/"^XPMKڨHhϽ"}_\\~xyn2) dveSS,-^z" X_+w|aӧOvCvjg1FNU Fid|ed2ivX8AdaR xq!| Pd-CD mu'G濙qFl8===79L~/Od;;WN显l6#+%.*i8ԻN*r\)Rr/0 4hn޼:ӧ>>= v>ȡUɻm{oSlle[H IFbR"RgfJ6BFcpmk~i$oҰ!'4MyX,N Π?}SMQU;0|b^)GJF=)UU}K_sn5 է>ޭJCg~_?8_v㷎OvL<"&ՐDՓ9‹I22Kb`Ҁ"GdnDdMb#8@fr!Y(" HX:V HpY zkCcIc\d]q0^Tz#cZɻ>>=YtrChXtCh&8gو Adc؄Pܧ~3 Ӻݿ×OuR:`_;oi6$f #B1@vlJTM&P:ՄԩEG|qq[wn{L]ggdl`Y<y_M'?bnY^yL*$ \"ա@DL Rh.)vkߧUsC6"Z.Y*-ABԓk׮d8œO'kQsb(jJ(T)GW+B0DT " 0/r#8DǏ`4^m*z'"닮n^túء2!(h>Y89# vS}eOiwE[ `F4#bFvkڹI\bz@^S9J[!Ƹ{?8KAӊ9xpŋz͸!Iؖ 6 ȱc@9C{{{ "Nډusb:pu󩳱(TY4P`8͏VUAV{Uu]W!9Qꖈl}?GQ@bVRRʥCO^#3(˛m VtP }5vvv^^<чnܘTu\.j;! Çګyr"E ~|n=CD뺐1<;;;>y9*Jj&",ʒ_L vR?;P;b.IqMڷuN]=!=|xz|Wѻfoo*B2:I8r0r<**DQ!!jN tw,"6r[`WPNBD\?+\'?UZRJi{l3,Iq5qUU,")[Y:V}JIЪJ.(]v!Vu>'4:( !8W;5Y$qxzz?@USFs}L7n׿G/NV+hb5y1u@AEai,|y06k׮TU$hND6'( fRHȃVB @K<栨:CCUs[ZVIDٔ2(e2B­ʻ_=ʦUc"t\5}&hb$h,c2S4C44TS!xfVjQoBtuv_Wl?/tjX/v\|i{ךI&YGE[10#H{qsAtrݳ'OvvBUu8Cח- U1CdXU53xGO޼ql7wttT~~8q~¦)\Iv*U2Q`Νz@& 33պ{1_T.g-3*zLmI[O'պV'/57s7ل&׍x0T-hN:Uݶ+% DP" bFľ&3`0c+W7we2`5g2@M\h4W_BեaR~ a)~F )3l*xx~2Iȹy9SNctcK9Z×̹^{ :'w7D:^n,r HEuflXD];h)~o~dm 1e>Td xmm{Qw_⋣.IX%#sXA;YV6KsT NZtA f0c3* 19IqŶ"DDD6Er(h:.AlqRуkp 0U tmid?W7 ԥ%SA3[r @enUQ14B ^y$LH̜_NЄf' iwzy_zqw;=Ϟ_TU{sAb*.^tW\iH )j(`gwV?|+1o=rq~ɣo~v9ӏ;E5 hRx[VUaCOفllDU$q&sP!Zq"qeբ~mwwAA\ 9 yHfAKJ^# !xC3.UtcfB@=8g0~ނTPBF6ը̤O} ~2aOTGW>-ٔL9L`2&&MiueYU3T}A, d<載n=qH3=Xvѻ%]~*#(`n_뷅 =ȩH8綈ĢGڶ}f:|rv~G9FglQճgvϋj6t-=C&*mzsLLdD1F+&)jqqi7Y.SLfޝCѳOƍ4;=_6&ڝ"f`W @X,ۣ_PFմ䃒MFGDzڕ$ kRn!y׆*'8Yf'> !8t0똠xsNHs#h]d TF-xD$ Y]dW;3+DEucP’qjT$a/|~7><(CL] ;==qSj{_/Cc7cO֏߭5ڳ>Zv9w4;VU-ɳ{CspsZ. 1'`͚5ߡ!37y+_җ߿;}oĹ|+ru]{"$3| Zp`{IDATaFvH]mIo,@ؽtVbhو*5HT +^!$>m˷m<dI+jY2-[Hx2Ά |lJ21O GhL-/"9 h9YzTU{7=zYLט·/N=~vt Q sW$Otm_~1!lMu t:]_[t:}7~u]uMądw~xgR;W6,m{xx/_tʭkt8N|7n\N,3f Ut~~_;g߸죏??۷e_';99$̾UiCčl<+idSiZ煮sׯNNNjvo|/ Ǐ=XMf6JTF&"rQ;wc}q &) ";s*Ar,&ҹ\F֋%lFʔ)fGg6)%p vOZL!:f${_U"T)Wsrw>i[R-w9ݧmAvq=zoϠ1m'g@/ޓCp:*/{|p'N'Ͳ*h"rp ࠩ澮h.QϞOoWuo$>!IwQճΟwޭ,!tu]'~=6U-\,՜!KYwͽ煉fx{uWiU콯ȡ)'jD)2iTaEÙ̘'tZO`}]&7~Kwo/_?[8߉inVQ5g*rm3Ob(JM9H6j.c>oK#W ،IjӦ6mSCvXoM0GU4zۿ:{ӇXuCDeBU ` 2&<#"ٴ;brXty\^9+i9g ! -;fwzm1H!8 ֿDf렧O_ArAᓇZd #2$$- }<3W%vyMNκU1C.bN=zD[3{n7[U6-١S:u۔BDǴe)1Fö͖1.\oLf;woܽ{[> "Xtvd2Yvڶ_/?կ{kF{{{MWEz1h~3o~a޾=9PM!ZяO~^N???ɓzǛX P $ P0_rZsA]* U3Lf!T;?ru@vgvwb UON2ReNs!58%U3,%ؕ,6^w=PլI$2djS`)07>sT_޲TfS1vbX.]j ׯrgӺYiYET]> YDZ"Ee(>Y%1Pn {/">?d}hߚQmgH%}T.7vZwȘD@f%.0#RAKYb dY2 )qp(5ڼ2=V0Ã}>[JDX򵙉 df9j9eJiwwN;QԯnɍW{n?yGNλn(` jPaB 8|,xpItUc@p-u"Ï~w0Ns?ۖ4œw~gl3A4O^_*>QynlXЁZnѓ/_kß}+{@pz|@~Zzzz~)? T,<Uk&&Ҋ4ydGR3T5ՎoՐۻvxfSNΞ~=9PьM լjj h"OMFXdSI yŀ1$@Dp V#'Ɩ Qsl =u>W@ p sO{)iWDTDNO[F58\ZB]mlbE[AP8$W_}ugVD!fҦ&DCx6y 6%,++(SD/!6s9f޵[A}Mh>^,n0G/[nr ׯߪ&[nvߝW*sa(H6.\U۞-.l Xfw9y|XXiݿ;;> k_Wo?rX\]x_?|󟭞||wzw6r5 sng>}ڭ h/^8Y{79WYl\?Om[ªE\OݿG{{{}WUUǥJDb*Eٟ{|pyƅ_ō7v>'CJ0&f[_۶}W֘`cq%5g*Q^3nF^lRM,hcDsFL&h`rؐ!B<9*^OjrαwN l ^8."mXON3c̓4"&I!rSUU4 ;y:>{䷅2} y\%^m'hO\Hʭ0H)j5 E0$)#@r""1ڪ&4WQz2ߵ_,.2E٩ lboBak'D U;\[CCDrX@hdJ)FDȬsQ6mfjEM`۶h&!8zUMC)LvˏSlvox~<9<~\uѣ w+n_d*?[?z'?^"4'is%K@tfET\Q]Xfbly~k7o?yyqp?xΏc<;d|gկw]տսNܙ5904 J;4}v/o=7lN_|{_Ý[|ӧOb Istrw_g˪U8$6Zf("ƠQ h.2ueX!N0ox:xre|l"sM~ }j QIONxeL("{ Eך9F#P5%ss,eCF`c1[6+4  ' AO"vƼ X,x ~JSJgbXF;|KfGgǽh9" 5M4$HlGլ M ι &QR,1,c̀6}|CLb4͘p|*sOgvcW>2O{| 6[.͈}9"D΃ u|ZU=_-OfTd((*c lk%MMX3FJ"d5FS@$vɊD 昝#gI 3C*Sp@೶R.+xi pLz" Z+ ):VEI?PM qV&B59G6yw/ɳ77^'_>_v W$,W "-  GT^4;y55.jyqq]ʛo&'w}W/8vң!lUaӸ9pV ^ʄwIZn,볳7h{/v{fó1x_[?t\ 7Lz_!b?)B *dr@bWw&ýI,xNUzsG׋9A#^5a-+WjPtc&Ȼ~t|Ko75ϟ<awwd8AU' 9T%RJ[۔FTQeŴRg^j2ܺu{/">|뭷/^믾V2|!ooݽկ/gybAqΉHa;hA;9ҨADE9sk{d㪓A뺞^iI$qDDVE[@q>`[:%2d܀kb(+ ̠ޅ6e$dL{gLTkحæ6^w*+\Us^ ';_Tncec+l|e11H4K(lEQܜ7M3Vқt?$b͖f!۟Ou~6Y@FUoߙ/+e@]VԭH`$TdCB4Tvնly0#Fh䕘 *(4L]?;c."^QH HD $ {PL@IQ 0a5*;6gEBP<"Xhl~97CBL|rAUP1Ŝ3h1Z$)#BJmO1wC\}'{/|K;3yٳ|Q }YO>x [&#eSUypb`L9u}BUpU5_< {NNjӳi5J[u`*M[LE, `a|l _xmO^,~;>r=, g9$MY:~g{e/b{W~ӟQJYLR#g`"d* Ç}CD}i6>To|+7 uSPyD`H2!(`kZ:[ aF !FRˣΕ"$ARtExc̬ND,6c n^LF1@KɶkMJDI49QbĢtժ̕v \`Uu:޻wƍw6}*l>,c !]R}v>7^/~ f2s^E(eFbjڋ{qr~q}hd[O5"@h0x[`œkOxyr^Ťw&@Jc&iiCdQU@EJPYnPq;:Zs[}TFׯٽkxBOZA! !{@"Z\,>~h6./'j2] yY뺮dB5$3K1y"FU`皪ě7woݽ9G=~ӧǧQ\42T33 i"3iř5z^Վ&jZ7dֶE|!/jTsh'\jso DyݻLh4I|8}Z"D&(&HEN';THv23ͬjBQٔG5clԑUzO3 `bmNIRqD;lb )J jRw4%4b[6F[E׾կsj賢2WQϷ~GS} ~W9J%۷o 籪QF>Ȇ ffuS$wz vlY2P6Q%eD#j0t: !iOQ1h0lHlpi.mL[J6F1)8BW4 D*kfbǎ FόrWF ] MY7"QأD,+ n0x4߹[vg` K)]siI|X!Uιk.cn=yD7yri۷oO*Y|JF1'QU/]^)ɢijjJ!Tղ QɬP@d~%׿pPl vMO Ԭ$!{fg)%Mk0!JRe@*I\V;O.^w)R sD$`Ul"2 S3ɛ^/f@\>_9ZRWr9 X(:C.CV445R˜͐{%\cv p~u>CGjg]Նt:mRJ61I9iHˈ|_zw:vRMwӐj]Zfq:gA|x^&p_TPd.'xa Kr+aGD~Z=kjnf;~?b T?~H]1X\Qb$(8(]ȥQ x$QT$B. THXȋ*#U KcBA30O`.03 à;1ƙ 5G!$^hϴUsfX邋菛WDT"*93:!V %wJP on~gSrNo}[͛/^>@PDF0su]߻wV "f+ kai;_i@cf0fc|JI}XMqSCbfXz}JNh/Yla/RR.jȳmY4 i]ޒVc\O=51VYU?}b5?4RUMZXTQcw+rBڶ+:mwEX|2#*eծVU%@F[a...yyfmZDIe!fW=FsKkiݪeZkX B\.SJUUvoo_\,\&D,EC1nu]dRzѳ:UP&fCHDQX*+9$3PFM&vm7tTtIJwz?*ӪںiCU5U吭nQؐ4Sn-8D u$FFl@FD ye33+!o.:.'m#tqE^4K9ιTiEhwww2 'iQ_s环.J)SWA`ڶ`6c9xPȊ{?OaHO,j2j|ӶݙMת>6jH;0lfb wH'i%UqG(a (Xzbccp )|v&" GDȅw# ssG0&&5:3K9S1*c?~|ǡה rYkTYTF5DLHV=lgkn *`^E鵏xsUuu4N8b=V}SA+1`LrJ3ijwwV{|p SU+jMd)-V+n2|GdzONW9Kү `(M   ujZ:Uaf$CXy IkbTre!,1D,VU ؔRNrg~޻*wX[Uy7=3G*CEZ lRbP0@6ʦܹ2jAjl;<3`QͰ"7߻}х.;,(% c3jqqrt|3`LEDG`VAW[d+rpYnv׌W#o~ݰcU9>>~?g&hHâ`aP'/][ݧ]'"| ZY3z̗p;QrmrHwMU y"ٙNwR 眳Hѳ#s+K)E3FciCG)2=GDTbI&I:88>gxŇ-9 piYG̬)#ypHLDSʣх9"'0|݄̗%gf y4SQb?U%%03 D'Mc 43Sz M C&jM Jwj6M/3;,f0#yfsW'Ux۶6%CZ &)wu]j5 5 r 唦i0=48_?m3r6?a@kd-,x0Wl)f!'ŹϋHX;ldN&{,ND]swU-32720\P44$U`TƸ}6xj3<"2{"rR'U3bRҌLĤTfje\_d+L,dŌܱKJML DLP{vdfM8O,8IT=i-eJs̒kĥ1$Bq8TӚ%S眉*8U5fę8Mr|^1I1Q(e$b0:xv}8۫?᭻w^wT9B-R7f؈hHFUݸz][+cCC2 "䚨b ^k'_Q 7I;@rF`D&!r*1'@K\[V9w Äs8 @T X3GˆЖtT໎.kDPLrΑCv YUe13@13HꑈF(R(!G`*IJ3aZ X"<;>]~,ztO I*Ȋal%2sĨrYQPD)vATbvA@d2-#OJ#"X(eш08YQjs2Akf:mƙu 1+}%L7=jQ"dL9=jr&UJ1]f}߯bX,Vzz:  ÐR *DKy;f <s)~cBlٳ Ȍ(B dS`sCb_QEj9K9$t 9Y("- v0ͮ7͵k.tWHA`*h<Cl^t qA%U_"^$%jq% Jcft̾rEh3 GoX.E'/SufB&sY}"b+ma\ohn֝S }jSQsm7vtn И#H|< [7^o~SI#32EK[ԵG&.>xKʬ-`XIKfQ-sWr yI1ܫB6Lh8ZY1UUH2 ʹx3u X_zcSDќU4䄢\M\<6Ⱦ !1Nh2聑L@49732B\5S "ٕeUe LT2 X (((ŕ&@egG/ώ^s !CZ5BT) ZҬ*,e }>wn{>|>????Zl7n( 3 XrΏΝ;u]w]ZMͷnƲWc1*+*K bD%E1 $eQ GY+(.3s+ОUefH;9z蚦pjV3 n n?)]53\ 3B/ B_01)p,>^fvrrR>]4u]cm;`$Wh0u C̄dZmc^%%/t!xiooHqZ 1z$)Ǭ9WG4D ,2ޗNJ!0W9f.){D9,"⽟U]rP BS|wggf:N| }F@A,Cι8c\7!&evX.^8B"txPJ+WTI̲1sQADt5ع }SU8) ggg\.JդA%D !^/..H9{pWD(L0epV+("}"mcρOsc7ݷmz ytxV}I{7ǾBdrao`}zR*I\Z|bgWWo/bw6b rxmFŧMӔS/: "F=I:9'dTES4cDdp튁MJi*!$1l<9ꆝs䁈()<;s~~vj6,W2RuO3ʥ0@;3R3VUи)gI ,fEH9*"|WŶ.#":+Uidb1a,jιK9xef|?9^ADRNTE03ISs #=#hoM$ԱļIvCRӸՁ]2 G"r P=o>nK "E,)2sF$fvRC,VFAr!(,L"lF[1K>CIuGѤYHM XBl3䲯Qʉ K 0 ~KV T 9 CCV9,"_Č $GqNFH}vޜeRefbO0%(@DFfRG  EXH>P euثju[yaӿC.Q ].3e\.ǒ J\ 78df.CJ97 3'M6*9۽{T5+]G΂'|fQ:`DY9*>N}N%19ξ0tj X cEZ<_Jǖ( !9̪’U5X馋ofX %\3#0c;,72B.,5}~8Z`d`ƆSߴX%;gBOL+XN!$fؐ+)([L󁙋.#3;-dz| Ha@R;ALPͼ9SygbrB˝~<@U L4. @ If@Oh`Z2+j/ͱDO(8G4sCi9`ZP,GBhC=|]]NHV0Qr18k`>k'Ӻ7ӃBNd U0yJB^D$odKgfV !8W)s.|nG?9fDD% `m}_ 1t]$&%K8X.‹t"eo榝I"ȖJL@5EľHսW_[ 3̥Akc[͵+qs$1WCv~?F6j$% |G4w]7ǶmEZu}p0vM&YZN26jHusIG؈x&``3)̎L7W$BFD(:`qv'{$,e1.Uu7PLdzq]`Oώ>y\ANdrιKQDKŁB.^>PŐ|40e&ӳRvO*9YywiQZ=i DhѳJ-&xJ6Rc9&)J$G J T"rexv"j۶)Vuu]ɪrÚ}#qX`ZmLp^q/ʓ:g{lAnr8d.C,5\r?ƝDm""OExy+-/(S>lJ|s,9WU)m׀h`p%qMWw;PjG'''aK/ m ;gsf |IRVJn?|.{fz[$wZt1G;0`H"ν/ v&Hl^ lnݍkӶzWTss!6^kwjYJԒ"b&Jftk #" A+ @`#KNDL)Q#$ us)PH ⇵i1$Cb6Ƶք(8a2}派xåv<qk< ]n+@4"(5rрI̩O6d;-wQ_TTs 2-4 @sDDMٻbV@c"GhX2Bt\$x=3i;!:K$BY ɼ# ɕJC"dlf9%ˉd&"zRU/ty:|Sy UhoȜZ)mYت$o-`J'fd۶$v~uyZ^{_c`V]>V3&<$9O-s9 ,%[ Эc`FED8@Tx֙ Du;S1yj q"ZgKEl` ݽ:TGdh#&;Coկ(Buum+!L:>[^ulc}nk߿nV<(c2K*}Jlaz~WfTu4N0L&_v mpcE_^WQ)vZ61_1*63WU"MQRJ])xvs֤V@ŪlT>ZZ̕zS<3d<۶-3?{j$Ȁ,`9gI4ݞͬ:(XOz]zC9W L@ 'F;Z>* wdF"Ri#UЃ?U7MRxցJW}i ],0K15 sJT-9O3s.x4ge{_D GtՕ!nI"ml 4KNVCe9Wռ{ƍiЗB{?5lU{d&\1H !F˷b0`c]Lhsm;-@Db?S簔?DD `jG4o*h]uboo7m?`ÃsQYd0X6r^}ծkQ !K[`>RkQ^f"fyB鰀m4:-.|J9rMP(`]ʯ_vwWY84V;@aM:nN>0 5]DuІ횺'&mꠉ*J!'U%U`-yHќ* w ad3R@̀`a31G]U\a0I@9$eD` VTL0Sl}'s}\D @Ss),.R5wq4!63!&W+dbuRr:gBU;QRDQoY|gw>뺴'HEx",(fs$YR1@s)$Sqw#w{a41Y4g+f!e˫ٴN~Z]\\ԯAt;{$`F$@(j>Uؖe3.k~V~!7|O?`snǏl>-('!e86hSG=88h}@Dm7:8SP%LdY2/Lh`}QI ӒryC< S*-6Qy.G%I A"IP<ِl6O<%e6혶(ʒ!3&" g_:k35lfE*&2YlٻU(wo) xzE@4x'SH,DofZ<л7 !8$"p7lwkWVÝqɁoqw~\_'㗫8 5g ĝq銙3ayj*LI&ek( r.MnNK$(ef2ڛPpev|9D8@]zہn,bYJfYJSDuYZZ, yUތ"fS`.p`Ď'؍Z hSsF>xQ~IgzPĔLj0z3dgN &PA`bdb@HC 8D$r{Q?i,OsygDfdLd(*PbEQdh~PsMʬU$E`H&"Dz4{ﵖ/:gsRS4!TUU:0ϱp T9. f8A܏DĈ(:lر#D5">T Ƙ:)%֢*Ĕ[=zy鎛͠ lpӳ%ks6EGM b@ iL:W{"Pž8Jш&{ݲ\s zH0#Bf/d8+f U LK>Z6]V7%/K/~_D>*IƔc ]OLT?e6ϿoSJ}c Be"qƘ(FjW&UU,Nse]v;# 9U`".ᡠ0"RQQ0ǟZmW/z0*zR\FJnô@l, P D9g! oafE_8yh1m8"+V35ГyO,{ʙ@ hfժcbRUIebyIDDH-QdU*hAFtNGb֜E)\!KJ) C&l,aH#gae)&Kޅ4lTLP@$Gl lTwfn$eIs.$)fk7Xn_>/LKW7DeP\M //V.Lhh& jګg@3aT-hNƦZi׾ŋ_?)XAq10 '̯U}j{X U?YMVnwǿ[sժm۾L|ƔR4ErfR&\9rUUbƪn֐զ햢ƀL'Ki %&PR@"b`r?{+M8M% Q¾A2dT0E@{K$1{qW]쑻d#ݫ=q~Po9#if0ղCΙ4;BRrΙ$t%w+xR"*¢YUtw@k+f5w/rafOrl2I'p-E.\QU^DD̰cfڻhfPs3@I\St^5MSώkz}MDPbGԩ Rvɭ/T=URe ȿ;B͹!6wZ0 -zոmRQĩGV% 0Q===}/AMJ)|,,/NX+S:kss?w ݤfg^o\^.-d'* 1{+F_!}6Dx`191,ܮUk`ڎ֡%%dԆt_crDZPiS82*+nQ 9ΰ` Uu* (1kBbfTYc!ߟ:EQɒJ=0DcBGeJ#.c+Q 6)6@(% S좂\j9EQEPfYUG  1Ե!H('" Y4(Xqb& Sx 0hLihf&@1bX`r ED %$+˸kD B@Ph1zW[r@ΧT ׈'蘙U(f %Fsrkp(VTιB)s20h$?DfĒI63ON &u‰ ;&u{A?6Ͱ"c5MBPBvE.2@+WB #2 c>z慯n=wbD"Yd2]y\geH}uW}VҠaѽnߐ妞*E__as_[&ji,g6PIXNl 4!bVpDJ4}ꢎ|Vm03I:BWxnjlx/ǘc=Vvޅӭ&_(oj)%$"lj:#wSX6M(e`LbYE^։Hp޳Ƽͩs⼓%K Tb;rAcdV.3∈w2T՘+{BY6M @ر9C瘈F*J̜!8pjܓ`8l&Ee10 mU;Vι?~.AUUŬmb_ &[*36s*hYDPjkO ;DNLU~|#+nz@#2̀Z\XM؋J?v"&979gs\y.7ޓAJILȀP@Gi~";rTڿyljwbFd"X$t<AUfv\_$jjlXY"30YɀhTjN($8zGg80Cf4wjc? }-.xZs$tDh,iDY, VbYTu<8yCP0,O>GSTP*ٷu=Ha13+ļ]K_feMY铪# hc|tp?ڞ(f0UX!d<:(qO֭W~HGrBV vGq4XaR3]s+~>g(8ZUg9;?}3< ɹ[hXtHH`*SR4f=!+\Rf)LP2)q+DZPъA޴GS&wW2(IE]~0jN)9#>UJQRYDPm*d*!q(hw#O4Qٛ^I!d #1##ԽWU@_i"`9m׫jP <ڢuqeZ/G[VS1S_r1"0/,)d@*1/d~:L ͜k2M4&!ҸA-Ls#CA5 jf)lM@0+kb)̒@`]"R2VlAh8qnZjjVu:03!Y@!'ɒrI NhC)pe V,* {WDU" PPJs6T-# ØG9ir٪*8xt윃@FGӚ;AA 9R55r%B@)ň HȦ`#5P%B*yjeX^DI$l4~9l+Ioo _of0r 1O Jx쩿+fV?wx|N!X(*^G|N|b>{<:}t>\Hwvv&`UUa3}~sn^w)qՄBD{Nsj'q+#Œ/ΦovԹ(]iLm͛/+ܼN7fLF*""A<sdy3n>xqEu8C4&p<FHiB!n!Okp`^~DDՂSDtdDD˺;V0CQYcqL"Xr@z"} ! )پ$HWe\#@V4TPE0Člf Zr!At8]s>sʅ;Hyp8 "X2ټp\,U՘qzn~kf숝h vnBb-#{_ٕws&&8*M*TH49dEG%.`i2T&?bEԩ!D !3KHʠz2jnHlebo9fVIEUQMP -n6@Or5i vtfwιyWD\=悠`QqdLJS0q,#0HfVBp+q{jKJ("艼J/{w6Ym/!"gW~wJF]D>I)%WY&wFp(Uԓe*zW0Onk-cc0DZm۽,駟(ݟs  qX{g] Sd`g]8jg CŽef jf|XfUuwID$IMz} X@dUUI %,D*wLYcU*N hB1@Dakf}c|"ǸaCN`'шg|\VL ">pԞtf iX9#"3Q{<+\)(6\w8&^m JWe8K%Xd"~?? L 1*jL|9Z 4$l4; ]qÞ@hк3?ڶX9 iQ0V\m?]cBfL8 BAu>mJDU4|w=gXXUar.9<:4@ H*]c:9(TрP \LLfؙ,&HL{q <)VQcb0$@GSjtPHBv3PUA몮0$Git+:Sɷ0(Ø4E7ɼ̃"$Dɮ}KD7bɢ Y0%Qa$Y 9A9#d-/ID@HfQ}HV׿aUfDV77yͮF`c2D0Vb)g.~L/+]{/@0&=V}vgN*>O{p[,}ߟ>ܼys.束죏6GmfGGG}hffB6hJc+@<+2+򴟄Z8';|4D"$(QB%*2x wR"ƺ9E7Mu)%ba0 b 5&H(@cCl6;vm^$cf#1FI SJRm*IUi9ǑRgDE>uJb*VuƢsOq$ DDtzqS_3HpHj뺂.`Y'o}zz#s>iBub& ԏĨqt9e`NJ=3WSA5Ĺm[yUU)T2&Ȇ! fjڌd"0OLl 2 "7J꜖3#$)Qi:?ΐ a7wTUeh Ua朇˿|'?|t~FD1ƪnܸq盿O߾}m[ɽᗪ0ڶ! ~aD΅9TUuګ?~ӳ3#lC:&~w\;|wF;q`ﱻ5 iJfA :ZXQxZ;ZHs3.C75%1PA5+8%ɼ|HȈ)vʅ5~TnMZ r] GM-م*A,#w\$C ASo#Y& 8x(Bcγd6)WUE*} Hr2-<]]`Nn Vzb`,f&hb.:͓19p,I3 蘓zDz@E$&IYL bGD*hN8qBBˇ;@83U?==ݬ}ߋ$3LbȈŒ ;"$v *0̐L(fU\.̌fP0 jjK~iC cSY<7RVB :բ[m@<G hQdIU7#;!> bNSWdnh`fcz3*jTG cz7^Otyn̉?엿bQ5uYR1@- 8f,D S|_5}۶M@#\_*wO}͋׌X_g!gJ# R`SX%v~|AW抮;_:B>׫*8Kl~Vx~vvyylLUTd¤sGVerU.ԡ۲(nN`M[W*qCT3w 8Ū{8-f.`ytpGDDaCkf- flJYE/xqD,"ҧ8Hi./ ;"2,<ͬfVbѬ kbX^{퍟ӷ{I0 i~|ϼb\H"ʗ^+vd96k1ƔB*K5OO?_}xrl8;sܾ=Z7oOLY=&@L y!OV;(+x,nAՄ@-+&%ټ lTePWJL8lܽNwad^]d~"AxCBnlAbPȱ x_:|q}|/y'))g[azQDn#(JAOr.ɀ QJ90(CUG$ @1QSVH)'TR,$l%CN8!j8y2R@$q 6a%EVofUUܸyb1P&Ŕ3q2REUɢebf< gQi9FDIAY3O7m]/iY,TcV,Ťb>@B!JJ6ZTEĔ@2* 9+6>*4Me0{>|[oMn)cB4G'w{zrvUv`.OcC' av~ IM{pG`P8]z<{.O򗙙y qnf[;w(cdž)L)"KDkO( %PIA&M0}` b# jQSó{ٹBsΪ\SΏYa"@ M]ڪq!H%F>q YFk | CU= PF*;}䬪,b4BN`&ըڟ DT{I-U5F![%f.ޢ\tYqœC_t]"y|u޽{ QEizwP'4Ε_֢S:.Ŏ .;:4@ ɣǏy.l6[٬;7+rMS-i$lf$ r)P՜!(yhYJbXWD,&ow~ֻ|}0Iܶڍ/K_9ur^m(2/ɄUO6vst{p av&`Iߺad< yvQ}}O=d=[ms ۖiվ{.oK'3wrݮl1x׃Yΐ BfB`͉<H25+SP$T1g&4չjfhœjbJێ(S!vgzgbi&%y!8ofe.l(pg\دbbD a:ռV-8[UEn"TR;v0Q#3"r>"r$9NňF` @gu$!M>%$e0dA@c-1I6bJlZ5[碂(:0:笹Oew{zPU,1圗%+DFP+W ( wzUt潿1:4%UIE9礭@hFE|+֚e4"(7jMm5ټjۦf?%عN());UTkVPUȒjƜmoVc7n.֫?x_)s5+.=կOnlGY7 P/ҭ`--ļj=A;jf9Ͳ)%4 }Bةj{WnܸvͿ~1z9ո┪믿CIDAT~o6w7oώƯ!Ȯebfi(aTivlRUacʰkㆄӰ@Ƽ׺"9~$m$c]+zrg/ΐ aJiR.IK Js7$4Sܝpd?D}@_):s2D;9ѰZ/[h|o^oo=utz wsR&x^`E9eLMΕX;'}{%ȢR)dY b-೅XSpp /zP|Ip簷d`fF`UKfs1-3G\.f hQS55TUww;XNG1t]'ìYDOu݌nS:jRSy}14lِv9?6t^u] ':s5xW.80ԤqdPAQbmGw4{1cKƔRT(Fg03:V 1)R!)Nê喆`P&21)hY&9#5qH@;חi>)w!;Wu bVy  <)v9cpT$Yw^y͛7߸@pG9௸ft"`;[i&h'Z>Y>Mtؓ>)"Z' /?䃟w~zC{3\~twS3^nܸq|njiJFfl(dl6zޮ}8 R`Wscʂ`+y*RC3Fq|jfȎds?13卂B))82TEbݘ?]`sAf>5?GۛO!dz:)3W)g%$X,**Ǩ9i9("*8C}ofCی`99?zpt·)^s9{_u]j&08;XR]͏~óaGfBf %o/vX?NٕLwvOi}  {huHs¤[$r uǷ[Mw////78v?81. ٬ϕ3Q7"cDƜ3#8)ED$DHUE̚B$R;t!8΃**]k+VF@daTILV8,zˣA86MS-|ٝ?\xz.>U!QV ұhf#re-RJz1g~ }ƲY=yRd쑓Ni9qHv 1"dZ@PqQ32f6v1N`YX].//Y#qKy 灔":`$AQ UI@HF,fDD.Y4kc캔ssM -α%{lдu{P#!Β =!rD#qF 7sMg 0.A!0Cppчӏ4xnhONNNfݬ><7k?[GÓ]Tehƭ/OVŶ9ŵk׺qx\\a b^.: ]ݕe/zՂ_~V+Ω/S Ðkm[uLznꥈ4Py{>{!Fٮhެ_o^fmS 12Yu\mønh0{W !,Ųx! GKfsٙs13KB0jaxU*N[c MXV7jyp=<=3d-*:UoDtҬn۪]v0:_TU- gVca=5<==}WjvΝn4>ztGX߼~,&_b>{??||tU~u͠!zw3d_`9[OFu;>YT23Hgs}{l=UBw:f f!€,?Z \?=ۖX;&Ф$@5BU=c.ݜs.AD̡c$"i 2(,3ED8|~{{<;/q3ܻ7c*2$A&sral?!VNjÛ8#hqS1Wtx{go{8jDj8C/"4TUE-sɔ c15 3; z\͸~vGk:v0_\;:snݚyԟ_BaJ&m9٨DCfɘ9̀{()3zte3~|?|nUh:t٘ה0}{ҋ_z$8O?14M_]e=Q vOqucw~D+KT "!2 #"O;"Yד# MԎA dЊ8 TT-͞ҟ/w7?@.޿𭟿ᇏ.m\-`q>|Oo|8 ۡ, T mCR"{Jȏ{y@NDI, ;lvF/Gr@k,Faۘ8NcXhLx]Ëwgggfj8t:^3͗A׭p[W8*#̳zΖqV 㐲IFA |+{AƑ _I1naL)9X%7gouu{&Ύ}+LF@D3.O%1 0Ij4G 8\@d̤I0g]`Iz¬wUsmn[ #\޻w~TE ݪCY깻:lPg2{ ZTHpUcY~ R:uz%,M?臑LMy\uKyv ݸ{*G~U̓Z̡61d v}?oɫK7f|җޏ y!T<"V"`jdf@f4%Z ) ?ŕxL fXA@P<ج;MKSW콗cF_ 33/}`߯M'9]Ca.拳_|g쟭V>љ h~lg˧b~?{WfЯkdwg8+*1{˅ycejX"RKDJZSFXU?4޻wa?Zmox6jb$ w,o g a}\| ?|Y,6y*ǦXl6kB`ҝ;"xͦXQk!,GqlZDBBCLГsPlLQVsWm~5g6NEwh/d|~*y|լ`ydfZ0ֵxy:;=>m./6ChPy" j$`>p^u{əiʔt=>"`Pt LQfseR~~EŲ&yeL1ff]n8::~a^& ="ΏY-h0 +$M(3BC3钙)u]WMͳZERHyA9P= ܲfɒFK'b`"Agaͼ;SKrJgMun絿7-LluFͨ=k7}};O|I O!ͲA2lY-&Dӳd(*zq\]gЉlX`$ōAKoY>b2BvD4M+{a/.O嘑C09zհ{$Ql6c~m6 {KR({Psׯ___l}RS7|3*jSS)xQ(2 ܸqH4\ιYZD +>T|sxDKBYD,"ƞVx)9qdf;l65̥6Ӝ3cB 5T%4Uz):dbrFԍY4A|̚3+df`fUcWΚPGzSJV2wQպx㍃zO6s_')#qUצOV٭`g>+<_4_׫w~_7g?^~72u`>À:z?|8<я~ԏݻwWs/>3xfAE!M!UŴW-dh Ht|ap9I[6<3msÑLӇo깦0QB!5ZO}yc?~wưLtgv$}߀Tl6ò*j6m]IEN#sTФĵeea'@DZ&ૣbɣZ:`r2"SR1J )ThExppŗy.*jvNh ERlZf_Lh)z3B\=z9?j>gYu]u5@i08$  Z!@,:fs>μkQcHAIJK\ \6QbEb|59GLisɇ0V0#1E{Gr~z\9t 41_($ l2l[NrNectEE537ν_kvy~`wx΍kknz/c?k'>d~l;dko,2_~l7G~GxqqrTە30(/}_LՒ=|i*X b~|X &Ǯ}AuBZo 6 G11"VUH-\CT'y޸3wXo?߼95--=|XUլ˾_qsU]Ш2GL2}WG4Q[+VfW!m=a@f&7ORJU]vݻ٬Q :9r)O/!PHBHZK1f37fa .׳z>mCzn] !"!/&^Y23)@Ԕ hwGgZ@9pT`A J=}Վk_[7898lO/6,n]lT3E}/q*Ttfb 110n6s K% :fh cayp.E3YmLmGQS3;tTZ#ɍ_ݸHlj*Q5ԁ%1'DtU "T"wY?U1o/hٻҪn!'!!ڞXXquΛDrddP6#"DUc"2G@X3 QsGU.ɢeSec(&"6YL%=4@ځz=iV|~{ z70bd:R͠)KR%G cTۮj<8՛2|_kg qT<w ~s_VB>{Wګ+r֭v;?H{v;BpNb쳇rUU98v> '-,{(+8!"1#sʐ2-a@f  3f+S7sؿΛ3;9<88Hk}J~ !37͔)}ZT)NJ$ݻuJ!fX4ar@ CŢ,ErJsN92AD&*vSO=X,֪E\&{$"ࠪn3 >PW”Rǔ;X(M#FXRBu X-Gゥ"㜋9M!&1)䘉lr!crĞnSB@fzsaxjs@}]]ȼqHbs~&"UU4|M>?f/zu.]=3W%+}N@z>X5jV$şwgoz??o b+L2_o;o믿5jj Hdʅ1soMY,"s}juk6H^1@.aLcUՀ92#bbrw{v*U0-P8qG]ڒk׮ݸ}ڵkN)}7?|嚂;<x7n=:}!j `R7$kzIa ̢w}^/'qKb믿.͢{G1 բ rnb߆avpa<ݿ{p޷#~nڶٱsȬ5M3wiXԍζzs1iaկW8j4kCh;uh$wjӪ\䖰syBD vf6o(,+/쨝Vfyno֣vjvppbFpΙYɇ,9Nb0cb>`WLӌJzc9Rnppp`+@s ')?Rc1&DB*-Exz2Ŭ>~sYI060].vS`a̘ǜA<')/(v>A?='׫0o)BD tif:ȳݸc:ZVu?H'~ْ#b7|Yi\1s `+:&aZz bn/g mtDSQA&tD]V 7kI[?kK䯿Ȯ!cjd4@BP>e 48"isjURݜ~r=VKwV3cH}!~.nʒF@A fU58GD@ 173[=L&A("-KM.g=Q`&! y-ΌA< ;sq@=`N6}Rp.,株Wb&`[#"-̵x 4ڹ;8z>d^[-wԝ޻sm Y)01# #`f ZR@9j{kׯ/|扎Sy/4d㔑'IYnT>;0 Ol?O?o~˯w m3C۶{GweU9`+ɑٓs#NU^xdkLO^z÷}?ۣcU JJ=5.3+{oeEdo;'BDBI=KR> }-"mw^0BUshaHf9g؍Gsc,̾ őRUM'nf !͛JH=O+)X6%ͦ4qДp۪$nILϜĔRG}dślpW>E1v΅47_.4fVRi U<<gD4Ւ<94VH|!wYp2,U9sUU(Qnu0R%IFEGΔG))sst{zgcXH̕osnV7gD\./.. ߿Z_0ئL^S054&DJU椹| ,@DIC5q<==9_$ ᓔy_W' }m>4_}jܮx?1g5o?<}tg[fvN&QT#T$ZE9{ 2 &j3#Fݻ{Wwz筼=:bfcʉ->wĆ(H E1 e3J3ͨ͋]Bm 24)qlGwϻGyqw9RU0sM]av91gu|57HQ8F0#Uu8[٬@aMjrѲBVc9\~Ѥ @%YS!- lU*D͹у9&Mr^|ѵzwQD,)<:3 >7R'Lb҄jflޠ19bf * jҔ8cpEn\0r,K%-E fINX#0jj2]ij;f9Sp\|H^^w1u?3mksd׫X e@3@bnEFg,iv& W!R^}Z403%k$K~"f.Fwe骫Rts#nF71]K˛qy)XEA1$҉vدYU{[H}O~ ݯב[/ 9K7m۞}/Vwɛ'%?<{.kdfb)@)LMT|ڥӎ.糺Zd36A=rXf} q>,pޣ1fwO@^=xPͅ'X_Ycw)G"TlVP;E>1:ZԐ Js7hY?Y8wןޯ_ =eҔMnBxV9kӶMX{I4IKv8wvrQҥ{G֛~ͷ/Sgqd9+wmi}'?~ɧGUX&_AD2FD)!*6)Q@mLLt7!@`  og Qj&˰Z~yPE &d2kV`:t+/_6MlV0 xRQFO#~Izaq\YU.Rdix$EQLJPDI&k;.!m|uʅʊ0"@NPb.a Nr5,CѲXWP`oc%%"=1 k#k7;u5ɗSeiTCbCDcؐ:;T9OAH,9f96g6&⛴}$wXsnRz'@d̡׉ ZkyN,~iDkIy%<7\Uaʲ~}/..AKF> Cp6:5FnY~9 3OכW/[k-* )XCLq"H0d@-1[, |wzO}EGuT6(SDDa6A`b38B0h%U Ƃg!")I! b.H,"b7 yG ![$4 YшqSuH^l>vmY*Us&(+[A bcLLHGggr$3i&//P+[F C=PUSyDL[qYY@P A&-3Ċ@95wfgg_~K2̒Bɸ?EACXrH;D<&3!Hp; !"3mI`?TMhݬ\'qG(2e&S&%4Tҷ19@4&n blj] *atF>mV·+ߠV\O> L)1Ӎ%l^x1͌10ܕx&89U%r8KLrY<(ms2cmY2(ݹnS9[p0QZkgCQr RfP `PfMMCCat;v%"@WG/aEQ<&ڪq'&֜!%.g)Dk&-{P:kٲRJ1Qjw8<$1[Hjq^?Ln[-cLd~e|񜌵jH=|OÆ !TU3cLH)f0<]#Rj֖m;4˓3b*bQֶ'}P.r9V  )1R"fE)c*'g<';_TX׳ZjQ11Sւʁpmݾa`J[;ƈTLW? _/ >/|]go}WUF!*-xT kMo~W?jw|)oW+Ǧ>s5d 4g9g6!ͫ$~7!aChLVa耞10SA (g@PCL9&,TMVk+GưQr AMJԃP@ dJ`R .xJђr:Bٛ=",%K9W*owg^᭦>Jo)DZOCDMS-*ӆn@([)r1;; (d__ 'o<9u%-@ƌYcNIf $m72tYĐb ރ~:etL%cU1d*dqTK|"P HfqH! `ΧD""/ڶ(fzz^]>u==jVADdZMvY-Lfe+1 ɈpDߣobۏ0n6/2/guN3(!0+,VXqOe%i?xWӣ#]JRZ OjRVы,b9沄Y !N(QPPy_L_o^~e%fZLX^^-N tׯ.tܼﯯq郇?ʰlRJ|^U0CPU OfIu6:"!!}ժ5GGGfY rsj2ھ9J6 Pl Ru; ؏Cjm(-a5g Df279gLHDx.5 Y"L=z~?|>]=/˲;+@'54)L80Z w7rM/lYbԻ׿)DVE8>Dkaz,o="q /9XW,,`#x"ܸjYu;M:AjfEܽTdQHĂIlR{@D$(H"L" j3E\-:$!A%B&1It9tQ%0F cʉrg1xg-΋y5?L%\9 [U~VYm4GZDVmCR*^ SSU,,)\ l?ˆLLI1ǔFr B9ƺ) h3BI5#ɩY5G2X.48c,wIEP@&1˪382L3xPNIzIǨ-۪-S?(gƘ4/#_zusw_}gqv<+ˣgܻw6?=.Ζ0 LqOnd~յ/f͢Yx ȲD&ȱjȰ:1I6L1m,0\>M;'g/7* 9X4y"/ $In`ќ&PES<$L tAVFUA`( ԎzVn^y{}{ 㭋̈́~m6qM3ݡO~ EQ@J"I薁_a 9=>>&AWWW%NkrE5 co~>q7DiX XG4QVC7}¨B.$H){?P̢9dd0rl93UUUGGռvzxO{usqqn'0nq_Znb1g1;cnW{⦅ނ9c(eYDɆ\|uʗRV"C(87|/:t;"JLD(S$Egɪ*4ڄRDD\td9CP ".\qttlqdj(e__o?}7WO7~7dz(*ȗd2BY8ɛq}$jZ IĒ-9BԫbUU]9W>zOOmY뫫+C6T1A'ݴ28-,6lo -o_LT>>Z!}6"u~oyzu 82s]nW.05> :k %10GhXUu$c]q8Z@إg?7?zݏ~_/TYΛ_UE"00I0'59 `Af؛E损7t~ln w c%->Ar 9@>4#fIJ C({!G)HvGq|6v57Bޓv|j_oWpΛoO}xØSULV;lt+s>;9@ 8R~G ;Ucfu RVRfGІ>1D1Ox+ꐱ/!/}HUYEiEQc:YI%j'hHNԪl(fٸz裱eۏfa(/s/P UiT8: V>x|w޴i趛ڙu\sΪ-uϞ+o7^=e$ٱ,"C6`!D_{L4JΩi0Z[09m8n}e%$92gd0-J wAST2)~11K&YTznޏ5ܗ_?7< ^~Mur]S3I:lUU1FэDd*ypl bB~ O<1d"*D Va\TcJId hV!@]`2zgKf,qBۇ}Q M<\{`f)Մ,9I9pwkESеχժv>dT{0c(3d)! a̔] sH9h-5h41P]'(2 uI}r9&R(uaErﮖn48 m d̍cオH10c"b5+rӄCUhfι{<| {1 P>::m׻W;ۗ0=j拾γm%i-gfhA8xm̔R=pF"N{k+('Q.D3ϟxZ~q~||zg~gEDyq5#ABիW8)MQ|N/((u//8zkU Cb$,&{ZT(9P`=(,t\DhM%ěom6,l6$q9b BX15Dbld AbnF5m4Ն9' Z6|)%! Ŝ3k%Ieͻ]#rF6vNj⨨TzE)2h=}#=7x9w99uYD0Hї{)o2Z MOlR.D Kl<\Pu0XE)!D Pv{YrybHO"LƐ39rvBЦCM&(:93a22Z{qq1&!z ;+ Cy6UGMXfO#4 LMv;dU<k_fZk餇fm­x:"2-+nmHh>y)„GN 8̀ʈ֢ɪ]ץ[F&\Ǐ/>Rz񮭸3R6Θe3>f TZaWr# Θw$# q9DDhabQrvՋvj4񺧞k "|>6ā):FkDl6OZmծOBͨm8-,9cwG6,W}oJvtB;_X~yYH@gc%tӲƋ8CPTTqԨ#XPZ'JU5Pj B ?:}l֫WƘϾorT)H,F(1d ^T*{Bby8j)z}}`cqؐDcLٙ/~w&Zs?>==*j)W~Hu ۠l? _w2'}zV4 b"e꺽HB@v5DK?Z&ˆ!&Hp<7>9Xu:k,Ę+18*jDAs0ڝI`Ȥen'q}M8 La HZU+TRq:[>%{#P;dmӼу3#0&b"Z!lZD k:0ּif^.ΪL牯׻_6#V!9nVG&)@fl1Dl.]qZkWD^ xE`X{%9vr}yn179BN`T4Lj6>0)OaaT rE0EuR жݾ|gYU׎1d4";;4vݗGߪ}y=}ܟWqZ"ݿ7^\q%˖MJ#")qYkF+$C~wO㧟~o?я~Qu8^]]&&_DL9;gbv Έz69̒Lgv9&LuQsSc%%kZMeMmn4MS3EiVW'I]/W_wmY䝪 `-,X&aH& oQ0 )%Qeg4c6H)B Ov݄ݺo|0S]ݾڵm;1FAi CPH!q[NZ *A Yݐy4" !>7o䜃tQ[+3@qLr#&w CNPX(0&'l,˦)"M)KSUs.) f1nO1xn_xN㢮ݐW)5j2@,T*[MBAq̢A;6^'@e]CTaz)/8= s,[+s2׮:\j`\v΅ۀ10cڙ0ѣ{DHlTadc)98犢hEzr^]\Z9'qqKb; Cޗ̮rgggQq۽|2S GGoTիϞ] &֦,˲h<3Oᐠ5͌ƔR,xLv;?2@rE@mQDDd\|\΋_WT4'V8fƘs;jdc[$D4*L׻䤴֎ID0eݹ+}]a>9E"dYĔTTnIzk7_5|%$ (~_!EUX9y?!,CNu0I,, xfO Wl BL)wQOV 'Y9uբG?;zt6:)ΚC9h]kxÇoӲHǘfY0*9GY'+bfF LCoc O -ۑe9{Cwt:r_Vb؎eG˺yZ{Wq=hkJVrVӤ0y;7[`L &tIK!,ڛ]G0RhLӈo;VG?8!''sWF)8CCT #Çg`PQ F! D޵x~Il.+MlG%߼pKM1#ϣL{`椻aJf֛W^yxR,ռ0. \NgMř;?:ŧ%jVIFF+8髫՛LG}ag2V];)Hpe=`~*0˥e!Pݥr)7CĔ;oϗ'}ׅR&H՘vǔUřH (P2) 8d4a{G *(d43&1(,&ۇ[\ǯ1:&TG$UgB;~=ecn:M sΕݪ;Ӭ͚ cmJ 'F )hsNA7=vn6(sc]ط~{WUv]Y4p2)'=ЦLqn `)m6{S! m=^d2gMU/~E(sF,Ku6.ƨ:= ~MN''ys^V^m10L-H1&)0驵6hyL*wxtbLZ_woVDaQ}7("zh;9d<`]#"eI d*K:DDhCYϚ(DZUX.uӧϟJ!}XCF bYƘ1hu4ͬ}}cW9k~qf̌D kc|uomע֘(`CwF!aH ^ -ٯONNau]3ku9[gO^nWz07O>~n#UjMS#}N D1h<+c"m;]-E}삼t^|-w"|,lW\_](—MDʺjѰۭ]uUC.E$gT_lVezvDiUi*WU׻]9cLYw=Z^,MCS;9kb~n,6)-wQ:*N-_3qX8V81Hzstt4.B~?̴}}?i]}gfٜ?y]0*N?"R/(}E4=Y/ٲJ㫛ju9]=87fT5yfN6[n.aVuUZe Ɍ>Ǹl|}[=n'#D_۳3SRVD\.70YxJv_**"gϞfv4}-,7yoR+^/2_eAPԄ uXd?K_W}[޳Y SI3#p ,0&ie|WdChK)up~x7z}eoL $H8FN#iVX2 BDs  jbݥ]p ʐ)lufNnTC#$]ZƬܢO[QrvDk yHC`űYa3q7zМYJmsX&yZuB.kކVG͒Oƪvde$|qL.ވ *z@N}n㎌hv*AD9+Xb*:c)$8cDH'iaĈkd,lN}SIh5hj ju4yئ~8[ۓZUW{XݼXz4[|fuݓ#:Jǘ|ofS! fG{{bKOgMor 7w8@jwm]gHsj< t0h+a(uӜ}Jh~H­ q7/1fYQyl6ynDsvhlfOu흳1~/k t{ߟ=IUo}۳xqLΣ1#: *`zI&Xqt kټqqھcYZSі#ۮ۫ cA.؈j+&G8]5'88cD֐a"+*dd)kiEQF6()4w)hbfΚz"MKPP&2&!rNJƛ[Lm?uHMH 6E{⃋6]{׻>x 1ǻTYkKFhA  f׮F'&/_1T[.弼 iΗ=[]7'nC5ѶxsN ^l? %?Y[o=|q\~^|a4z㇧~3d _kAt @a$8RLA>J$_^6)n>ᄍ'U(a19_~O~oֆ!M! "!%% eU@faq0c4c c}߅;}8'|>nbՌ/// Tbbh a41$DB .M2S+f6Scrnׯ6;ytBvΑ1:8vzq+n EZoHDqxl6{nq8&ЮsRIcs3Ș/Åe̤FMy:W(1_xѶmVWcrY`r>tztH ->H*S;/ɀPr n}O*5( PE㓓|qŋo<||~~Y3sA[N:diN@A.GY,/^կ~5wg9ӧCO_x1SQ3&5l6׆M;t62FD.u;l6mbxt]V|w~m3)YZ_/:wGN,bD {1}h_߬Y{;vw34jzx36*Vy}_}5  (+@ * Е,sg?/0ڱ/MmsTw9r-'o{mBN`X@ǖXxTn5t;?Ͽ<>qik^Ɲ#rhMHq[7.`:;yS6]څ[A;|rzgFL؏ykӚæ3^>:z?W ߽4+A093'M2+4^|y:zs[Pwد _ylnӟtRY<*ƜsR90Q ;0 3"Riiǘqh۪'O?ޓVtY5ך9 !c)9o*Vx}YHs6 8t }QꪮkW1R֬!ZC>uE9k>>>qDdj~'"Ru8NS"cM49'"{B6Sg777nVV4fDI?;!;N@Sxw;ФeY~Xg[Q%߶cҁb97z盳y{}P;vw(<6YxQE;'o(Txy Z{~~4Ja Dt)ςPk;vژP̃Ջ_8ܻƃ|Nɥd^ 1l6}qm($),G}g}v5J/^8ܿZ{tt*R~?ffGŬdth12^0{8yth@ʆa514b2Q95;cX1"sck+B $ۘz diU}O{kըEU i"5Ẃ)v<Q>cpuhhR*Sl ΜȺ"u\Ხc6|E@lɋ}ߟ6 GxZqQ7ރXΒMc$"w v&0P$,cUz,˛3g**N!eSz)1EG hhLB0dW[C!A,j"Ji6Dٮ_1ed8vZ"뎫rhyi_>NpMb6n7F%щ23'IP@ 0dOTQݽ|UO_=_IA>.b ~#,4nopvScHHN+T)Z4IDAT?k/]je`V]qT4֚Bm~ur|O3yc)'DdDͨ"2K>ا`Ɯs$xn/V7U2(&w|jbUyyxض-h^cXZKDuF4O1*g 3Ʋ]˦<ƾm5(scUUyonnkϟ̛1dMy_$wmGu9+\ &亮/ u'˜vLD+n[!`%jsէܫ/qUmr=ӤmMjݎz[oXu]n[lv,L]dXj2ēK09'嶹 I'k==~ܤ$kbu(oz} N,0rB!06)I9DRV6N%y55MhEK^f㫫})e\ Ab$)0K\x>f:RYl*ck."} rgpImTuu\ԧ- DInRTZGH4nQ,ϝ9d(Qv ZI&u~֘C#:`v],lsm5/uj^0'},T Rqܴؕrfgmx*T/i51*! d#q4M6TH15 9AtVWd8TA)%v.;"M(SQ#DPAV!l'OPMb_/Q`1Ckf^Uc0tEװN}HjCYew?N}}13j奫o%<(&3Ā QH6VQt<;u}Rp!@\ƔU/+,]cShckg;~ϟ={v.:MںUnKZ1檬MG FDP@Y$ɌU޽|~1!)Roҵ*ȿ"yuدL|ғLefk_z}ߟ4~-b}@vºkM .!KtF%pO?/шe91CQkΈ',[/"!ng0ȄLUͼ*gƗ&DH)%N!1bΦ,g{1EY^l77v,KfcA9)KcL0"0q"󒅧Id^SMyYo;>>wɿoJUuaE{K,C7Ջ:s _(4$01:=P߿GG?kU{_U$S&W8#XVxj8RJZ>0k6vhƻ,yL)tKXUsJ$7 i mw?^oh䘽XMӐ3f~rLg~W^xbNΔR۶)TVL-BPc_'}$wUљs3k[o]v~r!1ӿ7oe YFV@wn,()3`L3!u9 +#6 Q"˲ld=ZSb&[m4Y3GHw Y-10u{8f ُͩF>퇵}`(ʝT#=%Gcg6 -0F$:.gσx1MF0'\{? [.F׌4C##ԇhjS[dObfK{issT0Rfiw6.Jv9TU 3AP,szPLi p'6h fim)P Ao] f5L7+4"IH!TeNx|_X}NBL!VuY]&Nu7Q訴R`u\uU@߻58ʮE,/G6ABkԘa4 8$" Y a 4dX/ۦ9~{u%-hCާO!CQ{ĠmOCCOA}zzzf>|ߏLq#`e0Jck*ȫ7!;ӻm:)Ćj>㭷;?،}DdE1P0]E>ȗ5//lM{vm[a !ini݄y!a(n蠑$Pqc!BJϟne-hqvY2`Ұn*=}qa_ov%Ł[w8&)@Fmslxf5R"2l qUM9\t1v[[r 923+N7Q&D #0)M/tc1c/brߛs9 z9dUULĎmP? Au]sLH~z}-`8ʲb .Dܠad a&+( N2mET$%] MGb@}( `" Ĥ@UEVW/=ߵzUƁ n(|\GEͪm;la"#a4a )J Lŀ+/z\|Z˿yL;oG[fCcVY]pjX9Yy~: t˰*c['gN]9QfVݟaћ޷W~GRvZmMcwOWO~N"[:o)=ì7?`ٹūO.6?g4~c ,IBd #hb:>lt<35%"SX* [c*A6lmu {K,ɋ؏mq2.X÷iFy5HցdcI`jzmRg?_E߼h\Uuϟ-O@cq6yjhʙbQ޷TF%>Ҧ~ɲƂν?Sbq'j: 0FQ% "9)*#1'DK|^0+brcL1;!3HS~_zGw6 CY0A5 8"F#HdD5$!OYҶ]K(oԟ };Q (+LKFȘUkBHhl1-v Z-dS* ᯪ̿=fe$5_1n(a&Xˮ(ngQQժ,UmۜsNrIDb>?dc(hnhyv~~|~r^UcX5(y3ʺ'vJ)q!Ps9CH୵&frvŜ5- |y}qtt̓Gjzn%[tlj|:c s˶'"DrXs.ƭsn1fzԝ132?;wcf|gS1ÊMC #|mٜku]?ydb !pttömx0A(ȡ9 hwJUUHa,cPB۶t$%Ʃa%gPEdM@Dl&YnSXu;&PU9/Krw߽'߾gʹ?z!t.TUY4/>{4 3OZCx[5Axc_Y9ND}o?>|>/Ð#&ou"zby_헱_Y ??__f&<oayHB$Q)~ndn60t"lYD`cTek-Mċ[%,d 3!҃{x'eMLc@@3`r>t1bZ@CU]ʣWcߵ;FOrLB @Z7sKf@i:uigPIc(+|х1Ň7W/Փ\YD\UE=]v%d jȖ=ZW9?o߄nvzZvc{SZ ,L.`1 攵idP'E TH`dc31S0_c{R?(Xxqvل~˦.Da!69g[\cLʢ![-[}Ǐ~BcTMX)טvƛlWt'J9!L&<| "DD((" T嘆fggn~'fHۋuNx*w?ʚ(svU4*NNZ_ߌZ%T:tޟ?}>xW_B!v: !!$L2&T@ 0el$4,ż~xlYYcNqȮ,d``RTI͌1߰!teg#?'H9uNByFnA&(Y].g[QW+%<>M7;[媂M-XS!4x. f8 1F3Fbr`)c7jSA)CΠr+-K/jxqj"() 0 !7݊˺*4Z%-:> 0uUh`j]&H0Ast{'67|mqcf2}Y >` }d2l).r}^W+Ǻ{g} ^2hIcظn7tz5ҭ?(IbD4ul NybiU"FEU՘'IYrJ(?z٣Ƀ75U(AU` y{}xb OJBv*_ھn69f?ۋWhLo{/!m[CNn a?u7>LXRΙ zSDÇ.Ggg|YuZP4X 37633i%iBbT`Jil۱K 'd)CcuˣlC qC'z$5&5Os{?9 `qWNNpuuU%M9ÔDAx޽(/y=X 1+ U;NE'1FYAj:{f>-&UUc׮_:+|/?t]7@kxbDN.Bc-3N~|8ˎ.i!bR_,l3;$)O:e@Z8sL TTnx8fA1[gnm?~tt4?ZeuZɪ0e.Bsp˗! w 䜙nhb k>S3@)?zl yq V+^-{\mX L{ 9>gag5A@F<~J׳qfǺ.rGg/|x_*q;HYh; MRDL:)PV@8%U}|oX(!mdp"kLj#P*:>bsRU1F6ٗ\fma 6JF2^^> 権f] 싴Lk|r0ȘcQT{-,\6gIRu0 ! FÙbI @<"^o֙ۋ *W/0`)`4t  DLj.|Z!"v(Vɘ#! JlVHlmCUt%[g5320(!ID6҅|C~zuͫ1vݸ+jD2b; }Fm⭈u^(~\U?LrS 9$_Ff.Ύ(4!kw-#rS齟j[jLzi&3k |}_0=?,9,]rnݍf$AJ4$%qL61b|3}4#rdn"H@7hR][VVnw{KmMP3aei7"??/Zk3>mAk[[Sj.!| Su-O0\n-`eymEp6R۶)mpYƘw>{˲ !,݋GC=̺fdv1Ƕl'lOvqG&`0(>v>GzvkymWyͳMrHn (x9rDչLӦ5@7߸|9=[?!};ִ_!2H6D"bTQQVq=:<^܋?zkHqz9]POpZ֘xUTZ1t?=7M;n&# ` WWS*}/MǁTpz Յ~uA ܩ-&:.{;S1z .Гw Bd'pJG謫A|ԅ6Pׯl5) lS(FqD٠Q 8 " ǣf/޻TvRV^!rQItuYJ +g:N4jR H%gq16,Bj:L?_W:uK{DbWzҔҨ)&5eaGV͂UBpȝ>, ժ@eP&`DEd'XFW._x.^++^z]eXd(Bӆz8wpI SXV`h)m@8A;VEwL JZ"4Hшd nʥj=[mJ9Vl1Z# ,dY5kHa1͜b=1(Zkv /cJ 0J,4B1jt9L.:E0*YsRglU CR%K5Q2F%NDbm/B޾H.A* @Ȅ*"X5BEqZfҋw_͗7TUaDž1 AST0:@B@UZ0- ̞q4 {SU04)ƅ%#* +$ #(N #Tjv!d5sjC"cZǨCH)Z/C/rX#vт2?0t) g"(0(BF<}⚀BD"U"UaΆ m +6M3~7޿FdE=STEQU4BOOt==OӫYI[c}yֱ:~6\ܚg"-a-bU9shUs9$ *?c]rr4|+_yW"h4ěhE$B``osMSMD{+QZ뽉 )%5d#M)֦1&AzQ>\5AΡݪAM E~ eHh7 9]Úp΍ڹׅ#:_UUUUqD}Jmb<9oȣQ 򈖙cLb0%܂ƘtEv7^@1bQX(ʕ+w]3_z>;!kS. 9\ Sd*"0*JEJBm:PQf!P0P"Iʻ >K "%$B"YS3t]g ッQkrʖeaX:Ra2!3*nXytUAC"u@}ߣ {vvo6]z… 9VDӜӼ~en*5?8uUO[u1s w qZd5'']JCJls*TE"%Q5 ,oJ&(q m娶; rF{H=U&a[)¤bԽip|Zs?v遊 Uݛɘ}1LYyr cJ;S g,z l+i&q끙r]1;lâ(D#A1KcLI4\b5JkAvx[Eq*4 XQM=-jz߿X!HYK^ѩ0 AR2(}ͦHQd)8$)J_/QƜ "& UX1#X?D+p`dlCdU7´f9.UԳssV! I4J2baUlMZbD⊫֠!u"V)  14xDPKJ 4۩v\Wu][1GG `HPܺCܚD G3 YD|Q }* c+եW^kߙ]}9⸓U܊:i6'|:;L5\nR9seec-@UU1G}{u]GD!Ɯ47ƨ L&q)8x\At7|,k{e!\7nbZgRL1Ŕ"DDh4AJ))۲k_u"n.9g 3,Lax?㜺0l3!f&6{&(E1ޘCQ~Q%s`6-%躮p:/#몪$cbaBԖ( ˺V!g= t]u6Y,6m $]7ȀДUL*x}4:KSD@&W$eﻎy-$%U]YC|>ڗe,}٪_paރ;W;lrMhsH."3rUB].ڮEQ_yK_{9wdZ=28R=svS꼪R"(v uH.{h|e?q)f@ UD2,IMOY7q&UDQHP4( #OPA!HD$; 4*m8>Y^ƔB]lKWF"rR,3 襒ByYs) ”T5Ȍdˢ2QQ}dc ѐFQ! DhT"s{@lWKa$㪪AVr<[ X" YNb6F l cW^w7]9*zم|벞F{SNVbńZ "o˪.zuOnRe^gp+&3d0GGGggg(>z k1\v, (ՌY8#5 Ar(;t:՛7_|[woݸqK\N6mo6ߏȚO5278m RJ1I5O9ԗ pF1r󊺮#[pι ]U6D9)c8fJ2XEwJJv-Hw!¦S=U!,@k`Ef`JaXSlfSX+5{bJYx<{[ѕ^8 "F\V.?nSh)n)i;S1QYNzn8=4 1I0hQLޗ׊eZSԬР!!CBضu۷UQ51fv4DaM8;ﭵ/_7W6_uh8x<&]YiKەYJy3?Cj\L7eY! 1k2Jqj"߼y rf {˃hN@ XNg V 'g?&<6\Q`Kf"3B؁M$j6D2UH:NIPj:F~oүo}pwO~ӐzV.KEJR=3CzT!a:Ȫ*IhتH*ogRtnI.NiZbH qn긌XDۂPDʂi2vͅ9*Z[O};yuahՕ=_pPca%%E*vR)4,1Ge܄TX jBT7薱0U\T4+'UKH||pM}T)U sߒB3 ޅEH4>hjq'k[cͨ,#f"(7ugLc(Cב&vz\o{N}ړ33C&)ˬ}m:rTSJ#f~^Hɬ1FkmwF7oک5Dwt:%FEb=d%4ɐwΕ( I!pȄTMӌ;-ڶj`E"51}zRRn@DnRn{< 6T՗ eY'=3GQȑfY P/ }sN$dAc;C*c*AQb \r\1 Г;? e޵\01m.m1׷hm 1r1+3")%Y[YT (,b`Qr9@6FW>$"%DDT(@)ڶ0 me\/f3r*k}Yz|~tr\.Y,˼zem85?><؋ަwN}*!!V&Qgcԝ,9 3 `VB5΋HRfΘyHj8!k,kB! EJhxSM|}5HS߄ll|!Yb݈ X q_^*=wƓ-%޼{u¾"z’sVzl=bAD 'uQAP&!kЄJ'YB3FD@u-L" `P#s"hTWe۟`Uc{aVڬUb&inH@>Yfr>28!;9iGPO*H i$2rB8 ZY{<,ђjc@P2x(blU=:W(1 hB_1 wD}1K7B-h,XS:V\!j5L[|4t]ıGgJ_udJh< X1'fJȂhzak` @|W-a1 86iw^QcLyҕ1xȾyg˲1909jn]F6H"j2t0 Mdq\^- t{s.3Hɽp6MIoL|8AUEf>;].߿yb@Ĝ˂'R5+DGD5_=~gH\dN >=𫹺1< %@iW[wG`¢BA䠪95~' AOYD ,A䋲x޻wO_{_5|/Zk_- J"7/bΖ)aԍ(:_I} JT& #RwztnjZuv5؜igR]NT R`Z9i_ .%Tz&XU$4 e@ 㱠U)(:T`>(CA0)o}4"$}Ea!,bA*b\q<2&?+lU u/CQA6MHb<' z([>FgԣGDQ%GL6ИRBIZt$7^4Y[TH" Df@g#$СCהШ*Abe *rP:>8@$ %kdp<KڞAU=7<ڱG]LvvGlzpz6:k9\~OsgyGy^gՏ ٞƪ;Gmy"lydy%m)gͶn]mۄtnrmDDDK)<_,9; CDΝ;!߫jFr/rI+nZEYQ| -,ْm33')Xnh}#ӛcU !fcºEZذRJY#bu[)FہJ67}JQ/fkl[)<@ffaZm#ݫ*3ʲD[G4c6 ͶviT_›b wG`r++Ιl-W^!4.`>jq8rEȾ3o7&$CR%'n9*ݮ5Hqs0qXkM }e86( J+&t8HG)z4UA֭A I ڵk~@^z%ŵ!HۍhDCZS}FǦ.BMeaY(VVCۆ8A Ѡ p<"džUjQeТ#) 3ƶVXT3WτYud" uA$夜)ԕ|y6aBqt9,F!VAZWR4XR&@QTHI,s/tafƵօ@ h%U',DbЏG)n Z3 Ejժ*!)BWUo?tq΅rJbafk bC> 79Nۢ,|y0H QIeevM/5X{@5* &(g.34 aڥɤFe E Kɂ!-5"1!5]h7<g^q6e'NZ0yE@ j@XnVV;Q@FbjC"DU͝ <+Z4q>m'q/O/£360R^X p68TiBj7ۖk /K?OOO/^ /0LrȶyZe;؄KY"_}y;' #ޓ0Vm?i_37@cG!OVxs.@ͱ0_Y-nTrĔR:88x"J0 섫iL&\UI]n$mfoo/7!`嵂6GnmL4Gvݢۀ4=3•+IrjzE۶Rɗ`UG"v]wcvXśomV{I^ݧQH T5njg#O}ڕb8%c{Fܑ3 Ouз殎;^kV`Q "!,\"# ?[MX΍" dSX獉]}W9%@ Z) yFDXX2F kuGCboOt^~6wc7ynjW0\@+~Va闼ճ6aCu]ܿaX dTPqICs[kp0샙KuJS*WCs7ho6}ڂUsh҇۶e"2Vü.jE?;[Wo_ &P` "`N :4 Hֶ?wr6F)r*>T )8.ܞ']8x/|Wxo1sɁMK$E.SDΊw7.̦j< T.e5% m@ BDRs!B [8Hb 8` v}ޕK.*G&q%NIZL6Ǣ/BJqhU7tD Z &iD!(1  }j~~Өz:jC[ t>oCg3};Q@ (gXcpTUcIU0:;^~D .6yQH)a:=1kCClvSs-Y"soOCv-4?`_k2FiF mZc"|Cv黀H ^zs=w??vΥ>(\c簉#2M02rX,2Հ3ڒ9CfmԳBh}>|_3$fF<3Ig0mkI1z|V`=zڵz o[9s1g\{\䇿 v/xMeۅrJ=VlQwyXe]O|-qgÝm;+-rro| !'?ywybq(Q0 m۶mNPUE&wė3 f\*qYK!@5IE !ü?߿Q⼿vo{__?T帜V- `ZaĚ"Lw?=*.J\K/꤮&BgGZیzk)H'*ER?J@9bh3J*p50zW WF2R(VZ4\Dl҂T0,(7,Tͭ ł&-L",WU-'BuFUu(PlDuTIH%)anʛ坻'šuX/c\G `D aHXڽW;@!‡)85 jT@Z2r5fR8.Zgڮ>ڒY$o ھRHQd=A&H Ɋ%*ԅb~ϪjS{Om30N}K~!|>l6g~ ޭ[?7|_x|tz_  Qq[ $RL/|y/q[wom+WR,˒(e X6z-oLNfe*K9gMZq׶0UG9ްyce4MRyڨDv QzԞ4cv(C.\@|HGQZ9[~)* ,ضm|ˋ}QL 0v1ㆇb$!k?Ôxn;l>4=d}CfVS8m SͰA^G|8.3%{q9㳎#fu9_;|&"b,n`(w]Wv*ZN8ž[.4vX.^xxxprrrw:} ,Kgō 3 JUU`ʲL*)^N0YQ[wW,ߦ~Ӽ:KϜزa3n+W4w@FDB&Ff D ѲƄR _Kot/DrQ=k !IhAL#@Jv/]OQ,W_}`RYcVf+3!@H*Jk*Lޘ!Z}DA LQ{Vhe` X$Fl%$H{I3z;: F@DPTIX I0"%ztNMgW^ג"Rd@AH[z&"@МKW " "2 &B@W/~~ᨿw _Z61B Y8J;rE`u3y`Z+$wcݳA :2Z#Dփ#А P ""($- þuJ켢cF]L 30'$G*jPQUUc YӁ X/H_-V%eCOT59z|/Wm7q]@Y E$*Q"(23hr@Y;rV%AN$eY f8x[|;;<Ta蘑V^ezENv|է^ƞh~YNTE2|UUOhZn{m˗/[SUUIp%]։Hb5K"3',CFz4߿fXK.5-VoWt:Me"3N' Sꪪ6!ޫޯ&!'amiʲ23J86%X$9 0uM3M1;`LD, z9ݑZ#%ٺyIp#ApgN_wP~/||fNB#BZq8vic8GQ$9u:NVӑ- @n>[r|[RUL]״mKŠ}t9JgbB 2 "֗bs_=LR'kb)I0yJoy Y䉧a߶/bD$Eŵ!! IAT aD$DvY_V+phj~figUY"+(E42" UD4,OG1WI̻mopM7/˾KT$7a6  (V,"R1 ) m`JN@tQ 6Jlm؅0 ["5DtuXź2{򈰉LrQ*) cB q◿4sQvdE"DƨYDNUF@1(E}o^P_|uzzW5 03}<_OgQTjȈ"Zp6%(߫>¾E3ܡ)$^PPH ;' ]!hj&uƮ4y]26k0h<54 !HJ #!Mg@!U)$ h%Ł.ϊbO ,ڮ5T b! Kru+:MJT׵:wxxo}st%\Ĭ`DTqݰGsMǘTX>v.DIWK * f,i7oݺƘ-Ƿ-U=r42].P /0NNNr9YQ/RfHF 8 ֜Z͑Ζ}\G%o)\iV4ǟnNv匰vW:P۝%wa'~iKoFnKr۶ RFhx0#׷dm|[0k[z;ZoӬR[^u0ɖ{LVGz ff*RUspgj^1}7M usyL,Œ=dƓ_???_]U۶ryrr}$d?ɶ{G` nnaCGs?GMv7P`<;ܜTqV @`\\@M3䚁J(ǾUW8rz|1#A;)5jH@,_9WAFVQϗgm,S+wX;t}^(->b)mMD!XΊKߟ-k_g|#;[cXKthBZrGgx҄\in6L{r),lٌ{bgbFnwc4$%@$I$cq@ # K'kE=˒~PyU;e*U 1/QATd7#i`L4 "rk Eg.c ; 0=o\sB ̧:4^zwLT̬ڐ9}m"u-Q_~WɧkzIgb9pABlKƣ.IDg!%MBBC9ܜL 9CPf0/0xj?Ub~6aJ:qW/\f'Gŋ37J?F]($Z(Djk@ AC+& %De/Y&سcf޷o}?`9rh<LɌP!p4QUY{5<7!&5(ٺijR0 @ =>ּO 3ۉV؛޻~1&EADN- VYc 2Diº(13ܻWVŝ;G`Bd7bgiD",sN(gCk-(xm]i53IX8}"a01p*ffzָ\;1Bhd2݈mKlZu\ehV?ÝfZSo3@H ID|l>Zx@%jҪͱyJ)I<.af ?NZ9$Nl1wtit1@gu{7ML}g/>B]Mi4K hMJBJ;7X/ 6nYk0 (Q6ܾ}7~7?/_xK m۶Z٪JyK$i/w{|R=_E'䗮 D d$U46?^9jC+EBKDSbY3,VphXJn9 } C7?<{ӷiQ7^}+,zM>88U!2< ٣Ӿk5DfUԗO[=:U9 (!D膓U Z}4$H1ZsM)W(jSrwrc˸?芲 n _'8[GfnH*T(NUCRZ`4ZVE3V=SSrA} E11 > ВwZClRPHaFh7n 8aE4=Wqb Eb++pPYA \I)/fggS 1BT!ωa3 {UX7"H2XXk꛿jGŪ뺨w5Cl! >N+Oꭺl|2Ɇg*XlŖd %m?l4 d鈜`C꺠 YX6_ͻ 'q/??o`%32h !(9[22u|٣u\Qy ApT4U\}_27()dnlMLg],SJD#id;(( 2  )vŪ\=]'t| i[2hV\aЭ-*CfUJaQCdc4E͜:0 ޅwki5b0PmJ_:y cl6Ybd>{?z:n&f!OMpf&xXTB'#iӭN\ot] rr mH?>C &xzvO[5oOi Wd~lѶ󘺤αFM:I 1X;5"2Y %!0"Hj6q8Nv9_|yDW_JƦ:& 2#uPbjC_QISA&2?M_/A$HX-,?"蒛j;MCSTZKRFTzb *3JNT!E؀Yr7._]G5͠/%ZdlMM`!q@d`̞C"bJHg~+ef~&#H`rzEhrƕ (I (e0lT bJAByxfEFݨ x̚&T^ &<0Q?]~Clq]4.a`.O(ryimA V.Te6|[oV'hM:?VN[5-p>{o bVoί0 ?F8c9żA3t 9 ?qO]wi:cok2c7ݛN ^  DQ ,4̅d4dZv:Ek!fs޽{zj^eh4UUY?n\UUJI%e= iXEbv3U"p7x}<k1wNCמ{ݸ~w9==]5oHi♜]|':_gx|3W[Hnܢ`Wg*_]c[$7R%vq Dc@UTP$ Fk]׵T#>D iCIފ߿0\ՅR XĪȈAbBF%1#A#diQ@-]Al{2QZ gԤ?\x\Q˗.]slэ/?jUH΋&VD$ &HP{b'\`mL|{$ WOO#ZK)E^ld# [c}ym|r ݁PU5O؞Lᓿo`SޛA}{vV?oݸqc8K)Mg{"hFriy[!BzZ*"c[@FV!,g|~裳uJ$*`6`|(.+'DYkAA[d9W0۫G%ݽ}ҥKٟ(|g~xi61s dɊUaA["0hsv^ݧayιDQ"%THmRF4/wz;w@=k9X[1j}'"D(^ L+-ȫjI 򰞌0=8vrRNPx4)bLހڤh` πV@0 `?:ă8GEg\ C,D[P+΋/T$Z b4vQ_N w}( Hn4 %vhV$p1nl15uYUIO|,0C@cVvVոtx*A ELMbk03$R% I 5QFTU *y-H0\Hы]ajco,0""bX/wy7n#"odTZ"lgBRK0Ps/GM_K_X-rT J=+zioWNw}_rhCdHvÜgrv^4;|:9g4={wv'~~u okшvNNNAZ._җ^~fxٿ/chEA娓jR~90"Y+o3 AE$("g݊Es꣏>L_MI\7ߐ"nN:1 pvr\.~ZUlj3SJ0NL[9տsxhf49/c4 qbrc>MB/qRe>As>#lه0kYoZ30-q3hD`>Q̕DTSJ#%IJ,mQYcbqΝ||vxvvV1VDXcwkAF<`h]靻t/\y A\,ĭu]J… ?_y'KC7Sʯɠb˲Vn~uoygHeXcL'?#" $UҬЇtѫ/]s:i߈FƇ~/:pꖋz}W)9/pȭ5:Ia d(/R] -ѽ^xxJI֨du5$bEA30 )4ɨR"8D: 3[Ȩػ^|e=&UdwC)9qL  ]jB?t 6K5bzU ,`!RB '(;Y-E a5 ,6fա3'Φψ3DZ~ok>8kW=QJ6 %Ԍ]S(t 9C8(ui7m_PG:_1 b( >i.O(زbR뉜HJeto|뚝N(VoaTAA^}jPŷMs])ŕq /^r5~7οI?%џq1ꗿf ߈O5J2!3` >g~V_,cϹO=7oQɇn,RjRw^F#_"r5z7}K_:88w(+lObuFjT/UU_~u)+N2* G᨜u]F2CyUIy`lObv0h Ck-fg~v6 h4κmĺjNo  CPdufuMӈHJr…rmA<7:k]3ȇO}ϸu]_p}[I !_qؿ_J(\Z]#l.IYB7~[kǪj9( x[aqdEEʲ<<^|◾ŋַୟэG fFJC<M'??oOOO˲$}[oݻwػ8.eNh V{ϼ&(>2Qw*hrWE 1"eM꺮lmobX6CHd\+FU sc+#*cSnуSafdјT{ANb1fdN $> 齛2.`0;r )Q@% Fjj:B PiOzT+ɚe$JzU c Ca@Ib2z bժu|2A+^pi+/$"B1dQ MI@eNF>2iT2HU.]>^6{%C4J;k0:L$,! ] HUDp|%c:Hbb׳x!<޿w -] %I+5INJt ]YEQ2垢 tdz_~?x'9+>ܿo\Z+_x6|i՛ǧǷnvJL GHN`r]> BN_#t)F8 |܇CGׯ߽{w:àeY(F xnO秧w;CRb4d@p#I+ҐS" n~8%8x|c4*S}ߣ;fqCmΖ! !+Jn!1Rx XꚦAo,3Bt<΢(B O|.n;^ Þuv5rkZ}Zr9L*1ƨb(eȡ5Y'-nc 6 `N1)6yq6Ody`m/4K 2*J P5.ڽ.W̅˕/ԕkwvߞGw>xNTsL&z4*75}3@xP0@e&,51>DVрC<޽^1C qͦjKIN+'9Ssy, cU0F A%!(l (0HRZeH~&?9_ߢ B(HV r˯tt]Q$aZNi/#vhCt)|)h ѩ/j:)H"q7/=l2>Go=:Yw٪k` V"<|QQk*G'[NxBĔcv+~k/~??[?yY2Ζi޽U'v<ڿtW?8o?~2xl~1.Z&V9s5PUs\YpI<͐?}>|eM^#f>88/\v?'~o~_:>8,˱Yk_}k]tα>DGEQx!u9%`Eap|k_,"w(bz8B}{Iu\Z"%5Ơ`[V{sd-mif}vO4YU7]4s`p>mO2~Z9wԳ .f{k[N\r:V=w}1)e]7 !ƔZ ˥4Ms™ cܽҫW^|90\.nn}֣8*`ö:UU 6{߿d E]ݹsgu+ Q?wT^JIYnZ??_Q}ݦik_wszzJDۿ=]=;;|֣a"L3cÌ_G?ӧoy\mÊ]ޠH J)%Cۡ )ί|pwr/=~ʕk_y׾WN&<.K'^y+xv1_;mfbcMUFimx3 13I -i~A/񍻷k_7bKK*&shD9,JD+Jc(8D<S8HEJѫ!DUd`XB`Mlޥ~F9`Ʒei?=z<%y܋=¾|zܗV0xH̝WŴ]MyTS,P}ڧm! 6c?ctvKml@03BV}n:Nr1~bp}$9Y:}D\`QEU!DָQuZN|tBL$d(pZ Nѿq-WC, <Gb(]A_~'~O?tQ~7_⥃_ʋ0rµ6qE&EU!eky84@n=mz|$,E\Yʋ7y^/~\U믿~q]zԷoFlv3ЃyWǷONSߗEa _(Y,:o0WkYU}7JW-K7EQ$C {{{T[^-y71Ep1KV+AX lZkRZ.?ܭIDAT!˝ FbkfkFx<>)>_vh{nR O3߁]@ /]|~4q=a8Y\4MӀ Y0 :N:4R۳FE4L\V{޻q֭[DTU8 Yo.2/Y2\ɂuƘHh ""s` cskV<̕{{{_͢(}'}\z[,OGŋ'Z_wW߸wtGKLYC@']1_ƞce/Qu4 vC;T%JmtY?߳VGo}GnokVgܶKS78WMY:?8:MqB!"RDI%S0ê.nutao/7ZFS-w2vא௙TsU[H*ozM"x31Sd9 lWνf)ڍ@uWGu>CvCş Y:QQN&jqC BdəsZ֛lڄ͏yJ)4/c>s@2M!4 G uC(}a &yZ 4 @cJ-3׾*??я`>Ԛ!/sݻمeoG)%ˌ@SeQ}Gw߇mܴ{!ܝ[wƇ~g=mcp9QX`<uQ=[V/q[٠|ڞǾѝ?0W!'Q _*rBܻ;n~P8ڗ_;M qldިvQIGEHxE ށDf@W01`R:4Xcb}Qh0ޕj"X 4KS!$Vt9,WsSMtk8W(O'|&fk1_AvYԹ=dMQR"t*JQAj|qd|}B*d\Ǝꢍh2ZvL3_UL6oP'wmi#Te0+ `pVǣb|s]wo~`(" 0%&) d++,̻ko?4AϿ8i p߿b{;_xW\y>5KDBS#jA} [!" ,G*a#h ,~(uG[p*h-AZ{Dg/nzΞ>z0I'Z]F(1D rfʨ C)-HCߝk΍>;{ovv臎 b4«}dZz͟!^ĘܨKkKk*UX,Vn߻{|"9{|c΍c;mߵ}ٗo[7_~5=#"ܻt )h,JFpn+qDc0LG}ݿ?=[,L1~*tnx:+^4<[4\[,Te CJ O*x,ϏĹ+fWHDZ>| 26:v9*Yvnrg"#ORBTD1FpGd;4pҥJɭ̏Wbg'zn'wo7^l(.U؜ooeerRλ_k~􃿘5tEKX GDD8X:Ɗduh]~2/ek?AwvGNcCk2Vъ'  *;R "ְlyg`ށJBmk Xxlmɳ綧.c֭ErM 4YMgw޿sQk=8٫Sb3_|prd,7}Ng)Ky!dݴrJZSNUUv1O޹s(B0(Z2.$ap]]m iDDc0QBD uTde L,&);ܺ_3NW{{oz!he:p޽{UQ*N_d{uD 0HƲ |>9q7Ohc[)=>Nؔﭷi;"p>wW"£ bL'EQd7 u]۶)49U::: !u}zzWί~c:fz4M?W'3OctA`*8`Y \.}YeC7H>蠪ǿ/W1s5Wt*sB V;EpyOcbXp upp |Tn>CSm)lv{Z;.\~<88~3/彳aWJDڶw^NRpix/~P< BVfo?٭a|Y@5l5 :sv&!Yw3CntWB)G)뎒d$~~~Th~<<ɘD$#YWUh| 7dӜI>?=<}y2:xyIjE8Ib.G5B(V'm,X$9 ="Β]^UիꮥvhAbM QL$ӷdd>$C#Lf>8C$Ӏ@7n4zwlᮏ{ryﻯ8a;yN];9CX O g?sYM|W>v?7~}<}(&M-Z'Qrs+7zi^+_)qhDŽVY^d:]hI֧{k}-nß[z33R:vEIǸZ[xe UxpC)]7v%_X\## Y{W>{zRL8-8{_gϛܤGQ{L*.(9s;tͲ]YONi흢GD4{Og>y,#RxHZ3 *^TFH$b D0+V%be`Iyd(P1UU&RJlYZ0BgɪFɣǍ^y,wj Nh߿qxzPT&i~Gv#҈^$wS lmMw]YR6c+/.vYy6q>v1lАl(n Nyk?WO_w?dգ,w{M={vf~ou2.J;L?ÓQQ"Rq;8N=zSĽޟGyEjvZ+$qlg̐M72ɭ `vD2U7͛7>X;bR1 ۷zONC?0<ꍐÇ7,hY>>XT'b!aUU-KrU9F}&"iɭCDA,ֆt%;cLNjgXo%"RaO14l`sBC>;}vVijaM[ W0D%,TH&I:W$Fyu4rww~}u#:~\H5(VO:lGZg+MQbϟ+qD 9MWdJ;(1RIR 1l#WQ!X)8H{ETnĺ C381nRҵQ00|ìPW@ nal}ѫw|ڽvvQN 0IP\9gɷDq=hgYǮ2}b ~㕇yFQ1uX!a#ɘe!=7[7 JX{,S:,Vd9(?-2F+M}_;~8>xr2iMl3}NtJV-{3*Pd`\X˨4DFċD4x 1@^س(4KkAF{G Q;"2oDFk]ZeU\D!$$FD޹Ag 6;O> *?weY9+I?Mcerņi;ZVJJ{A:ifU!"u7 )R[":Q֭͆EaaGP,cN=/Olv3VYs'Nƍ&k3_TUVC4M<'VUU6Vf\ӏ#|f9.z~s>(( U$"Z pseD$" W{O }6?<<O='β6E:lɓ0$I$s)^PHfI NXkm陧Ӥ)E_ v]kSFZWUog瘨\Uϫ!Aþ'I$meeH0'KX=Ms CpBڜ!sUUJ0XC$I6H,ddsW@0˲l4lv̛o:hݺw׾k?}vNzkڲEQd+z@cs"nsWU^k EQ$9K]V k؋MMIMw" <iV&p7 xπ51 S}A;bXerH*2p&q96ڠnIĥ//܍&-}xߴ;OGHi=K;PpZMY1p%3!3 \Zdal+>>>Nt6h*ANڬ3w|4EReřWZP{&'3zP@jkCZ&nItn\Ε"ǷK@%b[eU2,}s--W5!f$)l|w?,=7¥uefYRZ0I[Q^Megc@ bre4jPЋBA+"JHtykAlV'@Upl%˜Ƕ,N3^V_~Wy`W^($Je;WEU\QTbEEQ^lR4FJh4@((WIp.xKJ6-j; Ք!rkeEVl!eYMd3v8cBL"Xd2O?/~QenU1Ƅ8Kc JPU8Yj>AD!/qE7nx"E:*]Ë \|i&Ib (cuֆGI^]dյ#|[nNMޡQ@qA{5L, qS`UXT$yA֞yCm}k"*y睻w6͠ڳ־;{{{ 9#+f(F!NN RtҜ~'6y:d@RL}>'mP+`뤆32irM?}~z =zV&QgOBbcRADbOD*2!BiRF(EAw tPt[5*b犪bf"-"FǦLOZiq\$689赜*!;PA@⃿LCVkBb,l*_\]rj'97(fۍ(ri޲,HUOO뭷~7~C+m4γǓr=~~tD,!wւKTYxAËR$[ RƘ(ۍzϟOCu^ַM|{$A$E/x8OR3g`yf.˲,ːz{/N'p+KMpeYBkuvUUYg;ID"[o}}33q&pRd2!e6A(g`eJL EQQw<|wʽWsGGG}ӧO?O>D)UUkyx4nw;V{UڰaQq^j>0vf,ahZЦhte2z̋jklw\]+Ax`B`@F" QJhPfv[D ʁq GeMҤ?=~ߗ gN/D H骪899=:9~(ڻa0JVe"IK{(t:`^Gv=f޽{f̂ (H)%qXc;+[`pLz״|s fS7{h,˽ M[3cL6;pqǣOJ(4(29m$ȲJ4M, pY@&nNVw.^)&.]Mͱ|U},YEjQ>j+)ATZ.I8^ܾq+c[-Mb6ni+&WRV>OѳgGE$.&EdtU4*gWeGDx9R  hI Jg*bZ@BM \'fμ9$gJMWUeԄ} jc&$,J!e BlT` )8?>!>QF(UPcfׇ{Xzu5>,*[~ @ UB Dieyf+Ei媒=jEJyDF@!ws^PHH1(B 7x/," B "B(TUGFA**g)oVrg"ϋꤴ8!VJǷW&C S@[xroN,čC27qs IV)ii#dRʱ\!5[H,q9[yoQMRǴEo_ PIuK_& 4\$vԧXf YqO9̆KD]UJF#H)UAI8W!bͭ?ǏG?G}ZTФqR9dRZ*;Q)c*DqΩuq]W;/l/ I *TY*~O @)DQZ30̙Aw* .b``)+RJ8S$n1CTla/ $I&ikf3Nx^&7q0"i''G7JE9MӼsmx-ˢ(~ԤBPV0<=jDzʗUH_#+#-ڴ[U1vC.:UMJ4Oy@DV8L =A"amEcs<1TCmQzW2??C7rƘGφ#zݻo?Gmf̲8Pu|<*UX1+ro5ZHBDL.X(&Pg =T Bq&&h_UnR6xL(_湫˿gG4F)DuB32(2{0;gLl޳WXzP!Zk?~gڿWܻutt0qn 5Lp@48D# pIlڦc78t^+GT>|||lHʧ}o,*hlY(gjܼ; *P(f^¢B脉8J(RV1I&ڴfQYHf$n6NOFyZ{FX'q'//$IXģg O܎D-Nbխ'?m_W7sϩ!R be8/4MZ"bEPeOi?2:&qbU8&aC$n6{]Vr:"Rv71;e`_lנX~Iuff fZ) '>L?}aE8n輽֨hLjX 'kobh&>"UyGlx\%L掙E@kh4$9=={7~7o}jQ$~mv`0C.mᐰ)y%DZ62xsTJ)n4N@[$+kiK,hq__ BZNٺy$N%0`#GGG_{oo'O* 4_#ES55P1gw& &$IRՌEQe''j9/}1Y{GݽC##{MDDc(zi~6|]A!įo>fu[=z㳳3ѱb-$#j(@\UUK"2ZRE"地cR(Ժ Yc %(+2aqLDșpAEiUQ:`;RlFU4:!N+@~`3,c5 *U[-_l"):_ϩҙ63D\6"R '{??ta'f.8ž}(I2I{ܹuC"Bo? =yyGi z/j-O_^SY8'EP9%h0*A`fO/,sp K,a9-hqj"ZZnAO''+(-lQiu~^EQt:q[FVH9rg9hC"q,"7oޜL&$h<oܿ>8+?p7Rg%J48BSeY*$Ic8ٳs.N$IJEa g4hafgf/RT$(H7Rnܸa$!x#4rl/_K}o hXo WZKUUUUjZi<c`f !y0Z-MV(_{w΃Wn6I ",˲>i5G(qƘGY0綯|b޹hl6R0*`ZR="5N牉'dU RJZ#T(`Q WEQDY=;n/W{tY8ͨQfq ,Bsp6<[W*B͌5fibgEoDIb2fDZF1ܸ4ZLD% )%9ӢbPQ^JJS4,wΫlȌgg(+lmKn4h'JE)tF*AT0CQc|E%uR#{f˜g%2$(h8No~{Fĕ D j.^U۪\VYsi(ѳgvڲHiE@@Eg.@9ǭ}[Fhι̹AFRnQdO~Fvҟ~o93+-Yo`x[Th;kȉ(Acvȷ9 :,"xRNoBEL4j°. ~!vfUY!,D$IR2^xprz G}n%qicA6ɢzx|v=FQ8dgX !Ҙ$i(Vfǹ>w{:ػ8;(6"2L 30"Vk "y WQCU*DPyP4Ӕ(p2S)D*I _=\9h9Gm'/m2\\ok{^wuw9*퇯^?g4Rxw72QTz nA cibZIϬݻu# ?>7 R{D xL3Ux' !NULA[說xm}bWj2ZV`AOi(qFE#DTxEk[bvk vyDrB L5A`e@,8D"  \YŨ1bUU5llxNgV>n786h2ws'U J8TLV X+@PdaDf/'m26U< ~Rqa T&QGdE #2C(QtEFicL4FhHhwH#a R!!/A8ƙñGmie[^__((,Ftl"=.$ Y&)J(+_ 㷅r;׊5:~iG-<x:N,ŵz7{_`8?&R1E$V 0$DRB=s;otnߝ0eG<842(mH(mT'g{:ߊR( R>v0>BE B,{P$ B`1t fk ii$wJ8+/Vhm]EJ gRx[Z64ӖLUdy6ѠR{ 9Pd@ q Y^νN]Kg̉$Dn%2"J+kmY[7ѓ'$VHZD4FEa!3U nNBqM B,h8($b(+\HvSoW߭$y`0y-23!X;טU:R~P"ncιF3qddB"*dE)T;|XUUd46{V"ZQ&DoES!;ܿ&(NLw-Wأ#^w?Gi:$J:2Q:;f++og0+˒MFb۽΍f?~::;zdoD5S- XX_cX8'ǭ΁1ڊ(ETx4B,˼*2-su bx()i/!"8 BC /"[?mXmv |zv|PIi6Ik&$CDsC*:'*IVx8l6FdR8o!; %(`4ycqƝZ9888x^݉aM %).++#!MUePu-*# I#8h-rY=, }e"EE f޽|+ܿWރ^qӣI^UXH "Ai) +>SiOMs'j L1;]@0?Pt ģ@fЊ3!٢Zx4ieAhI >댈+Ti,{Ar3T]Ԩek~5b'M-¢6J!E "Fw'eY`YYJBdw6q9u@ 켯@6%~O£fXFnpef[;Pr1It2s*nFJKHQ(PH7O~a?.Ŀηo락>ώι$ڝ;iO7y~n OHI$c<'wfe&@(J{&ei҆6ȝbR؃BfX5MM]^(ŻԔ]WVkVg 49+DDRVsU#N݃n/Mӈ? Hr޳gU,aq dTUU-C+"1?xTRdU>{v,{%ɿʭ;\`Y)8qzilI;m6ʟy}Bx\icï Zuv_>n&vY5@Ȳ,"EVUe:nD~8 3PCQvp5r(APù6Fp(:}3o9 @wwDι,IY"l6s$IYHRJ)2h4QFѪZCY٦뮾pOs+uv|#Xp(5Vq-JJoѭÛJE6HMxPq4ISB]ι`PƍF#MSTI8fP*FPJi8PBE@\EI4Z(JKƪ0PPo{?5cՀ,`9D s3ͼt?xp [Sm1Fd2kw^w^Ѐ}ч(NH:W@Hf`?{oL\ (J,JYxiQER1"& +fSmâ>nS cvu]wI|3BDSOD 19۾VH Apc ֪'yED :,`D MbaV)p V4ܹgnXC-XG{k$%췻7z{ѳ,EeLб>c'B&cT!Ph4dcw{7&:>>;=+ocBD;xJaj댵  ߣ(ZSð3 H ET Zo1nݹw<;./ilm7E JLUUeq6fEѨcE׉d2䈚UDDֻizZYcVb^Zf>f 9f'"ʜӯU35_.-8|s]"2.u{&m?5G#i-J ;{RFQVю~ᭌ2g<+nc@]]H"{$TzW4>nu?{'g{&?L4e&@Jl4Qb8"" NFc.K?gy!#Ũi ""EJ1s,dȤq8mkPBh7+ރD1`%l02L&ڈ=Pwh&"O:tPLvw>~eQuՇon=xf --{Q+F H)+.R:-MmoA0e^hc"OS,B.ED*V I(dl YHs@VJbJ\ݦPfRbP3XCWȝ^O $Syy7 *KB縰$/IhȖEpippvz9 ؀(wc$d6檲V D@J0`9;;N IO999f50׬1\Z_XՋ.Z2ADK%I Nz~{XM&'eggA!Ef#1PEHOЭsZ;ufaIya"j4mL+XX4S/Xd [F0"PrљD :m}H{^$y?ygƽ'qDZHz:ts?Ipn[VHi$ GD+nJt|(x8$: .EeV9c_&V;_x\|WNdLMO!ԣaۦ {x'FjhZf-+ՔvtJ$vs)i:unclU2@+N+֣Ѩ.vj%`쪥 ˫hkm3P0VBnuX'=;bA@)Fq5Ҟ҉ qqbZ3"ZyZ,WWipDMlj. C,Z)5/vjJ/o/]a/ZNZ^6qp킽kM RNVֹVhc񌀺; {cb^*_1R%Xq؝+Vָnq<a fl6$,z:$}fIDaeY!.ploY-vduyêAi NKK.mW&epwz]uWo v4 FvFaiEQUUyPmvn+gRd<kvI~Z;$XwN NZ@fCn$mr?ywtLQbfhTgY$$ޟnqkwDP )£"i;@H*Rqe1vqUtn#j^Ls &1vc/k,_ꉜdץ _3YWJyibXVI' "juY1IyoP 1%唪A#(AJQJӱ-؄y/BRe |^ޟ>(D$( 5{Iϖ78+LVVX_˦s4. X֎.ofeEQc$ ̫Eb̃*h`8., A)$I Y:%ڴle::'"snooF+?lTճK+t$ Ҕ  d;0B ك7ʲ|~|TRI454e*&qbb˩(Mϑc#rOT\rݔ6/v<^V R\T|/{Β Mr0$,^bG\guF3VMqd H_d%9JLB;_s6x*'@ZPIzmiơe`0W'xMQƘ |}PqZ[Es.$w=W%9(ƆĥK%CEdA!,!ز((Tl@9AEDp+HEqdL xHvn2ύFZUUJs8>)QYa0 ;KfvW_!Y.D297E]60X㯜[B;WxŒ5|Q y-.|գ `(dynZm#'CP" Q㜷%:Kh4BKC,NآgVUҶee4ԋ4$:<ıӧQBS\рizaf>9wvv6LDv J8d8a"bwZד:Ouppfy'?d2IUN:Gdp8Gei[w%`~d`Ffږ DDunݾuxgU{'B6@+**kRQP"NdBAH:%Y51.q~=t?kU70:k0\%meN3|7 |CM^Pxa r}so7tZjs'ҥWlN~<7 +)\I.T>Cr&eҿuEYR'k%Uhv6rx ƿI@[ilڬfIwoIPDZΪr2f+yYZ]l:+A^v^&RYHɍiVX!s[N$r[y:M,/ #K fV/u1mO+) 9z-0( d=Mʔ:B H )纋eEIWg}ٶPpC뒸ڃ5Ww7JY?mu^nY0ojz B5|!&fTYT& ;vgϲ"Q +VZA AEHe`gwz^Ԃ3R Ax\ŀN{^EQk=3WEs.e祭cLlŨn>ڥ_z'[z5WzǍ*X<˪o } ˆ6*ZK`w +K>ʩeϿeomEQ6Q)@lb9b{aTC@DOcaYКQm5 nWJQNQ1&PqyfΪI+$FD*a) R\7)Aw՘^ۿ^1^$-=5b]`da C񉴕'dZ mrE+ 7;0v.7%v14me.['L#|=iP9a& XoAh=D1AāmcPKU[r' :7B؊!<Byy6 iKFs1v]-ڑեNXhvan|:=٬3Zij# l,jkKmbDMw֍$gP5wˆHFH A^CrfF{Cz `fإUd!"D,;}+n18FbLs^B2mB\AP2uGNdIΪ֪u!A wJp..  Ռ}kj\yoYBUc/ZkiM\~N#AgGJuV*eD({cOHdQDtj!!-[c_pR*XkEi[PZkBQYD }W+,yX[KB^ #YRگ[Vqov4lj;bK/`z Hȡz_m=sF0&qtv…7 \a9/ɆFr>`;)sẈէlz"|!]V~{SԮmNv9 c~,}i6ZLͩsH13Z!t 04]E׋`;NjSKnR $:ŷMY{U.h :P CQZoVN]P_&"scQ=kyȈ̒D-?k]Eؖtkk{NեVϋUzÙ%P;]:? 5Kh%*5}ZB0p5t:-BojK؅ V1%gG"">2^[M暵k{!8-HHYԾ1+,þ ε{{5m{^_dUw7quґhq "NWumk ƌS+Yw_Z¡a]+.ksn\Qt퐹#k%hA%yE@½pKrR9 .ċ+.e$,Y{_Y`^Ąz)ie ˀB+egok1UdE`hYԂpOpEKM`[]IIݠ-t!O#yB/5=ED, dba}M FqiI{.LU ޴3n~תaaq(VkCFD 5 :YKm)Mݕ/خ0z핽%dWI@k./خ˓&(*`z5ȼy RRԦY?ۧv?g{K?Xl-^6kKMp0%vu9uvs-oKʕ':;; Kd7ㅛCnK%x7^#^]]rWU~ Ϲ5ۛk4}~ZG-7^c?Wvmu;_vHKTUjm g[(IIDAT骾.K6ot$[ߏ*EWeo:o(͕oНm9mu/̝Q=G8wdY"gu򲻢ca)ˎK!+l<k_Zz&B޵٨a/C}R廆>z"3]cu /H=*waZ6i]Bu%o4M:κ|_V8@Qڃb@m:/euv/ݺu8̵H&nZ9 ֢+ hr5OMŗleUnok9%4rtWnAr ks-0^{V^r[u裹oZlbTwXDzLٷf I<ڸʃl*䨵jZy{X7w; /֦le'.פ;y޷0>X[?pͱ@pY5P^bNjK毛>ܐk6y 4-X2U!0ח4+bfm-Y.&qb0]N'c`6o/WR_4]hK-Ӯ%vl_N{U$e<40}S~m~!qܶdˋfX-wlۜnͩus{X=׺]0  `=6߿D.`+ѿr8 y)ڶ ߱i4/.ϯ%Kۥ.0ޫAcSiKJ+XãKDsa $ڑ\>K=A\ॶ9?wuzq5y!"+yј3VT7~@؋E< ,EP,*6T~+q݅ Dى3Pdw[v/"W%Lpv+zL_/..*Me%|ymQ{n/M=׌~&ڴgd/V{U-&Cv>w .e_*\[sCHX2fu,Or5ۉ,zE}amXF%>r@^;MfϊH55)m["M~AwQx/.n-̮F]7*yk q`= ,z˛uv5̗ۮ.U xAWyIY2v-gzU/]z-8;DqBDsR̮n!" Cr.Y` Y@!?B8/-aqjdkzkւG|-ߙ~`t.TckRVLw~iSRF[pg1j6ڼ."5H~*m\לcšEػ9=Px$3u X"LD*T,V,M"<;5;9 qk89lQ)%Ӫ ʾ6p-9{ݴj>ق0<}v[斎X9,Tg[C@C NDVp9꫉s8`?d6)$ J)"*B]âI][V\"fk¦:s&3gsFute* sp-{W5>x:Kc/|Ӛ= %+yC5Z|:3JCDT"2}Bv/ ]'vj3vJ,T~4d[zb{q:#D.׈ "nCgq~;,_lu0@TuMg}ʹ>? thKĮF-],-zEw>4>!8,TDjo WWE/U٭ƺn>b0߄kb]MXf~D |Sn|a* :5NgR?m+.ynV{QXq@8;Zf'Cw|ab,mNsDmNͿ x-)")0 ub7E!1[w2RhIENDB`pyformex-0.8.6/pyformex/data/world.jpg0000644000211500021150000003437111655560676017676 0ustar benebene00000000000000JFIFHHC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"B!1AQq"2aRBb#3r$C%4Ss,!1A"Q2a#bq ?w'1Cz%(S <Ͻ%yօ;hIi+@8S6O3D@IL NhI}I$Ab(VGI?q԰HCV"% UhǓѿv2z|) JEI|$UrIQ3Ƶ[1 |އ[#R=Md+ː)<L Nڶͻ(Oyb> "'I۝[x8mty}6#O}M]4۶}Nen\#G7@ēѓ>)P=yHJʠN_JI[JZ_<vޡiJ[QYFrhC/eM"UZX m$ۊ8!O3?Z UÙ W=nMQ*G0lEf4 Oa|wMS#]*^(.$-%E$hv̏|ԭvOi G=+X1}^%wv.5 I#r0VXq3yCi2Q-l䪉K3?usKEi:RP ޯ;j#Lαqtœ)X*++J:wKYcea)В VuЕ($Ҷ0yizXi, \2flk'oREtOt||;Co&ݺ0,:ٰ;|9~$@vg[W& ƻ5y[tںe`*데ʞ}C$sֵ<AM,YZyڵ#mFYrBU+烄i4j(ZHRV Lj5ݖ>>Jʞ BeאbW_])lm10:?ҝ߳axTiis)C'%Juӕt䋔.!o!a$HJucvoĻ9aumr[Hגv Ɯ;8 )}!H( M7X˄MP0+gg HÚt,dZTn#(9P<{Xib.mݶ%:&-![:H'5-eJ/]kVc5P(TV)ط*[%-ye\,΀5%|%@YΊsxv0wzPȄ(ޯt@Vmr 4kU|%^ k(Nu>b!'{M_n5lFw2#]BK#!X:V\ppIU Z])T<Zj]:9*I c&M)IF&Ma6Ep9vBi+85(`v*qJ*Ӆ57(BPfVit׭Q6t]UXl ´uem) :UkP޲(R OIBlq;:޷qV4UT▢+2HMhWU"$3OaI;BJa-%J;4Z7 eSHW`f]h)J"Pto[=Ze_ZGk0vUʒuFJ'">j|[e)ǖP Qi_pܺىkS)1xb@!Ң|IBܢA5/9P'oZys$}+@$86E'q)$V$wrG=vŲ "gJ U0eNe?P&0V$k2<'mS6A`f[m峘0BRڢˬ2g5ܴ, ֺ02 01MN_7!SlGm:SKj+'D) m߻ÞYBI`宔.̭۷ ijRdHEJf{$*IQANJרv쳸MڐtS)$#Yܸʗ5@ozs%K+ 4r!; eݨ35븩q,!6H$ (j'k$]ǰ\R1gNggFmAEd]N Yw*r[oUdⷻ}?\kC`*+#_C?dݺ Ht8ra"e.Q*#n5mpW{ATi:fe-%J'Rw5ҽE^] }t"wsOaB$MtQ O#=hT>#w?>ԕ=hPQd?>ԕ~(;TGX$:iU!ai0N;=6 x;2TBKp} Z͆#dҔ9OQ1c I.,\CjRI"d5t!p0Z2[Q[j  xV^)(LnU$][0Zli B֔I!p'$մZ *VLr8bwiH>bE6[}eTK!\+BNtHߑr; {6`4)j)6=?E?>ԓǡVu;J}'4Iz͑st&āP:լ-˶Ubva g ꓽ%vt]qQ|)3H ? 5OeHRAcZ.b00T%.eJRI-έYrR}$׍-ډ_j<\lzliԢ@^~᫆q Re.6,G>ϳ;0-[BTJv㧤UxrBWTpbF2pνanpJ9ՒůhqPzYscG+lKE)n&lg_}k"--`Ⅎ5Eɉ|wJQP[,0=]$f֦2R Kv a]ڵ ="yk ݓ_8 zV.1MWo:g 3yO}ΞE*_h4͢9Vheu㉉)Qt;V.)*RP$qpS_f/-C\)%<79o]"SvOo=t3r{:6ŎF&+!sxD;^3mfQJ:߁iwl(pu l4&\۝!"n$(@:qPaG_UEeJBd5vyΛLDץKI9;|>TIH؞3P'1ȈO ڝvKq2ʿ7*7ڻO?{QIzmZؑNTR=RH|%o( xxs#<:P,ZݹcvYPZLAfr޹r=¬"Un IЍ8Wҥnܷ \ Q#eDs|T^3q=ŰjBUmZhGR T+l\PqRBOCMjivtm91&f8⼜xbE(Sc+M$2.׉X2Hu @LoU\]TSj@yR\Rԭ ^ ?= u2TG|B8X9\Ndh|i}ͣΔ- c>== ~$|?ZCetW$p"OBG= "|>$4( X0"N}/o[],ܼ& 9 -\a:,<=EҔP@)SNdp }8[8Tc1s^O:gN̜7 t[4eYIC/(,!0 qڮ[XfPx%>/~5i:6sy Vn7JOr`(7Pwg:qsN]hŨ꡾za<) Bߵxuj=U1uhIu>Ox6\ev%^\hH}+P$@΄v(P$@΄;'B|D5֭6 lL>uK򎦅RX?ܬh-n RN YYY<ȉVw=joTP>_6VLQkIK_DQh[6kZ$k?dM iFD0kN0PNDCzmy)tyBsiW6OgGN(zdr>_"Pl$`9 |5чW #]Ǎ O}'CB;AIN#P:^3Jm} *К!˔!Y`ׄE<7m҂HQ}O^VK)$y,J*=߷Nz/b1VÍ^sV 1\)RdWd[];E9}܅Vn-`YiZDIj[כ[ IyZbJn΋ƞuE H$'&4GS#Eqg_xavB=i-Wxz.\8}6't*EvL]z R屢,^IuGΪ%%J H%G`*wXS(HR r3]9>Y,cw$H0v'C"CV.iPJ%eFzUǡmǐ)O;Sij>E1iǝ6|í|'ޔJq}xU%yZW@83֒ xwڇ֒/J@jH:4(<&R@>uoVpF[KGPJ:U?/έaףMe/$%IRAQ2=ERwϡYa! BRRS Kt n+U`oo5=imhJE`ʠ ԨBS+Z[hB=4>ujN LJS$hd0RmY]ӂ+_j';S\_|{'2G*YQ˘Dꤟ8BODfw֛Ɗǭ`%6a*Q*9WXTGŸS7턝(+J%_J2N3X#0 [Jf6ȐUp2FuvoVN)3 #h?1<:Ŏ>yPW.5ܺ%m*?8ae ?z0P{eɋD ;zbq?u4:e;M~tKn栔p574"B T)BuI'\5 fR*(߯&ɛ)3OɯܵuVTit5izJR%ОDs5a %mlS>E9s!Dnx$AT^BH3Ԏå  SxPR>QР.yPj_B0[=ñK w^}bqR ( 2ָ=EzfvW NBÊ%F@HqDi>ukpQ{6>ƶ; ^d[jͧZT>~ʦh'/=f* @qڼI!|:i+ MCkR[uyTrWnq{kt3a)kq:`b^ 5&-)*QH $4+33 x$Z;( S*o*fxٕ>ujnY[#_wa8F$Gai68%ABd"Ey:DFʒ 34XʁDA- *2:V+CW2T,iUd;t,<li7yC!ISV1$ʌu$S6j$8T9ר,1(>ْIg/>H<\9zhO 3Djw<)Ou4Q~</΀P9j"2N6򞢀P9j! οOZدXB҅:̭?Jl{/a)m}s#_ϩ.fJ}cݸRSDֶXK)d6)hkuƈZBS @jT[BbkȞ,ݶt,W 0 PP Lhkwە[$FW*\O 2+mG~)_zrs&p|Gڒc0׏*KBCBpLVD&*drf:&=hS@|\yP})+zЩcʄi+zР@7„I[BqOT |_JG@8 ׇ*>/#Р@<\O >/#M 2R 2+QiB߲ͪA0e8)@W\ml++,!b 1=%F+׾53WjH ܉MzUwCmxԣ jkġl q\%=|T!D) <yeGDw |_JIЯj1QTHxP})'U䁘xROu@Ҋ|\ySxW UZjwCDyґ Q$V6-vd*3\ڜhWLy~[J mʼ7MʅJuQU\B/^<UQ[Yi7L7 oL/J2DÕ6tj@|_J$ >Q /*mz )q)GW&+V FzS\eJ›R|*J8KoQ WLk+FܷީTWBvӉ)ݬ+\qn;*W3RZ;hzʲ$ɺմ=l+%JhH>p/5Z`JL >U]Gp_v:X>өq !- ɏι#l2jы.$ ީ Ǖ4I$:E>aֽGlT~A@|_J; B:¥.QO%yZ`9CuHք~W@9C]„zޒ jG}(P#AvޑthPޗM oB=G/zР4pK7oq{kvFe-D8]#qkal9gRS^+ûI@{H F) ж`Y˫ e:TQCn$;oΆS)+zЫIz{J()&xl8}=%oPR`tC)BqIӝ R; NP42OqHGSBvSC)!BpIz5iOa,}pI@A=Eh`;tݵZ[^PRv`Z]P Qs]+؆+/_! iR((t\ \8%3FJ]ZxA[k BT6RU|Ы9 枼i蘉uGR${I(鷸sz*1h{*IzǥOqEI3aƛTA:P )JL%*ZP)G@$c]i.3ΦJNJuzOR`t]!)×xؕ!!Ej`\v/f ZEɣ )ri*6npI'mH0fNkLe>S]̥|-hmJ) Pfcs W-KN۵y0" Lm?*œG{9NSq}c\)d8ZT8ASg>{FlOr"q}2}=?Z->AtۊZ-րHC@RQӮՂBeZ9|ވIס^c) 0}ŷE΢*>;Ol_{ my'UߋQWyS(&~GuNrB3W  /qIA8% QH9M:N*'1Ӎʽg [bWb??d^9GeCǔK"^{;xt1[JL{Uv_0R?))=uy4kq\ ҭbϒ@lڽg e,X0tUIꟐi"aQr))LȊk쿵Nًfdۄc6{}VLHl|-H^fqזN`'ULZA%"= %G1]EU~|_Ap +R6HӀ`MlPf9]| Hc6ʨѼ=ihr cvĩKb84S.$RɅ)!ffH.yw`.2\™txglbi%ŀ$tUuLR5oYϩXy4gZ%8UTI'fGقs'h5i\6[fjQLHZuDX}ڙgJƷoIHġ\ĭ*䍡F]}vMC*1GGw[6#D&_|ډ wqԮNH5 ,?lC-6V#zV] HYVN}6ܫ:LY4}JҡHh}EpbE̷f*ʙ8FH-{AOXOh-b[@6 H]<-vp+U'E%9P5\Ycpw.g>)7=Ãw6N$IqLF{ p]7IA HRB@$Zf2YlBM*Uٯ?DKZEݹTVx{ˎ퉝IhTW GenA IZGxP>'&*ydJ*B`kS>̗N:RO,#] 88SS],RցJ{'b&oJK\IvՁn8ޕ*&!a &%ft_>AJJ(S(0~' 7ًfh# zTFWb}ZIRS*1%kT~=g'e+"iRy$؋臚B?5]pM{ T7ZlUh+k ;tҥP; gM8J8/4T+Z=H?礙T =ThZ?*̝_> #E?[HTM`3%HHtJG|) FE*U; ~4 oJ76~}鿃G[JڀpU}~TiT:*(yʲ4*T8aIesJ["6#pyformex-0.8.6/pyformex/data/hesperia-nieve.prop0000644000211500021150000000075111256152777021642 0ustar benebene000000000000005,5,5,6,6,6,6,6,6,6,4,4,4,4,4,4,4,5,6,6,6,6,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,3,6,5,3,6,6,5,3,6,6,5,5,3,6,6,5,3,5,5,3,6,5,3,6,6,5,3,6,6,5,5,3,5,6,5,5,3,6,6,5,3,6,6,5,3,4,3,4,3,3,3,3,3,1,1,3,1,1,2,1,1,1,2,2,1,2,2,2,3,3,1,3,1,1,1,1,1,2,1,1,2,2,2,1,1,2,2,2,1,2,2,2,1,2,2,2,1,2,1,2,3,3,3,1,1,3,2,1,1,3,2,2,1,1,1,2,2,2,1,3,1,3,1,1,3,2,1,1,1,2,2,2,1,1,2,2,2,1,1,2,2,2,1,2,2,2,1,2,1,2,1,5,3,5,3,5,6,3,5,6,6,3,5,5,6,6,3,5,6,6,5,3,5,3,5,6,3,5,6,6,3,5,5,6,6,3,5,5,6,5,3,5,6,6,3,5,6,6,3,4,3,4pyformex-0.8.6/pyformex/data/benedict_6.jpg0000644000211500021150000010735011303262163020523 0ustar benebene00000000000000JFIFExifII* z(2ihCanonCanon PowerShot A5202006:08:05 19:56:384<0220DXl t|  Y |v01002"*< 2006:08:05 19:56:382006:08:05 19:56:38 X X .n"*2j r>/  V $\@ \ !D:X\- 3fff666IMG:PowerShot A520 JPEGFirmware Version 1.00OH @ l#n"@R980100(    !#"! $)4,$'1'-=-167:::"*?D>8B3796    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOx! }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?1vɊRRpw:1exWv-K"d+sz*_S'/e-$Gl]f}.Nr>#ѕ_A:ִizB{Þ!M~ u061#(Zv.4wȠ$P:rk'cW2謗L$)'{kMsmf!QE(k8>%Σ~9WoiiW}$݈$(<1ˍKN7hW̔2%=v'Ȇ'Cg5\W:: 5$MֆF@C*kiD.G܌r3q[DNT~f9#چ:.WqGZϤ[^[DFѿMԍ<Κ5c%kK,pҳrX9OkLhz# ΡEF*+I˫.!"& 9cbniZ>=탫*O[ gJYq>5z3Y|c}i;,J2NNW6 )9Syl=>Zk}&mJQiuh*qw$u=|/h"#QM bNFO ;fN: 8Z?@,pb(PqڤddqZ ^kY8f2ϐ] I哠XUVᤜS}4"Xԅ UzU]]b\2ttLRN6}̋/<8`O r}yo^)]\,& IlP0~\I\P8ɮ[<;m/E{8ykqp ?j}gQKybH㿀d݌wVz{]yc|X#jv!O$l6aGq?3DvL!T_5Q^I'Ü Ԓr'$1ԖevKlg#>ľ%;F2? {~u$kyч=*bWp\鲋Wg6Y=zWs*!aO~&ED,Tub#yZ̼[ƪecJMpMkD TW2o_Ng&tF:{h^؇w4;O)qige: T35kck%dR8?zsg4x۹%^ki/ p%|& {Wk_Igg==ܰ ]2wjɻŸQk<]W].B\!KN S(vR*ҏ:FzSVk^cI LI|czG1U$d&E6>-%ݱ'ho|?)g>W( sd4|#-1&[<(╏*M/56qjr 9D>k[;w:h)`vϯ @J* Y(Hv*ui`VL665Z\xA*FH['4y0N.)8`)˿jsAz&|yϖǸy|-r4I9#,O(Iy_OBmv #kcZ8$1y1Gy"u`!1ts9?;=5 1B;Gm FI֑E\l$ysK[W}7a=zZK/Yą8ng:%d&EQ5crɔ:S-~JW7-c㊞-\ILq'{[Etq)wE8EtoZl27 LRBGh\LH*qv)Û_21Žvm*rW YTzi'Mm3צFV1 FVC(Zu.D;@٥8n[r  .ܰl2C"H໳D@E H~$Oi_122 !*N{cSc|A0hT q ؞ޱ-;\ތn߿MZZ0L 1_ךdǘ嗠=ǵgFZb#:[Twcں=72]<(p=Gz[is[B% n; Wരxz˭s~ǛU6/5/rh{ v qyS\M+aqN-$rv^hm,4=R8+T-uP-{oz:U ՗ +}ʼncc첱e=AY$0 dqYO*sLzn3M5; c?fwE=ƭ@(# Ϸz]2/}+0"Muȗ8\ǩlٯnRPЏ-=I=U/f؝ؓ%zխg$NK%ZAii2FzVt(3үZjP^~э*2d5'I@8_æ4qJfeE~ ~]z{rս gEӟ%Ԟ>/{!YE ;)V`M F>2_SWvndK ehoH?j/ҵ"aF6v"0jkst⼤hqv4#1b{֏Ѥ: &C/Vֲ!'\|6t5--Η2E?SG5DQ$f@7;o)`gLv,iI&jUH#"Qo$ʶLJ9?;$יWrOXпI#xZ<=nՌ[^IчoJ-mm,}͌t'یz8iͽ jȎ[bFQ]<VKAgn0@1SS2[KiYQWP, IIqAOƼOq%\tne/{wr )#Tܳ6Kd\jI[~FvLkk:gf8HF2X` ?xazҨo >糓%Kt>N '88{?BC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222!A!1"AQa2q#B3$Rrb%4s6CS$!1A"2Qaq ?ҜP b2ҴefT$H\C]@E.A'.H;-9w )+[H 4( ˀ(+H.@wœ G.(48ϗZ-TrWP=ໃer \78 ҭĕ_ H!NS1ki.e4h!q@ӰI@d}W ;A( ]@p @}GZ^X?]N;_Vt ZUTR@Wқ?֚.-RO=?\+!B]w\Bb:wH \O%rv!c*R=Fiʓh4R~vi47‹ڞ+$q\ 5t!I4|+Jk+^B r㌦TYšAX$)AA@uZXpRĀJB EY2 *k({q @ݔܩ2B0TyuaN?XsxX)g Rк Xq%M+ Wv!- ,|+BU<‹=yRuaE iew%r xF?i]ReM*)ȍ8'zvʑ)$wG|쥵i)& DmV (ҊAٵĨA!O!yvui_ TsD d ;heq= R08Aը|| z.œF4e Tiu.A_ehYBa;pjlQC"YҨ[vFI N^@+W@nk+c$)U0"ɍ*Jm&evHo)(9Tw\6T*#r( xrXxTY° B4 P T@^8˸ oaURWŠ dA8)m_%pTtO(xΈrpµ(.fo-R)ApӉJu;# "v“b%kINQ}H #m4CfF*8]]*dS#g`f!c<:?|T# v"Td2wFh9 =Xx]W \gkMV KM녎v'ǵc 7[tG]>=b~RnC[ q4r,Am](T9 Me4!J ,$U(- NBw<+bUu6( #G IvWlW𥤚($k1lXA UFYRՅqgj)W3D-U%Io 4hSN Vܐ^Tn X#%?cW(JI]tpv8U%n-@٤V=quXADDG;46,*-]w| HVJ쒃qhqY$Un%F a )aHH@Pu^${AYr͖x |~V~~!^>uJh~ecri4G \Ue9T)©)M;PovZm/&W+ 4Q2D)tgPM5Bh<]a4TٴRA󐈭(M"GqU$9d\ SkzD+\sQn(\X; ֚ʾ`&zF(-#T4֍Kepf8A*E+T:6c(5T@F`&i\ .YJ=ڼk i C 9sȣP-+bHQy.L+,^ s[FU5fpPM9[UβpԺz)x94\KCi8ir /yiC,e,_&6һNxYzPO ),6 .i=.Xon24\nCmHfT%QM%% @/Bvp-xPYyJ9HJJx')4Ii㼭NHϔQ%P<i7T-&Nld ]=cmi c3@9ꥍp6UfƋCUܦB,T,^)HReŖL}G.|r.!T8G 2@T#eԥ&DzC:+,8cФwWe-1\+T#ZH#PF+7 .0~[?FUW 8tBAW:1YU=BTPZ7[|'TA A-<=#H K(Mg R.Zb8Fx:$R.4U#CLW0\,6-)&P,D:} Cs(/ƾd[Y:cItH]%A N3 Ji 2;1Ii.<')ޕ7ZLfB(ƺW.x{]-n8WvKjWeE$^V0 \wSM#a`)7p +|aYPGPNAPa9 즔IY`%e _p 9c3 ZG>^ܒ@<DsG]C2#I]pVPLѳݙ<WK$`$E\EX.WE)8+90%7!*;h8z7V2s92Ker2"7S(b m X_v6~0̮F,|pgj> Ζ2: w H{LDW`4f +*e š.ޓqe+UF0w-%T OA#Z * Z AcA=AU(/QX٨뿅BrQUs* G o-5V` k[+ -\,[EEcOQn$kOJeqhxO4e7bDž 7}/)SNg0x,˓[HrgzdLyPw8s}-LHE&4%4ZsKV ,x$ML16#vQIUvj eUmpҵQ;7It43hwd-, vo+ r49*e F VelsTnc:= J94sI,vjt O@9:0\1HY66{%an"a80yU9.vqT*S;%Iu"Gl੤CM:ڹ8D*1UZQKDJܦJ*;-)ZVl:iRQn r4VnM!TVHe491aiBmnQ]WurC1R:c\<_@oLAU>\&pޚ/2 EW =,r$o*Y\r-ξB%Hz[{PƗ<?ud(vJ;A5ՌslM g!iOOMhجqaAۨpi\;T$9ʢRKr 7Y:0v;7^)Aiv-sMՖ   4FqS; !^<ʤqh=Ѣ ѻmYQ!V{U(* U6ڢYux̔!cKRXwB#9 60Pi ZDŽɴSwQ=wZ+M\Cpc+F;!R#هOV%ou#-$cE-h= 0-NM02#mXF "U:9 'I5W[5r)V2l`"CsCBٲXW8lp{o(:kȿvviNSOwSCJ9'U,6Fێ Eꎈ5!ZkQbLm8)kZ|Th7})w:wbT u*Ъ$ܩ8LJWUZcXx>AįϾeU#ؤXT"uYQW<ȝk?@w}#بV]M8r@`VWj68rƒ׀I(vL^&#i.L !{]-X8H7eTXH [G pT0hD 78:pʻplB[o>/U|Uw[p{*QΡw(6M/2XRE Wl6R i A<q F׺ XZcXFR[{1KT8lcu2'vZ8t{rҺ{oJL|*]!j)VFM=/qrqHnY7n]>8? V- I^ָeu8ǐ wi,s"v"V:16ZGDz G=Oq;op!Bc a.L0ȉpe8Vr+[e d8*  $%My!hegP*q% {)+N m$ UmQ\S7F-'jW)"i&2c'ۺ0| a+U] sXI¸K'.8+LXf_P:ÁOϨr 1u5K8TďRKkX/甑'pmzkF1˿S >2iz :]evHkWNYfٚ $,ٜӇh}zOԳ{O$q㎕' Gi%Ŭ <)B Xщ:A sK=m4UԹ8Dd3L[9h0$^?ͷR_ ]ѲI}F6D[/tT8de@g I,[mkomT(.-K#7?5(hSZi_$ &]HZ$dR{kZMڗ^ޒ-+K~dmA0D[d{wi߸eq>Iscd1 9>1 $3tM4 %+r4`Q)xX.7[Rv]cmy wKVvR,sIrcO`㉟\fY F?L.[ OE;f?C|(ݧVD*VA -TouͺKK g/c]%cgKmC wJP߸^17Dm[ "ӎ+j@ih)\F=?-GTR oU:,Q>i+&ϐcE_ " J/ і AH˜rFPa-;-qa+;\~-Uͷn3q/{Ckҳqr֏;MQދ6${bNƠWϝٌ)1;ړ)zK,v)R}*ۛc9t-H3d့%'NCǦcGͨ+n.Gqƺp?5$eV8Z9>SCz},$03_Mߓ]%+dUZQ:1BHHsOTޗ?|.Ѽ34GJž _zS=si)%ZEi^.lqM᧰D֒M++r8ޞncCQZMrgH^tqU<#נ N<~T lfmw*,q) s0R'4v'S7@邰]v L(O,stdښ@9XVz NnUdE49+myPPdrwYNhQvP\_w@Rr8)cY簋kh \bcI-khv66O}`d^ϩ>;Q#)-CX>*jOnP,2t9pne3nhD05B -+얟T8Y)EPtPV}tuw'HޕoX~l(8$eplA|R5?soM[+d`k*Fm^yon 0BnJv=3eiiQ+?:ZԻ$>R˴~)P hVYAHWdN]-d<4w[дh1{N[o+lY\E%^e [qM~萱5#ސ|4N?.o[q徜ֲsI,MhcE07K]>k贓~KC#5R8>W.rJsF27pg:} BQgasnבVЃ'8Claia @hn׊q+W=,)6jCI:Dmfz: RR3hFlU5wH7<͛RX9 "Qm.'%Z}SA!Nδ:mtb̄P\gӛo'OnHTkLB_N?-N3aze&ډIkOŦp W}=>Kl/nnhtI~33G26ftMu|{?Hã~Jj"@>[t|v̏Q-l4=1\9rϽ:qP6 j.Ox/k iVGwSv趯S0V˱ [DR.NIJM;ŵ;hHx^s=.M4a,vIZW>~Hcs ]#2|rס03OB+u͕waаu4r]6XTpŪ)iIUDN([*7$Pjv9cɗ%6@ۉWӂ_vډ>IsxJ#uc 4$/Ni9xn;(3 6csO:vBIP/ D3YM]24[{NOr{яZe4#nqWgGc^3B#F'nw-킷iO W#zO mEN^՜B`q[K:gDae<-!Zpc\;8FY-(Mmš6I(`KLU)i iwpZE6N3Mm@< OlEuk_[74 "Ie߅Xd-#$gh`ÅҬ9N薠X.a3ĭ3?i2֩斖Y!p`s>10Ddkuw*F]9ޘ*%?e{DiSn= VUH1 qGy$'4)gdL}e]0 4G3}7]+"e?Wss^HQ_۵P:-5/o0 \8ZPAV.Gl;Qޣ{^RV>֌R>5%3֜9act1p wAQܧg (݇ 8CKXVJH %o} 6fi.}LVVʙe=$슙7T{KsMIJc9J Y?QJ,*3fJǦ*ӹͩ =yJIN)Q}V=~ |8HQzƞG4@֞``zېQ#I.L2wdN f}<*ߏ i { kC?ID+ ]+:rjvcCij@p A6~'I!@^h I{tJ8g[w;qof纨-%ɉ0g&7+Iww*03-&0m 'f>ű;Aw ʹJ kh0pG;X9䒦ܤ\~즴Eh+AF!hv+/msMZQoj9F)1EQ)J7ʣ\QpY nH}#u#O?Ci쟇XXj [uq$:iq=i]a i>>vO5~OWm#\Y<'dŴhNM1sHͬDRD~ Y&xD?U`K^8+J~cߤIPoeTNjM"\dXRh *<2pvlp;k{Y16\ ŞLXhg[^ymNhh$}+ ]xΘZD7eFim%o1$l]59G@  8rc@)֌.\=S1dRNudiuA݌15pՓ-1Iѻ{9}64i){𢷆6XQWc`pa{e)J(+nTcU^8EE|`QRCc!ynWQ7DUpeUm+@kǧ.rl4T222N `SUG/{0Q*܄'EaVa6P dXH)$]8KKI$,[De2K@95=`V>V. Wj .hdNV:O2waM ~/\WjuQ]5#MWdB`uo=FHSOA7zMMX'oc-TZH.YﶖtG{fsH!=|~Ya"_p9i *^ppfa F9E Qv!8q*MvD~O+/YU|cc]Q6WONQHϕ FksLQRYNQ=L 4V-`M%Ӹ+_*\AW A@)F 4;-0vۤ0FQD e*l' %{xϤIJ4Uw[N{ʜ\ 7'yrPOK|D] _)g!߇7b*^tP{PZiSHsha}:QŨkE}xi坽/Ҟٳ6V:ٹI+2F[%:kipcoLl_q ;[D=d$c`D65h ,)nu(T*Cԁ~ fΤ%nx S)$a_;ii$V% Q).,fj-^USlcw<djL{>Iki~O`ʍ1ǂ8@K}#5(";i,XS(KIM!93mIj@qT]#zTcZ p&3`j" kK='1ꓧ o:ۋݑ+o zB164XH]4oSogOe:u.zMbx*@=#è }.@GSr~Ŏo2cQkKΘV:13"xx8hz D#5~ɫk'JJW9( {}lpO&qf9fֽ]\; Oi_RmkǨ efZ7@)95ID˼)j٧!I)0+/ v݊Lk Bƴ;nP i%x) T~Jh81ŴH,{^RINm+J(,nD9;{H7gԷkqtcڶ Xq~S#NM^uU-k0?+bfQ@$Bly],dGdtmGtwYS}.쐝qkNpSq_ }KuDꤣ.K4._&Q5焻#I6<Ǩ|9 40&kiѦ`h  4QEj%FRJmJBג/#a(p8gq룆hsrC+:xz)viM%jѻ$дK=<ZaEuLNF=AL"3Dǩ 2ё Te$gr"qRΔa#&(|:}6 Z FhoiN>.KU*hOpnӹET Aai>x^5Ϟ/˸7/K;x/ǻV% o9'[f'6RjF5ޖ1iZc)ZY?MN›W A(YB6#yvm-.N)xBg%#Kӊ8д pцH% 绿ʨR$&´m3eic% i1nvF%l?¦r6JFx\Q]x3Ań`uUUME (qi\g% 軆ΠhGqVgn]-#h"iŇ=$ *ul!,k(~[MnJ?ʿ,cPޟu$7uHˑ|/=V4rǺ\cfvڢWV>^]6$k4Vц.ʛuRWu?N84(ӷnEgZ7n$BCZݚسk^Nz/!koO7z,nTNx-#jto4еa'TCLƑrtھTX7ʱۜ)3hp\걊؎yK:kL9Dm9Uqq"VkEe9nm86Z1&tVX*Y Ml؛畛[]MkCj*Oh{G+씴{3/Ya!RomT7]aBiGq##gSI]ĝATD*hR?EԿ!7B&K7q`e_e.ϏLS'YVA2~°v(s v ǐ:8`?R7압<l|Z FM=dV_}t:x}B»k7?>V =q͛匞ZMF>0}rrO0T\6W*kIVi&vD8Zw_d;@ZIշ (d;)S-BAvi](ЊA&q\*8GK]Nicg%t `縐Z@B|@Ux TU\A6Aݒ~SrIh񃔬V4ǵ+\ ˏgk:ZEľv"c ;9TZg[^h{lr}/ϓe5)w˴rn^{=j-#v5ۤq Üu{o7P .Ei2w-i"³V$fjE+Z0@{~>3Re,Lߕ=/VƱƾR:?-dޅE;6W5z.{`{n'[ȯ͜Mm'44Hhh.ͤKxWw@eoKqk4'ma_okuO<f?'^Q8{V_~:ϛP#$ݔǫY]8VFѼ^4IB " $-^c gwDgfXl]Cj04/Q"d?n28)$鍻mBҺqIfߕP-H#Pluv4 uQ{0lGvĠ;.$ZHd(TEVs-o*v3XX [_v@+{ml|KOc!g}[m8X#P0A:J-ŸD Ӑv.D5(64 5H u[#Dz>PZ- 5ͦL{դxc#%;J99 y;m1;Y VΡHLX!isŠcET2 @򶰌7r({,'MU*JZ@^ ߂ _LvV-3++\oM!W)IyNf?mA%>CUCY )zQjA|iQ5cyx95)m8V4P9Twbp YvwZp|ZIo:6? }G|Dod_2Q)4s[ o_H&y{Oj: Y\㻶ƗI- ԍ,sCO\'CZRpl9Fs} Wr OX)_ 쮥HגE7eyvqN˚rIfֻ$,VFivehteF]Uqp2H8,; e]9cx8Y-9@xVsKԚϴF]T=ĒP%4윯vvu\#Njո9ξIYJǟuSgi^3稟o$/#c2?hTwpڈEGq䫹XBri52akVyFyolG}'V<-)d/#NaBFHewNF\;帺'粊aaumq`+9P`iEJHOmqstwpK(mWitxhQitӵvp^DHַ)7 {Z!>mgneE{ (hB$Ȳ6~ejI.ͪLO$CJPj^nAݐnQCGt+PO2YZfB@"xD6fVuL~T='ז("'BGӄF@fScvUª4Zcڰ*smzns2vӾ腥mApIR(r7F;rVtW4 0sJZݨ 7+(M 8A{"B L&fZ?ҒqŖ-];yjM"zMy7ZBJt =mru-hgv¨C-f9+QWtHW'5W1Ր8ʛtrPD7[N-Mz K*E/+۟Q~8M%hzR2KAWoúg@Awm2>}ך.|m% Ve2jhjAʌG `-Zd/m 0ZHUi;F( w(^Bh(EWDHQ8b;ҸCf0vLl䎎 {I=H[J\:XN9U=UH=#%}lU.9GH3( 'iɾY#Isq8 1sMv?x1>l.@9)3ڭEMJb{6p뺆3hQE pb0v(bD߄9V@-,"Z@+zcgUBtZmB-a˸/+bAͿz:F L0'4eA\Ed  f4\oE*Ja=T%t< h&]nR0ma`ZAjp+sѵ$e:_M[E)zقSv2~mid%t.ߕ%)g("SiT=Zʍ/hmDY"; %.cKx W3kzBY T khTsIv[ @KNniG& uҋU"QVGئ)z4dv9t;©U3NY Vc[`+8ٲSxjv9f+_>-/ veKC,x$;KjARꢾK@]r2UڐvqB:ZBiآ74F6gU5.OHIO>Zi zVGutl7 %vD흚Y{"OjrC a4c]$po(ѷp³4}voMy%jGuʛW-7\2chh˾HOe=?D."9DͺABZ/mG2ٞPsá{Ȭ5Mo'!'X4y9FE+c8 nVǵu`KM1Av9DV]τ3*\l`H\+(Ċ#GeOMW Y@h&I%ew sHϷU[+{؇d̺ZN֞@JvpJ#e~@Hw+7Vpzh[ 3;N-LݑNq$qm1ۈS1K(UvYZ/=`Qfk4L46!kǘW1\Z۟ kT.[\ؽIыU>7= eKM@-MK>X0[qʾH^n;%)58I,p(tkLz&x zkr۰“R08%&J\8ʼr6zap7mmg:ra pqm*x#40ASr"G9ҫU_J&\y5Rg`W hxA4;vNBvVP^fΞ`,ݬI,Tbϔf0nyF4ʌenl9rV^""{lWQCe!RXv `ǤOV˗! P bL"9 ItwU*Kw>KM|t-C"/-Ѩx뤭V8OiVW&Gaд'L0 JmihUc[']SPܵJ4e7}`~=T- ݮ E_aFC#oh[$#!1ER75AM!PRP@~JZTVPvw[nnh(VȻV yR=+%Q#T/fZ(@opZ0ZTqpʠBET/.]v䤧K*f3+m(*{zw?G :n=۲QtNT6-'|'c46+Ȳxn>-7EܭX4 mSV75t}p0"cB2 I\wez(wC%d;)CN<5T`vH7^'ԩ9Lh E]Ռ輭;p#Sé4nw`X@Jwna!9JYNvWص̑PՃX7}Rؔ7 EjqFn^iΡm <+OR]p p 'eγ7&)f˫[ %U_tI)4$iC"n^Rpm7 Q ihA%g-Ԃ鼵FbLlS&[\vޓ'y 'Jz旈^}SM~Rj,tJ w/#KM-xL*{goTR#YU\$<'E1wJv_L6Bx$ 'onS.noAXyJDZ05IYdZ SQlf,#;֟p8왳p,In,O$P]P$=#1"0ll o%="Ge %`#ENAmgjQPuC?B}#S/:jiEm7 `m}Q5ȢP㶬rӄ4qW'BɦQKR2B|e8QӴZE!(A Kj9ttPyWѓ)Z!K x.>QxO:9Px_%4Z:kX$h4W J.4# !4JD x@-\TeUaTm0 ՍS- 7ip|)sh *y ڐIDҕ[D~Hi&$Z[` J6#at􌪘uEc )עA\|&֐3 1Y30ƎQ͹@iU%"^lh (p9s˜ʇS=oՒ#񒒢 A!- ̜p]AEY;9XԕVv~%L/R\kX |ϔzfm0MN2M}zG?^J^mkIkGM$Vu58EL>SMa%2A"Nq`R _Uczi6# H@fxV pJS0Ba-`x|aQ WkEN&0 H쩠K3=HZ CvNPm +!5MS7$:VGdZ[FUP- LL'!dOwʧTGdash R-yLW#%@h DCL}z`d)u[8Fz(@ yrsd?+GF#)(ƺ=r=,GKhj᷐qa#! .q@+ckwi%VV\)-'8,q !O]E"|p3wYwZ%<Chmd(m^6Y%k{)T%"24J\hR &_Scߺ4mhUIDc)men ΍9<`QC. *fB(<$ QA# YDaQAO-~B rxH@aHD^_U/mtnzoj9aSA\cY:ZF+$O;L,iчXFvV]k[9)rmk%k`t7{SA.6 @]$"'(5h:@Z7tL5J~B2f e48I/9KU2*G{ɤr5Q^9\P e(iV r(G`}R &`# «ai|Tzj[@%;!! h`Dc.L2*(0NݢiX%Y⮑Qw>kI&4eWpU$H" :wSOMd%S:=aH*+^*Ku* v!hfɘWd((ڃ%e & xNP> =RwNN0F[hի2O5H4BF FSº ڰ6rVkCF.6;AU@sl nF#oTn48`X7< #.807hst0t<,Z6@Sd@kRJ"N[A1onpZ]^B@VJ0o IRъ=\%u#$֜;Q ң1Rr2p}߄ٵА@Z/觧s z7▓ƒh6 ч ȹ:# Ķj+Jvq!EZk O Z!70H(kNC؉(*0JT ABiyF[3yRZF#pX]=%})pPxw %8D" ;<4~)XfOUU'A!.NaYq&"%0/XXy7 &fRx 8RՈXSbzNf _*ry 1˞1UluAřls ˱k \ =Jm/+a(ksh4h/r _8h+Bbl@$wB)m85/ycʚ-_Qobi">р?p+Q4*+V4wHⓔb}x&)UhPGkqyQ F|%ȭG^j{*R{%u}T݌ )&r Ҁn*t4p4l*YI(Kʥˡ#~ڴȧr7H+6&o&kNtU!pmaPurrQ(4DۄʤCdkCEi精%-\GkB;ۈI  e]}` # Y<}Ujgøp1XO4RN)UוY9njϝ!rvHHVdZ8DLwS@ +e@6 93 XTvM*^)"WrH*"XCqQG*C@+Zf_ni? f2QZtzJFM!#5X5 mŤVDq.ҩj0ŚH^VlR06_ۏ?(=ObxKG夼 BY& jpyformex-0.8.6/pyformex/data/materials.db0000644000211500021150000000250511705104656020313 0ustar benebene00000000000000# $Id: materials.db 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # # Sample materials database # Units are N and mm material name=steel young_modulus=210000 shear_modulus=85000 density = 7.85e-9 poisson_ratio = 0.3 endmaterial pyformex-0.8.6/pyformex/data/horse.off0000644000211500021150000011511611321654712017637 0ustar benebene00000000000000OFF 669 1334 0 -0.00552152 -0.0164589 -0.0201353 -0.00971124 -0.0125951 -0.0233362 -0.015117 -0.0188091 -0.0260683 -0.0871416 0.0463454 0.0139984 -0.0838715 0.0507907 0.00711988 -0.0870705 0.0527327 0.0148492 -0.0568155 0.068009 -0.00663143 -0.0637907 0.0637098 -0.00896132 -0.0656885 0.0705391 -0.000896229 -0.035645 -0.076384 0.00708498 -0.0426603 -0.0763025 0.00917288 -0.0377826 -0.0715468 0.0081003 -0.0212302 -0.012334 0.00449436 -0.0270781 -0.0214741 0.00396086 -0.0228502 -0.0165407 0.00465867 0.0917204 -0.0596322 0.00453672 0.0855906 -0.0568575 0.00729697 0.0852778 -0.0646592 0.00590485 0.060462 -0.00718369 0.00888196 0.0543399 -0.0060324 0.0082924 0.0584447 -0.0131367 0.00669921 0.059268 -0.00785804 -0.0265315 0.0600458 -0.00507801 -0.024914 0.0570098 -0.0103748 -0.0280888 -0.0174274 -0.0494893 -0.0261709 -0.0187529 -0.0492972 -0.0287065 -0.0166947 -0.0649744 -0.0305309 0.0385896 0.000291875 0.00444744 0.0495941 -0.0050551 0.00281283 0.0458973 -0.000604765 0.0111892 -0.0803267 0.0397947 0.0164728 -0.0846682 0.0387631 0.0249171 0.0226896 0.0281979 -0.0306504 0.0339799 0.0333764 -0.0331196 0.0211554 0.0312037 -0.0245886 -0.0229821 -0.0271414 -3.11254e-06 -0.0230524 -0.0281649 0.00343657 -0.0215144 -0.0195798 0.00190127 -0.028723 -0.0406479 0.00720231 -0.0329865 -0.0393763 0.005084 -0.0304189 -0.0502045 0.00526973 -0.0454866 0.0472589 0.000885554 -0.0542124 0.0460482 0.00364887 -0.0400195 0.0354117 -0.00117508 0.00936002 -0.00345246 -0.0368482 0.00573588 -0.00811646 -0.0328484 0.0144752 -0.00708488 -0.0344923 0.0348762 0.0356907 -0.0298049 0.0438261 0.037509 -0.0233849 0.0356195 0.0371011 -0.0225757 -0.0398654 -0.00837041 -0.0082457 -0.0418214 -0.00742404 -0.0206348 -0.0434817 0.000976005 -0.00737236 -0.0245153 0.0113812 -0.0345406 -0.0262746 0.00359827 -0.0357828 -0.0196294 -0.00128008 -0.0339008 -0.0038771 -0.00124161 0.00781158 -0.0122827 -0.00724741 -2.84041e-05 -0.0127004 0.0010893 0.00499837 0.0689211 0.0049433 -0.0121465 0.0656466 0.0032964 -0.0110076 0.0687282 0.0103019 -0.0152067 0.0688898 0.0143099 -0.0347476 0.0710226 0.0116372 -0.0303474 0.0694161 0.0198294 -0.0330271 -0.0325671 -0.0150364 -0.00584217 -0.036308 -0.0172073 0.00185445 -0.0368985 -0.0142811 0.00302318 -0.0825153 0.0416291 0.00937629 -0.0403283 0.0349769 -0.0214139 -0.0381807 0.0407793 -0.0199735 -0.0509763 0.0460034 -0.0177591 -0.00962804 0.0256944 1.13362e-05 -0.0181487 0.0191756 0.00725237 -0.0180823 0.0280928 0.0031232 0.0100418 0.0276992 -0.0274829 0.0602697 -0.00843366 -0.0316083 0.0675278 0.00351993 -0.0305571 -0.0321283 0.0364747 -0.0215411 -0.0306761 -0.0296483 0.00481164 -0.0257243 -0.0339681 0.00464337 0.00083865 0.00723584 0.0104547 0.00531597 0.00230241 0.0111258 -0.0333822 -0.0065245 -0.0326015 -0.0242858 -0.0106024 -0.0329432 -0.0277499 -0.0136684 -0.0326979 -0.0144917 -0.00543817 -0.0263481 -0.00354366 -0.0125812 -0.024519 -0.0137986 -0.00182434 -0.0287737 -0.0680032 0.0388116 0.0118984 -0.0643575 0.0411904 0.0170047 -0.0709582 0.0410793 0.023696 -0.0394592 0.0211676 -0.0313589 -0.0276381 0.0215675 -0.0321951 -0.0391113 0.0111347 -0.0334664 -0.0804472 0.0510345 0.0322105 -0.0877385 0.0418181 0.0414661 -0.0824305 0.0437801 0.0388802 0.0705671 -0.0142336 0.00806667 0.0754076 -0.0163156 0.00239125 0.0725734 -0.0235378 0.00565016 0.0539004 -0.00540308 -0.008947 0.0561688 -0.00828116 -0.00418273 -0.00733372 0.0176978 0.00535471 0.0122077 0.0155966 0.0104405 0.00324054 0.0132272 0.00995829 -0.0562585 0.0401269 -0.0175459 -0.0446119 0.0268159 -0.0240277 -0.0473445 0.022285 -0.022387 0.0572974 0.0331504 -0.000841112 0.0609902 0.0335635 -0.00948954 0.0651677 0.029153 -0.00138657 -0.0356889 0.0322658 -0.00136898 -0.0352949 0.0277009 0.00385763 -0.0409486 0.0245515 0.00264322 -0.0282047 -0.0116054 0.00933899 -0.0289716 -0.0219964 0.00619357 -0.0353058 -0.0717959 -0.000896994 -0.0370293 -0.066472 -0.000971589 -0.0336973 -0.0659902 0.000380542 0.0839468 -0.0338174 -0.00136893 0.0839629 -0.0313502 0.00374555 0.0831556 -0.0222346 0.00146253 0.0173372 0.00796427 -0.0401615 0.0249151 0.010766 -0.0397207 0.0203335 0.0168978 -0.0395461 -0.0194173 0.0395698 -0.011378 -0.0214076 0.0391494 -0.00776851 -0.0135147 0.0359381 -0.00797349 0.0755309 -0.0326715 -0.00224941 0.0697775 -0.0257172 0.000200477 0.0721607 -0.0227865 -0.00311842 0.0117476 -0.0187927 -0.00953142 0.00139058 -0.018452 -0.00464215 0.0148994 -0.0162406 -0.00226025 -0.0278973 0.0252974 0.00663842 -0.0355814 0.0215604 0.0078019 -0.0295234 0.0311564 0.00292094 -0.0305029 -0.0379659 -0.00386007 -0.0337675 -0.0367394 -0.00267042 -0.0281936 -0.0273209 -0.00371586 -0.0356383 -0.0533352 0.00276125 -0.0363511 -0.0591656 -0.00133417 -0.0391315 -0.0653253 0.00145681 0.0470997 -0.0131811 -0.0284205 0.043289 -0.00950279 -0.0291274 0.0493392 -0.0192066 -0.0282237 -0.0639205 0.0411554 0.00809911 -0.062263 0.0477368 0.00780301 -0.061735 0.0479027 0.0140177 -0.0412646 0.0296845 -0.0242789 -0.0306213 0.0172087 0.0100856 -0.0347197 0.00916987 0.0111047 0.0734315 -0.0137594 -0.0012326 0.0725205 -0.00817402 -0.000920072 -0.0109156 -0.0659577 -0.0315063 -0.00612019 -0.0598084 -0.0299313 -0.00823789 -0.058883 -0.0338822 0.062391 -0.0175557 -4.21606e-05 0.0643664 -0.0178353 0.000139735 0.0565775 -0.0062394 -0.00480305 -0.0572371 0.0565767 -0.0142778 -0.0493568 0.0630387 -0.0120902 -0.0591786 0.0700523 -0.00168477 0.0622391 0.00130491 -0.016286 0.0684424 0.00943437 -0.0206362 0.063553 -0.0652642 -0.0305935 0.0650694 -0.0661949 -0.0290601 0.068222 -0.0600505 -0.0280908 -0.0128324 -0.0432547 -0.0266205 -0.0136914 -0.0546173 -0.0263203 -0.00944765 -0.0544546 -0.0279082 -0.0314232 0.0354067 -0.0027812 0.0159195 0.00791421 0.012986 0.0278224 0.0121009 0.0102275 0.0248503 0.00308164 0.0112843 0.0300506 0.0192408 -0.0371699 0.0424965 0.004065 0.0110577 0.0378098 0.0080696 0.00718983 0.0889291 -0.0584181 -0.00213292 0.0924972 -0.0599343 0.00196788 0.0870006 -0.0468936 0.000147852 0.00320344 -0.0166476 -0.0215024 0.00794282 -0.0124184 -0.0286089 0.0147101 -0.0147073 -0.025351 -0.0731193 0.0711506 0.0126372 -0.0686425 0.0706431 0.0113608 -0.0707638 0.0720756 0.00634764 -0.0294777 -0.0637631 0.0049476 -0.0354873 -0.0626793 0.00653606 -0.0330114 -0.0568285 0.00476831 0.0769691 -0.0750305 0.00255952 0.080587 -0.0756366 -0.00136879 0.076739 -0.0762003 0.00715723 0.0664182 -0.0537509 -0.0296266 0.0610779 -0.0446139 -0.0333518 0.0649473 -0.0564472 -0.0301647 -0.0918236 0.035478 0.0378491 -0.0876309 0.038438 0.0428251 -0.0916561 0.041501 0.0378023 0.0709632 -0.0282764 0.00427899 0.0622591 -0.0206167 0.0051501 0.0648333 -0.0193411 0.00892408 0.062759 0.0254758 0.00493017 -0.025234 -0.0395757 -0.0011651 -0.0233209 0.0171474 0.00885205 -0.0349773 -0.0686603 0.00445972 -0.0878021 0.0334778 0.0395365 -0.0822595 0.0367102 0.04049 -0.0834547 0.0319089 0.0349037 0.02345 0.0172611 0.00965423 0.0317819 0.0191276 0.00692742 -0.00303131 0.029368 -0.0160501 0.00204795 0.0282316 -0.0088956 -0.00179908 0.0287953 -0.00849424 0.0787196 -0.0281961 0.00396182 0.0795691 -0.0337514 0.00747774 -0.00435515 0.025613 -0.0247763 0.0105642 0.023972 -0.0332787 -0.0699571 0.0646915 -0.00456732 -0.0444244 0.0199281 0.00183703 -0.0484882 0.0242904 -0.00165671 -0.0453875 0.0272053 0.00166924 0.0696968 -0.074799 -0.0332352 0.0672755 -0.0748537 -0.0283082 0.0683374 -0.0672494 -0.0335944 -0.0259382 -0.0346616 -0.0289873 -0.0243194 -0.0365217 -0.024631 0.0605432 0.0167442 -0.0403055 0.0513994 0.021705 -0.0416474 0.0604317 0.02293 -0.0391152 -0.0247759 -0.00955957 0.0101692 -0.0193057 -0.00640913 0.00741031 -0.0207055 0.0039233 0.00909946 -0.0606119 0.0685695 0.00690157 -0.0477432 0.0646688 -0.00769541 0.0577098 -0.027658 -0.0262849 0.0550033 -0.0291686 -0.0278637 0.0631863 -0.0409916 -0.0296013 -0.0150379 -0.0664253 -0.0269393 -0.0228811 -0.0747796 -0.0308284 -0.0529618 0.0279913 -0.016736 -0.0483879 0.0177123 -0.0199927 -0.058404 0.0388491 0.00238339 -0.0615916 0.0410479 0.00514492 -0.0572749 0.0462431 0.00337953 -0.0160122 -0.00505491 0.0022377 0.0577225 -0.0205476 -0.0277863 -0.0878713 0.046921 0.0273304 -0.0884202 0.0461695 0.0338194 -0.0848841 0.0544501 0.0266955 -0.0300818 -0.0526309 -0.00022992 -0.02696 -0.0495303 0.00157891 0.065611 -0.0270528 -0.0288561 0.0604396 -0.0146447 -0.0287422 -0.0503109 0.0511616 -0.0167051 0.00260267 0.0286371 -0.0185408 0.010619 0.0295069 -0.0210639 -0.00350153 0.0275402 -0.0214963 0.0644003 -0.0638302 -0.0359592 0.0663646 -0.058229 -0.0376847 0.0650048 -0.0514324 -0.0363247 -0.0796837 0.0613559 0.0230028 -0.0867733 0.0609648 0.0183455 -0.0431793 0.0188506 -0.0299623 -0.0634358 0.0546736 -0.0118903 -0.0329525 0.00574304 -0.0349178 -0.0409326 0.00616488 -0.0320984 0.0116762 0.0192525 -0.03742 0.0261729 0.0250672 -0.0349872 0.0472641 0.0361711 -0.00451005 0.0380271 0.0364867 -0.00783421 0.0347373 0.0342971 -0.00132001 -0.0252887 0.0373779 -0.00468408 -0.0218934 0.0331122 -0.000901367 -0.0351863 -0.0765412 0.000269047 -0.0334314 -0.0711332 0.00546559 0.0703099 -0.0588181 -0.0379473 -0.0873394 0.0418515 0.0236989 -0.0141137 -0.0657514 -0.0340646 -0.0162333 -0.0693077 -0.0359948 -0.0440319 0.0313653 0.00165479 -0.0299632 -0.0203247 -0.0265567 -0.0279544 -0.0210832 -0.0291056 -0.0351685 -0.0100315 -0.0311827 -0.0177184 -0.0485681 -0.0308596 -0.0124929 -0.0531733 -0.0327238 -0.0138332 -0.0603952 -0.0342085 0.0890111 -0.0495745 0.00237033 0.0819737 -0.0426091 0.00562431 -0.0149491 -0.0439244 -0.0251441 -0.017156 -0.0368272 -0.0223064 0.0616062 0.0335902 -0.0206316 0.0584006 0.0334685 -0.0289357 0.052087 0.0365738 -0.0220487 0.0488089 0.0252794 0.00892338 0.0555975 0.0286405 0.00528475 0.058561 0.0207895 0.00992077 -0.0295641 -0.0183344 -0.00392657 -0.0106726 -0.0760597 -0.0269342 -0.0101258 -0.0729844 -0.034092 0.0290144 -0.000971558 0.00837186 0.0350223 0.0115732 0.00757634 0.0609688 -0.0750586 -0.0276747 0.058991 -0.0744717 -0.0386377 0.0555628 -0.0745321 -0.0335095 -0.069066 0.0400547 0.00533317 -0.0684592 0.0459763 -0.00223574 -0.07361 0.047381 0.00139974 -0.0321105 -0.00197184 -0.0351895 -0.0226133 -0.0363039 -0.031984 -0.0201097 -0.0344851 -0.0324018 -0.025281 -0.024728 -0.0293742 0.0806738 -0.071406 -0.00161154 0.087079 -0.0711054 -0.00195319 0.0821103 -0.0652388 0.00123242 -0.0163839 -0.0424141 -0.0313667 -0.0132942 -0.0358155 -0.0281592 -0.0123113 -0.0451744 -0.0291689 0.0595833 -0.0364169 -0.0356254 0.0582844 -0.030276 -0.0378638 0.0514739 -0.0248519 -0.0344557 0.00362627 -0.00963022 0.00580563 0.00798164 -0.0143225 0.00208706 0.0129595 -0.00801198 0.0081237 0.0543803 0.0360043 -0.0105735 -0.0698441 0.0758315 0.0191485 -0.0680219 0.0746397 0.0158251 0.0422127 0.0228577 0.00999155 0.0511791 0.0201473 0.0112951 -0.0891168 0.0341398 0.0321871 -0.0646033 0.0391448 0.00296346 -0.0631757 0.0389323 -0.00277568 -0.0143243 -0.0176195 -0.00678763 -0.00657241 -0.0163257 -0.0035067 -0.0232569 -0.0392128 0.00331221 -0.02808 -0.0553042 0.00326808 -0.0745909 0.0392233 0.0197847 -0.0791538 0.0380757 0.0263592 0.0629565 -0.0314845 -0.035558 0.0628938 -0.0445156 -0.0355054 -0.0647224 0.0668753 0.0136522 -0.0507754 0.0627422 0.000138282 -0.0685835 0.0648454 0.0195779 -0.0671208 0.0611895 0.0208556 -0.0660763 0.0578989 0.0189452 -0.0421986 -0.0759468 -0.00256403 -0.00245249 -0.0189631 -0.0111457 -0.0216245 -0.0176682 -0.011781 0.0874175 -0.0661163 0.00354018 -0.0151859 -0.0280585 -0.0270801 -0.0167126 -0.0282691 -0.0229621 -0.0140534 -0.0357938 -0.0235176 0.0728958 -0.0278529 0.00544968 -0.0182792 -0.0199474 -0.0281753 -0.0236519 -0.0245032 -0.02875 -0.0208535 -0.0113473 -0.0300427 -0.0160561 -0.0138077 -0.00380461 -0.018354 -0.0179969 0.000481826 0.0585702 -0.0081232 0.00419312 0.0613832 -0.0133591 0.00220111 -0.0221735 0.0248349 0.00527668 0.00896836 -0.017886 -0.0177516 0.0193835 -0.0151846 -0.0219734 0.0456951 0.02708 -0.0398735 0.0529841 0.0303148 -0.0373462 -0.0771701 0.0674374 0.00493047 -0.0764897 0.0619542 0.00187294 -0.0847748 0.072636 0.00620255 0.0665408 0.00159849 -0.00484928 -0.0729606 0.0639233 0.0233494 0.0798162 -0.0199223 -0.00217687 0.0690665 -0.018706 -0.00380082 0.0711213 -0.0141868 -0.00323123 -0.0779806 0.0700702 0.00802536 -0.0322233 -0.0618354 -0.00231007 -0.0152685 0.00252702 0.00554669 -0.0468598 -0.0764624 0.00214877 -0.0844843 0.0636155 0.0146971 -0.0836499 0.0665282 0.00920514 -0.0785786 0.0702539 0.0139144 0.0852193 -0.0724492 0.00872989 -0.0451306 0.00219422 -0.000861572 -0.0413944 -0.00729784 -6.61109e-05 -0.0661604 0.0740167 0.0206666 0.0262714 0.0246431 0.00333153 -0.0721086 0.0587045 -0.00398772 0.0461903 -0.0180509 -0.0307826 -0.0102531 0.0101205 0.00680815 -0.0164575 0.0125576 0.00686522 0.0578673 -0.0354047 -0.0308891 0.0666749 -0.00735377 -0.00298768 0.0697568 -0.00990359 -0.00423297 -0.0347637 0.0273377 -0.0288796 0.0586783 -0.00372311 -0.00361428 0.0583207 -0.00380796 -0.00911644 0.060362 0.00562289 0.0125812 0.0530218 0.0132511 0.0114025 0.0544708 0.00255479 0.012859 0.0374022 0.0269115 0.00774644 0.0336627 0.0301721 0.0038193 -0.0723753 0.0711081 0.0155806 0.0326763 -0.00954702 -0.0169461 0.0317372 -0.00692039 -0.0261175 0.0355566 -0.00777971 -0.0261449 0.0813561 -0.0232167 0.00461527 0.0649569 0.0234412 -0.0345332 0.0655371 0.0151418 -0.0360496 -0.0701977 0.0612766 0.0268817 -0.0754706 0.0572461 0.0283698 -0.0739857 0.0511276 0.0285731 -0.0164034 -0.00939153 -0.00214912 0.0547828 -0.0236575 -0.0368034 -0.0121656 0.0265316 -0.0258189 0.0563069 -0.0334836 -0.033492 0.0733331 -0.0605282 -0.0343146 0.0714299 -0.0534966 -0.0324479 0.0680057 -0.0399324 -0.0337683 -0.0173669 -0.0188725 -0.0200484 0.0814371 -0.0711336 0.00815863 0.0782531 -0.0722364 0.00564286 -0.0095996 -0.0609665 -0.0261475 0.0140735 -0.000268705 0.0117725 0.0197911 -0.00383032 0.00980013 0.0182611 -0.000761016 -0.0385481 0.0102475 0.0033322 -0.0393172 0.0503737 -0.00361778 -0.0148283 0.0512404 -0.00441911 -0.0253983 0.0556263 -0.00370852 -0.0167933 -0.0664762 0.0484478 0.0224707 -0.0336844 -0.0225332 0.00486868 -0.0361147 -0.0120508 0.00691541 -0.0190911 0.0310631 -0.0254278 -0.0111597 0.0221811 -0.0287143 0.0469395 0.0357568 -0.030629 0.0719613 -0.00833191 0.00354015 0.06822 -0.00788478 0.00767028 -0.0314822 0.0163094 -0.0335104 0.0720915 0.00546025 -0.0104031 0.0743349 0.00406846 -0.00470197 -0.0847937 0.0397633 0.0391925 0.0420702 -0.0090574 -0.0347143 0.0477273 -0.00715121 -0.0378672 0.0447441 -0.00216123 -0.0399011 0.0704147 -0.0521666 -0.0348447 -0.034272 -0.0143612 -0.0263751 0.0818466 -0.0646916 0.00460107 0.0809006 -0.048798 0.00395166 -0.02229 -0.0185299 -0.00168475 0.0197387 0.0314973 -0.0197209 0.00980328 0.0287907 -0.00937963 0.0627965 -0.0644453 -0.0340751 -0.0386478 0.0575842 -0.00973256 -0.0712874 0.0450919 0.0275897 -0.0690961 0.0526289 0.0247766 0.0637603 -0.0543169 -0.0341284 -0.0292823 -0.01665 -0.0205445 -0.02685 -0.0169011 -0.00799196 0.0574748 -0.0728332 -0.0297979 0.00759892 -0.00405429 0.00967596 0.00612076 0.0238218 0.00119427 0.0226107 0.0281753 -0.00177787 0.00741492 0.0266807 -0.00382404 -0.0594912 0.0402324 -0.013859 -0.0616059 0.0379795 -0.00707599 -0.0468836 0.00825335 -0.0246345 -0.0455527 0.00271499 -0.0177962 -0.0444579 0.00304092 -0.0277561 -0.0363426 -0.0124281 -0.00762832 0.0312597 0.00204979 -0.0341791 0.0233551 0.00308697 -0.0384194 0.0601615 0.00885821 -0.0400738 0.0621104 0.00453599 -0.0360744 0.0566076 0.00240897 -0.0403126 -0.0745307 0.0564153 0.00077043 -0.0642815 0.0499868 -0.00973032 0.0458527 0.00865361 -0.0422105 0.0384086 0.0051497 -0.041034 -0.0252824 -0.0285316 -0.0223765 -0.0409355 0.00761892 0.00908279 -0.0364622 -4.06306e-05 0.00923091 -0.0207476 -0.00821672 -0.0326586 -0.025394 -0.00600079 -0.0348486 0.0855331 -0.0662355 -0.000724932 -0.014921 -0.0729868 -0.0253179 -0.0209525 -0.0299016 -0.0216957 -0.0251057 -0.0178924 -0.0185516 -0.0671718 0.0575826 0.0252393 -0.0281191 0.0276505 -0.0284133 -0.0234806 0.0277768 -0.0288705 -0.0200256 0.036296 -0.019593 0.0564444 -0.00904699 -0.0371535 0.0504258 -0.0165821 -0.0392883 -0.0582276 0.0486686 -0.0147868 -0.0121781 0.0159934 -0.0321105 -0.00667092 0.0126475 -0.0336419 -0.012272 0.00743708 -0.0320301 -0.0864301 0.0525974 0.0242326 -0.0515044 0.0203009 -0.00433273 -0.0520144 0.0224781 -0.0120825 -0.04802 0.0139896 -0.00342766 -0.0212532 -0.0387862 -0.0227634 -0.0210812 0.0406455 -0.0149189 -0.0272649 0.0430675 -0.0166629 0.081629 -0.050499 0.00101881 -0.09148 0.0378939 0.0321716 0.0405975 0.0136088 0.00962597 0.0664374 -0.0657137 -0.036285 0.0668106 -0.0704559 -0.0377878 0.0393961 -0.00669426 -0.0340571 -0.0781401 0.0399436 0.0323086 0.0533758 -0.00816208 0.00332604 -0.0181399 0.0110053 -0.0338392 0.0708145 0.000992352 -0.00854138 0.0825529 -0.0241261 -0.00263263 -0.0393667 -0.0106545 -0.0230055 -0.0889887 0.0597609 0.0147773 0.0672499 -0.015451 0.00984042 0.0837336 -0.0474399 -0.000192327 0.0784854 -0.0390462 -2.51326e-06 0.0735008 0.015405 -0.0108876 0.0713143 0.0189655 -0.0151192 0.0769165 -0.0372789 0.00415737 -0.0379136 -0.00708555 0.00403723 -0.0383309 -0.00495197 -0.0253514 -0.0414947 -0.00295711 -0.0225407 0.0292802 -0.0126164 -0.010653 0.0200015 -0.0166919 -0.0100968 -0.0379568 0.0557544 -0.0135134 -0.0737346 0.038439 0.012184 -0.00894065 0.0311568 -0.0189375 -0.0457199 0.0129892 -0.0289379 -0.00275399 0.0223833 0.00140675 -0.0652353 0.0470017 -0.00540793 0.0233577 -0.0130152 -0.00125345 0.0668412 0.0276774 -0.0293407 0.0679226 0.0290336 -0.0159583 0.0709101 0.0228984 -0.0246697 -0.0317055 -0.00275706 0.0117794 -0.0309611 0.00549498 0.0111165 -0.0262299 -0.017806 -0.00452886 0.0447547 0.0325946 0.0032511 0.0665984 -0.0429505 -0.0306055 -0.0120547 -0.0671373 -0.0281486 0.0704542 -0.00372632 0.00695726 0.040037 -0.00509023 -0.0284016 0.00984027 0.0123339 -0.039398 0.0858452 -0.0757836 -0.0010213 0.0891577 -0.0754423 0.00495697 0.0579115 0.0292274 -0.0363328 0.0206308 0.0312986 -0.0104876 -0.0635817 0.055931 0.0108307 0.0842659 -0.0518601 0.00558785 0.0210444 0.030273 -0.00706697 -0.00338091 0.0199865 -0.0312582 -0.0334986 -0.028257 -0.00100805 -0.0345422 -0.0537866 -3.66152e-05 -0.0614165 0.0504314 0.00643288 -0.0342002 -0.0296476 0.00257492 -0.0322042 0.0489788 -0.00726024 -0.0366347 0.00177049 -0.0322802 -0.0381523 -0.00304431 -0.0294285 0.0163268 -0.0114427 0.00449271 0.0504876 0.00247842 -0.040908 0.0504528 0.0169037 -0.0417337 0.0418008 -0.00242997 -0.00372028 0.0438971 -0.0023664 -0.0157473 0.0395265 -0.00346564 -0.0100429 0.0222891 -0.00725239 0.00697266 0.00347416 0.00105483 -0.037085 0.0319815 0.0352999 -0.0105316 0.0706675 0.0171718 0.00147832 0.0665931 -0.00253646 0.00908812 0.0680409 0.0129547 0.00671541 0.0769819 -0.0300572 -0.00352066 0.0696675 0.0262377 -0.0128174 -0.0497838 0.0559923 0.00280405 -0.0649351 0.061943 0.0139571 0.0575529 -0.0222102 -0.0367079 0.0605648 -0.0226184 -0.0335885 -0.0803875 0.0608414 0.00552877 -0.0859416 0.0709469 0.00737344 0.0380214 -0.000848871 -0.0383271 -0.0412536 0.0559657 -0.0148495 0.0301525 -0.00372725 -0.0304074 0.0554472 0.0115023 -0.0421773 -0.0344442 -0.0056998 0.00958135 0.00251939 0.00667322 -0.0372836 0.0324032 0.0125593 -0.0369362 -0.0851302 0.0776749 0.00939945 -0.0297504 -0.0631618 0.000223 -0.0395993 0.0181455 0.00810673 -0.0434099 0.0118353 0.00674757 -0.0800869 0.0719246 0.00986814 -0.0426189 -0.00202813 0.00455468 0.0700433 0.0106197 -0.025425 0.0717578 0.0159573 -0.0263392 -0.0384465 0.0464623 -0.0020483 -0.0171094 0.00397073 -0.032191 -0.0133467 0.00200213 -0.030619 -0.0151772 -0.0751926 -0.0358218 -0.0206137 -0.0750931 -0.0343297 -0.000636836 -0.00549023 -0.0329833 0.06559 0.0184391 0.00736437 0.0724336 -0.0604862 -0.0301267 -0.0205582 -0.0756064 -0.025287 0.0359209 0.0201838 -0.0398071 0.0441742 0.0165657 -0.0412995 -0.0741798 0.0406901 0.00791172 -0.0789092 0.0428538 0.00382694 0.0669341 -0.0362 -0.031447 0.0434862 0.0322264 -0.0363259 0.0574303 -0.0164519 -0.0373467 -0.0448839 0.00699474 0.00491577 -0.0529349 0.0521784 0.00460431 -0.0444217 0.0602424 -0.00442243 -0.0652576 0.0690395 0.0202288 0.0347271 0.0277645 -0.0385278 0.0491876 0.00344011 0.0133795 0.0600862 -0.00594944 -0.037147 0.0126881 0.0209196 0.0063639 0.0838565 -0.0586024 -0.00192843 -0.00599885 -0.0184409 -0.0152901 -0.0604898 0.0358108 -0.000405533 0.0477038 0.0117064 0.0115124 -0.0170139 -0.0347189 -0.0306247 0.0843728 -0.0348832 0.00490371 -0.0324502 -0.0143569 0.00908889 0.0442871 -0.00394489 -0.0271087 0.0240502 -0.00868919 -0.0290734 -0.0521116 0.0417004 -0.0192603 0.0605014 -0.0130755 -0.0308906 -0.0473024 0.0122802 -0.0124696 0.0403502 -0.00280293 -0.0176493 0.0672424 0.0293735 -0.00736515 0.0646858 -0.0747605 -0.0385285 0.0624106 -0.00390694 -0.00189819 -0.0272913 0.00181261 0.0117625 0.0688917 0.00488887 0.00309445 0.0738354 0.00327896 0.000486695 0.0564466 -0.0182245 -0.0300306 0.0711486 0.0247458 -0.00319634 0.030952 -0.00872463 -0.00288756 0.0636152 -0.00344871 0.0112777 0.0747518 0.0119194 -0.00421227 -0.0427931 -0.0713429 0.00690988 -0.039051 -0.0665917 0.00520235 0.0528867 -0.00321292 -0.0398596 0.0294583 -0.00562924 0.00414759 0.0447825 0.0373931 -0.0133785 0.0354122 -0.000109014 0.00450045 -0.0353548 -0.0399241 0.00048683 -0.0812488 0.0432654 0.0373681 -0.0131821 0.0203566 0.00420704 0.0675887 0.00517251 -0.0339572 0.036641 -0.00356341 -0.00233653 -0.0911772 0.0422583 0.0333827 0.0662747 -0.0250612 -0.0328749 0.0736649 0.0202187 -0.00637979 0.0643844 0.00924572 0.0105862 0.084292 -0.0761281 0.00855887 -0.0845351 0.0501509 0.0333208 -0.0893639 0.039408 0.0356427 0.0736501 0.013314 -0.00067591 0.0366727 0.0113793 -0.0394278 -0.0847245 0.061041 0.0103509 0.0598698 -0.00478676 0.00212141 0.0750422 -0.0225737 0.00231398 0.0465668 -0.0175501 -0.0362436 3 1 0 2 3 4 3 5 3 6 7 8 3 10 9 11 3 12 13 14 3 15 16 17 3 18 19 20 3 21 22 23 3 24 25 26 3 27 28 29 3 3 30 31 3 33 32 34 3 36 35 37 3 38 39 40 3 41 42 43 3 45 44 46 3 48 47 49 3 51 50 52 3 54 53 55 3 57 56 58 3 59 60 61 3 63 62 64 3 65 66 67 3 68 3 4 3 70 69 71 3 73 72 74 3 32 75 34 3 76 77 22 3 69 70 78 3 13 79 80 3 82 81 56 3 83 84 85 3 87 86 88 3 89 90 91 3 92 93 94 3 95 96 97 3 99 98 100 3 28 101 102 3 104 103 105 3 106 107 108 3 110 109 111 3 113 112 114 3 13 115 116 3 117 118 119 3 121 120 122 3 124 123 125 3 126 127 128 3 129 130 131 3 133 132 134 3 136 135 137 3 138 139 140 3 141 142 143 3 144 145 146 3 147 148 149 3 69 150 71 3 152 151 136 3 99 153 154 3 156 155 157 3 159 158 160 3 161 7 162 3 8 163 6 3 164 22 165 3 166 167 168 3 169 170 171 3 172 43 112 3 174 173 175 3 176 124 125 3 27 177 178 3 180 179 181 3 78 150 69 3 182 183 184 3 185 186 187 3 189 188 190 3 191 192 193 3 194 195 196 3 197 198 199 3 201 200 202 3 203 111 109 3 140 35 204 3 151 205 135 3 119 188 206 3 208 207 209 3 211 210 174 3 213 212 214 3 215 216 121 3 218 217 75 3 7 219 8 3 221 220 222 3 224 223 225 3 226 25 227 3 228 229 230 3 118 143 142 3 231 232 233 3 144 146 23 3 214 128 74 3 163 234 235 3 236 237 238 3 240 239 26 3 242 241 108 3 244 243 245 3 246 232 12 3 21 23 247 3 248 249 250 3 252 251 204 3 236 253 254 3 255 161 162 3 256 257 258 3 260 259 261 3 262 263 250 3 107 92 264 3 161 265 7 3 94 266 267 3 125 268 269 3 270 271 272 3 273 172 137 3 127 273 274 3 9 275 276 3 277 260 261 3 278 248 3 3 279 280 26 3 43 42 281 3 283 282 284 3 285 286 287 3 289 288 216 3 290 291 24 3 292 293 294 3 296 295 297 3 66 65 298 3 155 299 300 3 301 302 175 3 304 303 305 3 307 306 308 3 266 54 309 3 311 310 312 3 40 39 190 3 137 172 112 3 314 313 315 3 316 317 318 3 320 319 321 3 322 323 324 3 292 294 325 3 185 326 327 3 328 329 295 3 330 207 197 3 226 310 285 3 332 331 306 3 333 133 334 3 336 335 38 3 128 274 74 3 338 337 91 3 339 340 319 3 234 341 342 3 343 344 345 3 156 157 318 3 322 57 334 3 117 346 118 3 75 217 258 3 348 347 333 3 180 15 349 3 350 351 352 3 100 353 216 3 354 355 356 3 333 357 358 3 160 359 360 3 274 135 361 3 132 362 363 3 229 364 365 3 366 367 368 3 60 59 369 3 344 343 370 3 371 372 373 3 8 374 187 3 119 118 375 3 246 58 376 3 346 117 275 3 346 377 143 3 258 212 256 3 130 158 372 3 379 378 380 3 381 349 17 3 382 50 383 3 384 327 326 3 211 328 385 3 367 366 386 3 387 146 145 3 388 389 376 3 237 390 238 3 235 162 6 3 360 391 392 3 150 78 393 3 394 395 369 3 177 27 29 3 396 397 398 3 35 335 204 3 385 399 400 3 384 326 401 3 402 403 404 3 215 121 405 3 406 64 407 3 408 409 410 3 357 334 411 3 321 412 320 3 191 315 313 3 258 217 413 3 387 321 414 3 392 154 153 3 416 415 417 3 2 418 351 3 419 420 381 3 239 421 170 3 175 422 423 3 424 44 425 3 426 427 428 3 149 429 90 3 66 430 431 3 432 413 433 3 68 30 3 3 293 365 434 3 98 435 436 3 266 94 437 3 154 438 439 3 97 198 440 3 442 441 443 3 415 444 417 3 445 284 282 3 356 2 354 3 315 446 447 3 448 37 35 3 449 257 450 3 196 451 166 3 126 452 127 3 453 429 454 3 455 261 259 3 185 187 374 3 65 456 457 3 166 458 167 3 236 146 237 3 455 195 261 3 422 82 459 3 375 142 251 3 456 348 457 3 460 461 462 3 463 241 464 3 466 465 467 3 445 65 468 3 469 470 124 3 471 472 473 3 474 386 475 3 477 476 443 3 227 478 456 3 479 480 152 3 481 482 55 3 147 89 244 3 315 483 314 3 239 484 299 3 396 297 329 3 478 485 486 3 344 408 487 3 370 262 409 3 489 488 490 3 400 461 385 3 312 283 85 3 442 491 492 3 475 493 106 3 494 495 496 3 248 250 497 3 498 499 500 3 478 227 501 3 502 490 503 3 387 390 237 3 447 504 315 3 505 278 330 3 178 177 506 3 304 507 508 3 145 509 441 3 279 155 300 3 209 510 208 3 28 102 511 3 53 494 512 3 320 339 319 3 5 248 497 3 513 154 392 3 131 371 514 3 50 51 515 3 516 263 378 3 100 517 353 3 518 504 519 3 520 438 521 3 223 224 304 3 522 216 200 3 31 338 330 3 431 523 67 3 524 445 525 3 363 526 527 3 38 80 79 3 408 344 370 3 528 452 502 3 529 306 331 3 212 258 530 3 242 531 465 3 429 453 90 3 371 99 122 3 446 315 191 3 214 72 532 3 533 307 474 3 134 527 534 3 270 109 325 3 535 536 537 3 150 393 92 3 521 438 61 3 480 538 539 3 444 415 277 3 540 448 140 3 109 541 296 3 519 200 130 3 253 238 542 3 543 239 299 3 156 543 155 3 436 435 544 3 161 255 71 3 191 193 420 3 145 545 509 3 546 495 268 3 460 214 532 3 263 516 497 3 548 547 314 3 549 535 406 3 123 425 546 3 37 14 36 3 387 237 146 3 550 449 450 3 149 551 345 3 532 72 103 3 5 3 248 3 452 528 235 3 482 54 55 3 195 340 261 3 322 324 459 3 552 15 288 3 553 550 450 3 433 554 494 3 555 298 140 3 82 173 81 3 556 251 142 3 42 41 557 3 430 66 558 3 46 183 45 3 334 133 323 3 385 328 399 3 231 538 115 3 427 22 428 3 273 127 559 3 267 560 561 3 197 505 330 3 502 126 212 3 540 333 448 3 323 134 562 3 563 476 564 3 566 565 567 3 143 118 346 3 301 423 568 3 44 569 425 3 272 570 553 3 80 38 335 3 19 28 20 3 174 302 211 3 397 396 329 3 572 571 573 3 129 574 518 3 575 521 537 3 576 341 577 3 578 579 339 3 11 276 206 3 558 141 39 3 367 580 581 3 374 8 219 3 332 307 464 3 238 390 194 3 316 318 286 3 443 582 477 3 230 406 407 3 260 277 259 3 502 530 490 3 212 126 128 3 503 70 583 3 100 98 517 3 438 59 61 3 296 203 109 3 582 584 469 3 585 471 473 3 264 94 531 3 160 360 159 3 2 351 350 3 272 553 461 3 440 208 97 3 538 586 115 3 587 425 569 3 173 174 210 3 347 133 333 3 122 99 405 3 588 124 176 3 581 589 368 3 565 28 27 3 590 251 252 3 473 563 585 3 495 587 496 3 591 220 592 3 589 593 374 3 450 213 462 3 157 286 318 3 589 374 366 3 510 209 338 3 480 479 594 3 242 108 531 3 521 595 596 3 338 209 330 3 335 252 204 3 78 488 393 3 597 576 41 3 459 324 422 3 88 598 599 3 83 309 84 3 600 601 280 3 358 14 37 3 45 87 602 3 557 148 244 3 103 389 388 3 185 374 380 3 500 592 220 3 383 594 382 3 328 397 329 3 436 517 98 3 541 400 399 3 195 194 390 3 319 414 321 3 445 468 515 3 97 96 198 3 119 206 117 3 297 603 203 3 444 277 261 3 276 117 206 3 604 415 416 3 198 197 207 3 310 316 285 3 240 605 239 3 565 566 426 3 606 564 607 3 14 357 411 3 239 605 484 3 308 608 609 3 22 77 595 3 196 168 194 3 610 542 416 3 611 33 47 3 87 1 86 3 120 574 514 3 598 55 53 3 595 165 22 3 78 503 488 3 578 492 612 3 336 38 40 3 106 463 475 3 479 613 594 3 41 614 557 3 323 562 324 3 83 561 560 3 534 568 562 3 441 582 443 3 153 373 392 3 412 578 320 3 279 287 157 3 559 615 597 3 152 539 151 3 592 500 613 3 157 287 286 3 343 616 401 3 123 546 125 3 159 392 373 3 239 24 26 3 229 617 364 3 398 397 618 3 165 61 164 3 293 292 535 3 171 170 421 3 279 157 155 3 485 418 486 3 363 403 402 3 283 312 226 3 57 246 411 3 552 288 289 3 612 579 578 3 87 183 182 3 90 89 147 3 84 312 85 3 51 466 525 3 473 472 619 3 600 299 605 3 540 457 333 3 415 604 225 3 230 549 406 3 583 255 162 3 302 174 175 3 201 202 20 3 210 620 104 3 557 244 245 3 340 414 319 3 358 37 448 3 179 483 621 3 441 387 145 3 409 408 370 3 258 413 530 3 591 592 479 3 418 622 486 3 332 498 623 3 183 87 45 3 278 31 330 3 337 30 529 3 293 549 365 3 624 618 397 3 153 99 371 3 317 316 625 3 181 288 180 3 121 216 626 3 222 281 245 3 431 627 586 3 281 114 112 3 628 427 566 3 387 414 390 3 173 105 81 3 322 459 56 3 424 584 629 3 173 82 422 3 309 482 84 3 240 26 280 3 76 491 619 3 106 493 630 3 523 383 67 3 631 612 491 3 355 354 625 3 563 443 476 3 632 465 466 3 506 302 178 3 65 540 298 3 402 633 567 3 74 274 361 3 302 506 211 3 231 115 12 3 630 493 71 3 74 361 73 3 68 529 30 3 422 175 173 3 269 176 125 3 83 85 284 3 291 352 351 3 101 426 428 3 257 256 450 3 391 369 392 3 634 110 111 3 508 635 304 3 475 533 474 3 114 591 113 3 359 636 391 3 163 235 6 3 93 266 437 3 136 591 152 3 398 618 29 3 270 541 109 3 81 58 56 3 421 239 543 3 20 202 18 3 292 110 536 3 498 500 221 3 428 164 395 3 570 449 550 3 383 523 594 3 538 231 637 3 638 639 571 3 106 108 463 3 502 452 126 3 149 90 147 3 640 23 146 3 216 215 100 3 620 103 104 3 270 272 541 3 108 264 531 3 603 571 641 3 424 469 584 3 226 282 283 3 615 235 342 3 304 224 303 3 497 516 5 3 539 152 480 3 438 154 513 3 33 611 617 3 12 232 231 3 191 420 446 3 519 522 200 3 534 526 642 3 536 110 634 3 80 335 36 3 243 623 498 3 138 251 139 3 501 291 485 3 340 195 414 3 44 602 569 3 396 398 643 3 380 593 379 3 66 298 555 3 290 169 352 3 333 334 357 3 245 243 221 3 551 149 148 3 250 263 497 3 508 225 223 3 2 0 622 3 470 424 124 3 644 639 439 3 20 511 201 3 41 172 597 3 425 587 546 3 404 509 545 3 312 355 311 3 12 115 13 3 483 180 349 3 441 442 492 3 116 430 79 3 645 11 646 3 181 518 574 3 647 443 473 3 517 202 353 3 204 251 138 3 629 584 403 3 408 410 487 3 642 648 534 3 280 300 600 3 599 569 602 3 542 194 604 3 395 60 369 3 192 313 314 3 29 19 643 3 294 48 649 3 29 28 19 3 482 481 84 3 194 542 238 3 39 141 190 3 356 481 86 3 159 360 392 3 325 294 649 3 628 566 633 3 401 185 380 3 518 519 129 3 240 280 601 3 439 639 154 3 494 53 93 3 117 276 275 3 277 415 225 3 352 169 317 3 163 8 187 3 621 315 504 3 83 284 561 3 650 301 648 3 558 651 141 3 188 189 206 3 652 97 208 3 265 161 493 3 50 67 383 3 284 85 283 3 378 263 380 3 493 161 71 3 615 342 576 3 616 343 577 3 382 613 500 3 23 640 247 3 407 228 230 3 134 132 527 3 388 105 103 3 218 554 217 3 467 465 531 3 403 584 509 3 11 9 276 3 73 389 653 3 204 138 140 3 275 9 10 3 355 312 84 3 302 301 650 3 77 654 63 3 570 49 449 3 331 243 244 3 536 535 292 3 464 241 499 3 535 549 293 3 388 81 105 3 428 395 101 3 24 227 25 3 186 341 234 3 453 454 510 3 531 94 267 3 268 554 218 3 564 606 617 3 555 558 66 3 185 401 326 3 48 434 47 3 476 477 607 3 266 93 53 3 419 381 17 3 496 512 494 3 52 466 51 3 219 386 366 3 133 134 323 3 184 629 363 3 642 567 655 3 29 643 398 3 278 656 248 3 543 156 421 3 291 290 352 3 401 380 370 3 33 34 47 3 76 619 654 3 636 369 391 3 399 295 541 3 642 526 567 3 196 455 451 3 48 49 649 3 205 151 539 3 401 370 343 3 657 253 610 3 624 397 328 3 480 523 431 3 129 519 130 3 658 521 575 3 499 332 464 3 486 622 348 3 659 573 603 3 89 331 244 3 651 139 141 3 212 530 502 3 23 22 427 3 203 641 111 3 410 652 510 3 386 219 265 3 547 548 660 3 385 620 210 3 560 309 83 3 259 451 455 3 96 95 661 3 577 343 345 3 352 317 350 3 337 89 91 3 525 467 561 3 639 638 572 3 657 254 253 3 245 42 557 3 351 485 291 3 360 359 391 3 123 124 424 3 551 148 557 3 239 170 24 3 451 259 304 3 63 595 77 3 57 411 334 3 413 217 433 3 274 137 135 3 636 394 369 3 167 225 604 3 3 31 278 3 639 544 435 3 68 4 609 3 324 423 422 3 24 501 227 3 505 662 656 3 593 581 379 3 629 46 424 3 498 332 499 3 201 158 130 3 658 663 644 3 424 46 44 3 572 18 436 3 660 548 381 3 609 529 68 3 477 664 607 3 596 63 64 3 284 445 524 3 193 381 420 3 458 305 303 3 522 504 447 3 389 637 233 3 15 552 16 3 452 559 127 3 145 144 427 3 141 139 556 3 661 250 249 3 348 333 457 3 445 456 65 3 178 302 27 3 242 632 499 3 630 71 150 3 145 628 545 3 539 538 637 3 514 574 131 3 219 7 265 3 331 332 623 3 468 65 67 3 418 2 622 3 469 477 582 3 655 565 27 3 662 197 199 3 536 634 641 3 384 341 327 3 500 632 52 3 579 612 631 3 253 236 238 3 101 395 160 3 526 402 567 3 534 527 526 3 568 423 324 3 435 99 154 3 317 625 350 3 93 489 494 3 234 163 187 3 297 295 329 3 502 503 528 3 136 113 591 3 167 303 224 3 301 175 423 3 456 282 227 3 580 474 4 3 590 375 251 3 489 432 433 3 375 590 119 3 187 186 234 3 402 404 633 3 428 22 164 3 623 243 331 3 172 41 43 3 137 112 113 3 615 576 597 3 572 643 18 3 241 463 108 3 229 228 585 3 212 128 214 3 488 489 93 3 650 655 27 3 455 196 195 3 416 542 604 3 663 571 639 3 179 180 483 3 65 457 540 3 453 91 90 3 339 657 417 3 603 297 659 3 619 491 647 3 400 541 272 3 616 341 384 3 424 425 123 3 88 602 87 3 48 294 434 3 39 79 558 3 378 379 665 3 61 165 521 3 366 368 589 3 548 349 381 3 532 620 460 3 664 469 588 3 181 120 288 3 474 367 386 3 495 546 587 3 172 273 597 3 417 657 610 3 657 579 631 3 462 213 460 3 254 21 247 3 439 438 520 3 284 524 561 3 277 507 259 3 385 461 620 3 483 349 314 3 190 141 189 3 589 581 593 3 639 572 544 3 454 429 345 3 218 269 268 3 169 290 170 3 536 575 537 3 154 639 435 3 382 594 613 3 19 18 643 3 633 545 628 3 218 75 32 3 585 563 564 3 199 96 661 3 305 451 304 3 631 254 657 3 347 132 133 3 53 512 598 3 468 67 50 3 1 87 0 3 506 328 211 3 298 540 140 3 655 648 642 3 42 245 281 3 162 235 528 3 143 646 141 3 551 614 577 3 606 269 617 3 484 605 299 3 97 652 95 3 441 509 582 3 35 140 448 3 601 605 240 3 132 363 527 3 23 427 144 3 427 426 566 3 211 385 210 3 389 73 637 3 515 51 525 3 436 18 517 3 468 50 515 3 366 374 219 3 304 635 223 3 447 446 16 3 0 87 182 3 620 461 460 3 60 164 61 3 347 362 132 3 579 657 339 3 376 232 246 3 491 76 631 3 78 70 503 3 274 128 127 3 629 403 363 3 560 266 309 3 46 629 184 3 650 27 302 3 640 146 247 3 510 338 91 3 622 182 362 3 306 529 608 3 34 75 257 3 124 588 469 3 101 160 102 3 643 572 659 3 586 480 431 3 641 571 663 3 92 107 150 3 471 585 228 3 95 410 409 3 553 450 462 3 475 464 533 3 500 220 221 3 296 541 295 3 622 347 348 3 271 570 272 3 205 361 135 3 601 600 605 3 318 169 171 3 59 438 513 3 265 493 475 3 345 551 577 3 403 509 404 3 396 659 297 3 565 426 101 3 410 95 652 3 430 116 627 3 300 299 600 3 469 424 470 3 182 184 362 3 615 452 235 3 57 322 56 3 263 262 380 3 394 160 395 3 580 665 379 3 15 17 349 3 256 212 213 3 307 533 464 3 619 647 473 3 647 442 443 3 614 41 576 3 357 14 358 3 168 604 194 3 227 282 226 3 491 442 647 3 285 279 26 3 24 170 290 3 653 103 72 3 355 84 356 3 661 249 199 3 289 522 447 3 105 173 104 3 363 402 526 3 213 214 460 3 542 610 253 3 93 437 94 3 666 636 359 3 189 646 206 3 609 4 474 3 14 13 80 3 34 257 449 3 444 261 417 3 555 651 558 3 13 116 79 3 160 158 102 3 490 432 489 3 655 567 565 3 0 182 622 3 339 417 340 3 162 528 583 3 382 52 50 3 114 222 220 3 405 667 215 3 387 668 321 3 377 645 143 3 216 522 289 3 255 583 70 3 98 99 435 3 521 596 537 3 288 120 121 3 365 364 611 3 494 554 495 3 411 12 14 3 181 574 120 3 264 108 107 3 143 645 646 3 315 621 483 3 416 417 610 3 637 231 233 3 617 32 33 3 633 404 545 3 536 641 575 3 167 458 303 3 576 577 614 3 29 618 177 3 376 58 388 3 156 171 421 3 44 45 602 3 311 625 316 3 410 454 487 3 54 266 53 3 265 475 386 3 280 279 300 3 208 440 198 3 458 451 305 3 370 380 262 3 626 216 288 3 586 538 480 3 309 54 482 3 473 443 563 3 625 311 355 3 558 79 430 3 230 229 365 3 278 505 656 3 559 597 273 3 340 417 261 3 568 648 301 3 356 84 481 3 12 411 246 3 602 88 599 3 503 583 528 3 55 88 481 3 572 573 659 3 220 591 114 3 636 666 394 3 372 371 131 3 644 663 639 3 569 599 496 3 513 369 59 3 476 607 564 3 77 76 654 3 593 380 374 3 192 547 660 3 122 514 371 3 511 102 201 3 479 152 591 3 570 550 553 3 409 262 661 3 62 407 64 3 166 451 458 3 530 413 432 3 572 638 571 3 287 279 285 3 165 595 521 3 73 205 637 3 167 604 168 3 36 14 80 3 478 486 456 3 522 519 504 3 173 210 104 3 658 641 663 3 634 111 641 3 586 627 115 3 632 500 499 3 463 464 475 3 345 429 149 3 334 323 322 3 478 501 485 3 544 572 436 3 228 407 471 3 454 345 487 3 350 354 2 3 185 327 186 3 431 67 66 3 281 222 114 3 667 100 215 3 207 330 209 3 390 414 195 3 590 188 119 3 567 633 566 3 580 4 665 3 407 62 654 3 635 508 223 3 339 320 578 3 376 233 232 3 267 266 560 3 158 201 102 3 218 32 269 3 136 151 135 3 507 225 508 3 292 325 110 3 93 92 393 3 264 92 94 3 306 307 332 3 159 372 158 3 74 72 214 3 616 577 341 3 517 18 202 3 137 113 136 3 554 268 495 3 141 646 189 3 603 641 203 3 137 274 273 3 660 193 192 3 146 236 247 3 490 488 503 3 609 474 308 3 568 534 648 3 612 492 491 3 16 446 17 3 648 655 650 3 499 241 242 3 466 52 632 3 39 38 79 3 169 318 317 3 477 469 664 3 336 190 188 3 471 407 472 3 456 445 282 3 155 543 299 3 512 496 598 3 76 21 254 3 221 222 245 3 252 336 590 3 373 372 159 3 20 28 511 3 106 630 107 3 368 367 581 3 86 481 88 3 487 345 344 3 590 336 188 3 285 26 25 3 654 472 407 3 130 200 201 3 177 618 506 3 627 116 115 3 561 467 267 3 1 2 86 3 285 25 226 3 131 574 129 3 555 140 139 3 71 255 70 3 325 649 270 3 76 22 21 3 271 49 570 3 52 382 500 3 96 199 198 3 184 363 362 3 453 510 91 3 351 418 485 3 63 654 62 3 518 181 179 3 489 433 494 3 662 505 197 3 492 668 441 3 466 467 525 3 11 645 10 3 448 333 358 3 587 569 496 3 369 513 392 3 349 548 314 3 7 6 162 3 530 432 490 3 75 258 257 3 89 337 529 3 389 103 653 3 356 86 2 3 624 506 618 3 196 166 168 3 387 441 668 3 419 17 446 3 378 665 516 3 190 336 40 3 268 125 546 3 373 153 371 3 427 628 145 3 121 122 405 3 254 631 76 3 598 496 599 3 617 229 564 3 384 401 616 3 365 549 230 3 64 406 535 3 342 235 234 3 248 656 249 3 667 405 99 3 594 523 480 3 335 35 36 3 28 565 101 3 99 100 667 3 649 271 270 3 580 379 581 3 529 609 608 3 224 225 167 3 576 342 341 3 664 176 606 3 350 625 354 3 82 56 459 3 596 595 63 3 10 645 377 3 254 247 236 3 139 651 555 3 359 160 666 3 281 112 43 3 308 474 307 3 662 199 656 3 72 73 653 3 626 288 121 3 164 60 395 3 525 445 515 3 141 556 142 3 434 611 47 3 637 205 539 3 456 486 348 3 347 622 362 3 30 337 31 3 346 275 10 3 217 554 433 3 520 644 439 3 199 249 656 3 206 646 11 3 328 506 624 3 556 139 251 3 627 431 430 3 510 454 410 3 620 532 103 3 559 452 615 3 312 310 226 3 571 603 573 3 316 310 311 3 64 537 596 3 409 661 95 3 446 420 419 3 318 171 156 3 205 73 361 3 147 244 148 3 568 324 562 3 394 666 160 3 286 285 316 3 200 216 353 3 659 396 643 3 562 134 534 3 507 304 259 3 621 518 179 3 335 336 252 3 377 346 10 3 531 267 467 3 184 183 46 3 654 619 472 3 584 582 509 3 225 507 277 3 447 16 552 3 376 389 233 3 498 221 243 3 608 308 306 3 381 193 660 3 598 88 55 3 535 537 64 3 142 375 118 3 293 434 294 3 120 514 122 3 501 24 291 3 150 107 630 3 191 313 192 3 658 644 520 3 450 256 213 3 668 492 321 3 665 5 516 3 176 664 588 3 606 176 269 3 337 338 31 3 131 130 372 3 331 89 529 3 186 327 341 3 250 661 262 3 617 611 364 3 658 575 641 3 32 617 269 3 5 665 4 3 607 664 606 3 49 47 449 3 518 621 504 3 198 207 208 3 557 614 551 3 367 474 580 3 192 314 547 3 652 208 510 3 110 325 109 3 578 412 492 3 412 321 492 3 246 57 58 3 649 49 271 3 180 288 15 3 447 552 289 3 200 353 202 3 295 399 328 3 525 561 524 3 272 461 400 3 47 34 449 3 203 296 297 3 393 488 93 3 632 242 465 3 434 365 611 3 461 553 462 3 81 388 58 3 521 658 520 3 229 585 564 3 613 479 592 pyformex-0.8.6/pyformex/data/horse.pgf0000644000211500021150000060752411406164537017657 0ustar benebene00000000000000# pyFormex Geometry File (http://pyformex.org) version='1.2'; sep=' ' # nelems=1334; nplex=3; props=False; eltype=None; sep=' '; name='horse_000' -0.00552152097225 -0.0164588801563 -0.0201352518052 -0.015117011033 -0.0188090614974 -0.0260683037341 -0.0097112422809 -0.0125950593501 -0.0233362093568 -0.0871415585279 0.0463454276323 0.0139984423295 -0.0870704501867 0.052732732147 0.0148491580039 -0.0838714838028 0.0507906712592 0.00711987819523 -0.0637906864285 0.0637098252773 -0.00896131899208 -0.0656885206699 0.0705391466618 -0.000896229466889 -0.0568155124784 0.0680090263486 -0.00663143396378 -0.0356450267136 -0.0763839781284 0.00708497595042 -0.0377826243639 -0.0715468376875 0.00810029637069 -0.0426602773368 -0.0763025358319 0.00917287915945 -0.0270780660212 -0.0214741248637 0.00396085856482 -0.0228501725942 -0.016540652141 0.00465866690502 -0.0212301537395 -0.0123340161517 0.00449436390772 0.0855906233191 -0.056857522577 0.00729697011411 0.0852778479457 -0.0646592378616 0.00590485380962 0.0917203947902 -0.059632204473 0.00453671533614 0.054339889437 -0.00603239936754 0.00829239841551 0.0584446676075 -0.0131367407739 0.00669920956716 0.0604619793594 -0.0071836868301 0.00888195540756 0.0600458458066 -0.00507800886407 -0.0249140188098 0.0570097789168 -0.0103748161346 -0.028088811785 0.0592680051923 -0.00785803515464 -0.0265315212309 -0.0187529120594 -0.0492971539497 -0.0287065207958 -0.0166946779937 -0.0649743825197 -0.0305308811367 -0.0174274221063 -0.0494892522693 -0.0261709243059 0.0495941489935 -0.00505510251969 0.00281282933429 0.0458973273635 -0.000604764616583 0.0111892325804 0.0385896004736 0.000291874835966 0.00444744434208 -0.0803267359734 0.0397947318852 0.0164727605879 -0.0846682190895 0.0387630797923 0.0249170754105 -0.0871415585279 0.0463454276323 0.0139984423295 0.0226895809174 0.0281979069114 -0.0306504108012 0.0211553759873 0.0312036816031 -0.0245885774493 0.0339798666537 0.0333764031529 -0.0331196337938 -0.022982083261 -0.0271414145827 -3.11253916152e-06 -0.0215143859386 -0.019579783082 0.00190126814414 -0.0230524111539 -0.0281649492681 0.00343656959012 -0.0329865068197 -0.039376296103 0.00508400192484 -0.0304189305753 -0.0502045191824 0.00526972860098 -0.0287229511887 -0.0406478978693 0.00720230536535 -0.0542124435306 0.0460482388735 0.00364887295291 -0.0400194935501 0.035411670804 -0.00117507681716 -0.0454866327345 0.0472589023411 0.000885553658009 0.00936001725495 -0.00345246354118 -0.0368482433259 0.0144752385095 -0.0070848762989 -0.034492328763 0.00573587883264 -0.00811646040529 -0.0328484363854 0.0348762497306 0.0356907360256 -0.0298048574477 0.0356194712222 0.0371010899544 -0.0225756745785 0.0438260957599 0.0375089608133 -0.0233849193901 -0.0398653969169 -0.00837041065097 -0.00824570097029 -0.0434817001224 0.000976005452685 -0.00737236114219 -0.0418213754892 -0.00742403836921 -0.0206348095089 -0.0245153158903 0.0113811995834 -0.0345406457782 -0.0196293834597 -0.0012800822733 -0.0339007712901 -0.0262746438384 0.00359826651402 -0.0357828103006 -0.00387709727511 -0.00124160922132 0.00781158497557 -0.012700362131 0.00108930072747 0.00499837147072 -0.012282749638 -0.00724740931764 -2.84041252598e-05 0.0656465515494 0.00329640181735 -0.0110076479614 0.0687282457948 0.0103019243106 -0.0152066750452 0.0689210742712 0.00494329864159 -0.0121464785188 0.0688897669315 0.0143098514527 -0.034747581929 0.0694160535932 0.0198294073343 -0.0330271050334 0.0710225701332 0.0116371689364 -0.0303473770618 -0.0363080054522 -0.0172072835267 0.00185445009265 -0.0368985123932 -0.0142811005935 0.00302317948081 -0.0325671248138 -0.0150363976136 -0.00584217207506 -0.0871415585279 0.0463454276323 0.0139984423295 -0.0838714838028 0.0507906712592 0.00711987819523 -0.0825153067708 0.041629076004 0.009376286529 -0.0403283089399 0.0349768586457 -0.0214138980955 -0.0509763434529 0.0460034161806 -0.0177591200918 -0.0381806567311 0.0407792925835 -0.019973538816 -0.00962803792208 0.0256944280118 1.13362448246e-05 -0.0180822927505 0.0280927624553 0.00312319723889 -0.0181487128139 0.0191756095737 0.00725237466395 0.0100417975336 0.0276991538703 -0.0274829212576 0.0211553759873 0.0312036816031 -0.0245885774493 0.0226895809174 0.0281979069114 -0.0306504108012 0.0675277709961 0.00351993273944 -0.0305571258068 0.0600458458066 -0.00507800886407 -0.0249140188098 0.0602696500719 -0.00843365583569 -0.0316083207726 -0.0381806567311 0.0407792925835 -0.019973538816 -0.0321282558143 0.0364747196436 -0.0215411484241 -0.0403283089399 0.0349768586457 -0.0214138980955 -0.0306760501117 -0.0296483207494 0.00481163570657 -0.0257243346423 -0.0339680574834 0.00464337132871 -0.0270780660212 -0.0214741248637 0.00396085856482 0.00083865004126 0.00723583996296 0.0104546826333 -0.00387709727511 -0.00124160922132 0.00781158497557 0.00531596504152 0.00230240589008 0.0111257759854 -0.0242858082056 -0.0106024397537 -0.0329431965947 -0.0277498979121 -0.0136683974415 -0.0326979234815 -0.0333822481334 -0.00652450276539 -0.0326015427709 -0.0144916502759 -0.00543817412108 -0.0263480637223 -0.0137986438349 -0.001824338804 -0.028773734346 -0.00354365818202 -0.0125812022015 -0.0245189536363 -0.0643574744463 0.0411904193461 0.0170047152787 -0.0709582194686 0.0410792566836 0.0236960314214 -0.0680031627417 0.0388116128743 0.0118984086439 -0.0276381317526 0.0215675216168 -0.0321951173246 -0.0391113087535 0.0111347045749 -0.0334663800895 -0.0394592247903 0.021167581901 -0.0313589386642 -0.0877385362983 0.0418181084096 0.0414660908282 -0.0824304521084 0.0437800884247 0.0388801582158 -0.0804472342134 0.0510345138609 0.0322105102241 0.0705670714378 -0.0142336087301 0.0080666737631 0.0725733786821 -0.0235378220677 0.00565016036853 0.0754076316953 -0.0163155626506 0.0023912510369 0.0539004430175 -0.00540307629853 -0.00894699897617 0.0561687573791 -0.00828116200864 -0.00418273126706 0.0495941489935 -0.00505510251969 0.00281282933429 -0.00733371544629 0.0176978223026 0.00535471225157 0.00324054062366 0.0132272355258 0.00995828956366 0.0122077101842 0.0155965732411 0.0104404864833 -0.0446118675172 0.0268158894032 -0.0240276791155 -0.0473444573581 0.0222850441933 -0.0223869681358 -0.0562584511936 0.0401268824935 -0.0175458882004 0.0572973936796 0.0331504121423 -0.000841111701448 0.065167747438 0.0291529707611 -0.00138657167554 0.0609902478755 0.0335634611547 -0.0094895362854 -0.035688880831 0.0322657860816 -0.00136898260098 -0.0409485846758 0.0245515368879 0.00264321570285 -0.0352949239314 0.0277008544654 0.00385763379745 -0.0282046999782 -0.011605437845 0.00933898519725 -0.0289716441184 -0.0219964478165 0.00619356799871 -0.0270780660212 -0.0214741248637 0.00396085856482 -0.0370292626321 -0.0664719641209 -0.00097158877179 -0.033697348088 -0.0659902095795 0.000380541518098 -0.0353057719767 -0.0717958807945 -0.000896993966307 0.0839467793703 -0.0338173881173 -0.0013689303305 0.0831556394696 -0.0222345832735 0.00146252510604 0.0839628651738 -0.0313502401114 0.00374555471353 0.0173371694982 0.00796426646411 -0.0401615314186 0.0203334894031 0.0168978217989 -0.039546135813 0.0249150972813 0.0107660228387 -0.0397206582129 -0.0214076433331 0.0391493700445 -0.00776851270348 -0.013514730148 0.0359381325543 -0.00797349400818 -0.0194173324853 0.0395698286593 -0.0113779669628 0.0697775110602 -0.0257171951234 0.000200476919417 0.0721606612206 -0.022786539048 -0.00311841559596 0.075530923903 -0.0326715037227 -0.00224941363558 0.0117476321757 -0.018792707473 -0.00953142251819 0.0148993665352 -0.016240587458 -0.00226025399752 0.00139058486093 -0.0184520073235 -0.00464215269312 -0.0278973150998 0.0252974294126 0.00663841702044 -0.0295234173536 0.0311564076692 0.00292094191536 -0.0355813615024 0.0215603802353 0.00780190061778 -0.0337675176561 -0.0367394164205 -0.00267041777261 -0.0281935930252 -0.027320895344 -0.00371586484835 -0.0305029321462 -0.0379659235477 -0.00386006757617 -0.0363510511816 -0.059165596962 -0.00133417372126 -0.0391314774752 -0.0653252974153 0.00145680934656 -0.0356382876635 -0.0533351935446 0.00276124686934 0.0432890281081 -0.00950278807431 -0.0291274245828 0.0493391752243 -0.0192066207528 -0.0282236821949 0.0470996797085 -0.0131811331958 -0.028420528397 -0.0622629895806 0.0477367714047 0.00780300609767 -0.0617349892855 0.0479026585817 0.0140177402645 -0.0639205127954 0.0411553606391 0.00809910707176 -0.0412645675242 0.0296845398843 -0.0242789257318 -0.0509763434529 0.0460034161806 -0.0177591200918 -0.0403283089399 0.0349768586457 -0.0214138980955 -0.0306212659925 0.0172087047249 0.0100856469944 -0.0355813615024 0.0215603802353 0.00780190061778 -0.0347196757793 0.00916986633092 0.0111047113314 0.0734314844012 -0.0137593969703 -0.0012326046126 0.0725204646587 -0.00817401800305 -0.000920072081499 0.0754076316953 -0.0163155626506 0.0023912510369 -0.010915578343 -0.0659576877952 -0.0315063148737 -0.00823789276183 -0.0588829554617 -0.0338821560144 -0.00612018536776 -0.0598083510995 -0.0299312565476 0.0623909682035 -0.0175557024777 -4.21606127929e-05 0.0565774738789 -0.00623939558864 -0.00480305217206 0.0643664449453 -0.0178353134543 0.000139734620461 -0.0637906864285 0.0637098252773 -0.00896131899208 -0.0493567548692 0.0630387067795 -0.0120902014896 -0.0572370886803 0.056576654315 -0.0142777692527 -0.059178583324 0.0700522735715 -0.00168476975523 -0.0568155124784 0.0680090263486 -0.00663143396378 -0.0656885206699 0.0705391466618 -0.000896229466889 0.0600458458066 -0.00507800886407 -0.0249140188098 0.0684423595667 0.00943436846137 -0.0206362344325 0.0622391216457 0.00130491273012 -0.0162859521806 0.0650694146752 -0.0661949291825 -0.0290601216257 0.068222001195 -0.0600504949689 -0.0280908290297 0.0635530352592 -0.0652642175555 -0.0305935442448 -0.0136914439499 -0.0546173118055 -0.0263203363866 -0.00944765098393 -0.0544546283782 -0.0279082152992 -0.0128323584795 -0.0432547368109 -0.0266204569489 -0.0400194935501 0.035411670804 -0.00117507681716 -0.035688880831 0.0322657860816 -0.00136898260098 -0.0314232259989 0.0354066677392 -0.00278120324947 0.0159195382148 0.00791420787573 0.0129859792069 0.0248502586037 0.00308163510635 0.0112843178213 0.0278224293143 0.0121009163558 0.0102275209501 0.0249150972813 0.0107660228387 -0.0397206582129 0.0203334894031 0.0168978217989 -0.039546135813 0.0300506222993 0.0192408021539 -0.0371698960662 0.0424964986742 0.00406499719247 0.0110576944426 0.0378097891808 0.00806960370392 0.00718982517719 0.0385896004736 0.000291874835966 0.00444744434208 0.0889291241765 -0.0584181435406 -0.00213292124681 0.0870006456971 -0.046893555671 0.000147852333612 0.0924971923232 -0.0599343143404 0.00196787621826 -0.0412645675242 0.0296845398843 -0.0242789257318 -0.0403283089399 0.0349768586457 -0.0214138980955 -0.0321282558143 0.0364747196436 -0.0215411484241 0.00794281996787 -0.0124184312299 -0.0286089126021 0.0147100556642 -0.0147073278204 -0.0253510158509 0.00320344255306 -0.0166476164013 -0.0215023886412 -0.0686424598098 0.0706430822611 0.0113607710227 -0.0707638338208 0.0720755532384 0.00634763808921 -0.0731192678213 0.071150586009 0.0126371867955 -0.0294777080417 -0.0637631043792 0.00494760088623 -0.033011417836 -0.0568285062909 0.00476831337437 -0.0354872792959 -0.0626792758703 0.00653606234118 0.0805870443583 -0.075636588037 -0.00136879249476 0.0767389982939 -0.0762002691627 0.00715722655877 0.0769691392779 -0.075030490756 0.002559523331 0.0610778927803 -0.0446138717234 -0.033351752907 0.0649472996593 -0.0564471706748 -0.0301647242159 0.0664181634784 -0.0537509098649 -0.0296265520155 -0.0876308903098 0.0384379811585 0.0428250767291 -0.0916560813785 0.0415009707212 0.0378023125231 -0.0918235704303 0.0354779586196 0.0378490537405 0.0709631517529 -0.0282764472067 0.0042789876461 0.0648333206773 -0.0193411242217 0.00892408099025 0.0622591376305 -0.020616741851 0.00515010254458 0.065167747438 0.0291529707611 -0.00138657167554 0.0572973936796 0.0331504121423 -0.000841111701448 0.0627590343356 0.0254758372903 0.00493016792461 -0.022982083261 -0.0271414145827 -3.11253916152e-06 -0.0252340473235 -0.0395757183433 -0.00116509769578 -0.0281935930252 -0.027320895344 -0.00371586484835 -0.0233208872378 0.0171473640949 0.00885204598308 -0.0278973150998 0.0252974294126 0.00663841702044 -0.0306212659925 0.0172087047249 0.0100856469944 -0.0294777080417 -0.0637631043792 0.00494760088623 -0.0349772572517 -0.0686603486538 0.00445972103626 -0.033697348088 -0.0659902095795 0.000380541518098 -0.0878021046519 0.033477794379 0.0395364612341 -0.0834547281265 0.0319089256227 0.0349037349224 -0.0822595283389 0.0367101840675 0.0404899641871 0.023449998349 0.0172611102462 0.00965423230082 0.0278224293143 0.0121009163558 0.0102275209501 0.0317819230258 0.0191275794059 0.00692741526291 -0.00303131062537 0.0293679926544 -0.0160501208156 -0.00179908366408 0.0287952888757 -0.0084942439571 0.0020479504019 0.0282315947115 -0.00889560487121 0.0795691013336 -0.0337514281273 0.00747773703188 0.0839628651738 -0.0313502401114 0.00374555471353 0.0787196084857 -0.0281961280853 0.00396182155237 -0.00435515260324 0.025612950325 -0.0247763358057 0.0100417975336 0.0276991538703 -0.0274829212576 0.010564208962 0.0239719748497 -0.0332786925137 -0.0699571371078 0.0646914690733 -0.00456732325256 -0.0656885206699 0.0705391466618 -0.000896229466889 -0.0637906864285 0.0637098252773 -0.00896131899208 -0.0444244034588 0.0199280641973 0.00183702539653 -0.0453874990344 0.0272053200752 0.00166924321093 -0.0484882481396 0.0242904219776 -0.00165670679417 0.0696968138218 -0.0747989788651 -0.0332351587713 0.0683374479413 -0.0672494471073 -0.0335944145918 0.0672754794359 -0.0748537257314 -0.028308160603 -0.0187529120594 -0.0492971539497 -0.0287065207958 -0.0243194364011 -0.0365216545761 -0.0246309749782 -0.025938231498 -0.03466161713 -0.0289873387665 0.051399409771 0.0217050053179 -0.0416473895311 0.060431715101 0.0229300074279 -0.0391151756048 0.0605431683362 0.0167442467064 -0.0403055250645 -0.0391314774752 -0.0653252974153 0.00145680934656 -0.0363510511816 -0.059165596962 -0.00133417372126 -0.0370292626321 -0.0664719641209 -0.00097158877179 -0.0193056520075 -0.00640912586823 0.00741031300277 -0.0207055173814 0.00392329553142 0.00909946020693 -0.0247759241611 -0.0095595670864 0.010169220157 0.0493391752243 -0.0192066207528 -0.0282236821949 0.0570097789168 -0.0103748161346 -0.028088811785 0.0470996797085 -0.0131811331958 -0.028420528397 -0.013514730148 0.0359381325543 -0.00797349400818 -0.0180822927505 0.0280927624553 0.00312319723889 -0.00179908366408 0.0287952888757 -0.0084942439571 -0.0606118962169 0.0685695037246 0.00690156780183 -0.0477432273328 0.0646687820554 -0.00769540853798 -0.059178583324 0.0700522735715 -0.00168476975523 0.0550032742321 -0.029168618843 -0.0278636794537 0.0631862655282 -0.0409915708005 -0.0296012889594 0.0577097833157 -0.0276579707861 -0.0262848697603 -0.0150378849357 -0.0664253011346 -0.026939317584 -0.0166946779937 -0.0649743825197 -0.0305308811367 -0.0228810738772 -0.0747795701027 -0.0308283958584 -0.0529617629945 0.027991278097 -0.0167360249907 -0.0473444573581 0.0222850441933 -0.0223869681358 -0.0483878850937 0.0177123211324 -0.0199927464128 -0.0584039799869 0.0388491488993 0.00238339230418 -0.0572749041021 0.046243134886 0.00337952957489 -0.0615916401148 0.041047938168 0.00514492439106 -0.0193056520075 -0.00640912586823 0.00741031300277 -0.0212301537395 -0.0123340161517 0.00449436390772 -0.0160121638328 -0.00505490787327 0.00223769713193 0.0570097789168 -0.0103748161346 -0.028088811785 0.0577224753797 -0.020547606051 -0.0277862790972 0.0592680051923 -0.00785803515464 -0.0265315212309 -0.0884201526642 0.046169500798 0.0338194072247 -0.084884122014 0.054450109601 0.0266954693943 -0.0878712534904 0.0469209626317 0.0273304414004 -0.0300818383694 -0.0526309125125 -0.000229920042329 -0.0252340473235 -0.0395757183433 -0.00116509769578 -0.0269599817693 -0.0495302826166 0.00157890853006 0.0656110197306 -0.0270528197289 -0.0288560781628 0.0604395531118 -0.014644660987 -0.0287422351539 0.0577097833157 -0.0276579707861 -0.0262848697603 -0.0572370886803 0.056576654315 -0.0142777692527 -0.0493567548692 0.0630387067795 -0.0120902014896 -0.0503109395504 0.0511616170406 -0.0167050715536 0.0106190349907 0.02950688079 -0.021063869819 -0.00350152561441 0.0275401733816 -0.0214963126928 0.00260267360136 0.0286370813847 -0.0185407903045 0.0644002556801 -0.0638302341104 -0.0359592176974 0.0650048032403 -0.0514324046671 -0.0363247282803 0.066364556551 -0.058229021728 -0.0376847051084 -0.0867733284831 0.0609648488462 0.0183455236256 -0.084884122014 0.054450109601 0.0266954693943 -0.0796836540103 0.0613558962941 0.0230027772486 -0.0394592247903 0.021167581901 -0.0313589386642 -0.043179307133 0.0188505705446 -0.0299622882158 -0.0446118675172 0.0268158894032 -0.0240276791155 -0.0634358376265 0.0546735934913 -0.0118903163821 -0.0637906864285 0.0637098252773 -0.00896131899208 -0.0572370886803 0.056576654315 -0.0142777692527 -0.0329524688423 0.00574303558096 -0.0349177606404 -0.0409325622022 0.00616488046944 -0.0320984274149 -0.0391113087535 0.0111347045749 -0.0334663800895 0.0116761699319 0.0192524939775 -0.0374199561775 0.0261729359627 0.0250671524554 -0.0349872037768 0.0203334894031 0.0168978217989 -0.039546135813 0.0380271226168 0.0364866815507 -0.00783421378583 0.0347373187542 0.0342971347272 -0.00132000620943 0.0472641289234 0.0361711494625 -0.00451004551724 -0.0314232259989 0.0354066677392 -0.00278120324947 -0.0295234173536 0.0311564076692 0.00292094191536 -0.0252886787057 0.0373778603971 -0.00468408223242 -0.0252886787057 0.0373778603971 -0.00468408223242 -0.0218933653086 0.0331122018397 -0.000901366525795 -0.0214076433331 0.0391493700445 -0.00776851270348 -0.0351863466203 -0.0765412300825 0.000269046984613 -0.0334314070642 -0.0711332336068 0.00546559365466 -0.0356450267136 -0.0763839781284 0.00708497595042 0.066364556551 -0.058229021728 -0.0376847051084 0.0650048032403 -0.0514324046671 -0.0363247282803 0.0703099220991 -0.0588180907071 -0.0379472970963 -0.0878712534904 0.0469209626317 0.0273304414004 -0.0871415585279 0.0463454276323 0.0139984423295 -0.0873394012451 0.0418514795601 0.0236989371479 -0.0162332504988 -0.0693076774478 -0.0359947793186 -0.0166946779937 -0.0649743825197 -0.0305308811367 -0.0141136925668 -0.0657514110208 -0.0340646244586 -0.0542124435306 0.0460482388735 0.00364887295291 -0.0440319478512 0.0313652604818 0.0016547862906 -0.0400194935501 0.035411670804 -0.00117507681716 -0.0299632400274 -0.0203246790916 -0.026556706056 -0.0351685248315 -0.0100314561278 -0.031182685867 -0.0279543641955 -0.0210831910372 -0.0291056055576 -0.0124928699806 -0.05317331478 -0.032723814249 -0.0138331912458 -0.0603952333331 -0.0342085324228 -0.0177184212953 -0.0485680811107 -0.0308595653623 0.089011117816 -0.049574509263 0.0023703314364 0.0795691013336 -0.0337514281273 0.00747773703188 0.0819737017155 -0.0426090881228 0.00562430731952 -0.0171560104936 -0.0368272177875 -0.0223063975573 -0.0174274221063 -0.0494892522693 -0.0261709243059 -0.0149490861222 -0.0439244024456 -0.0251441132277 0.0584006085992 0.0334684699774 -0.0289356615394 0.0520869679749 0.0365738160908 -0.0220487043262 0.0616061538458 0.0335902385414 -0.0206316392869 0.048808876425 0.0252794381231 0.00892338063568 0.0585609525442 0.0207895357162 0.00992077030241 0.0555975027382 0.0286405310035 0.00528475176543 -0.0325671248138 -0.0150363976136 -0.00584217207506 -0.0295641105622 -0.0183343663812 -0.00392657378688 -0.0363080054522 -0.0172072835267 0.00185445009265 -0.0106726381928 -0.0760597363114 -0.026934184134 -0.0101257953793 -0.0729843974113 -0.0340920165181 -0.010915578343 -0.0659576877952 -0.0315063148737 0.0350222811103 0.011573231779 0.00757634406909 0.0248502586037 0.00308163510635 0.0112843178213 0.0290143638849 -0.000971557688899 0.00837186072022 0.0609687641263 -0.0750585868955 -0.0276746544987 0.0555628426373 -0.0745320767164 -0.0335095077753 0.058990996331 -0.0744717493653 -0.0386376976967 -0.0690660402179 0.0400546975434 0.00533317448571 -0.0736100152135 0.0473809987307 0.00139974115882 -0.0684591978788 0.0459762923419 -0.00223574182019 -0.0262746438384 0.00359826651402 -0.0357828103006 -0.0321104675531 -0.00197183503769 -0.03518948704 -0.0329524688423 0.00574303558096 -0.0349177606404 -0.02261329256 -0.0363039076328 -0.031984012574 -0.0252810381353 -0.0247280094773 -0.0293741747737 -0.0201097112149 -0.0344850867987 -0.0324018150568 -0.0329865068197 -0.039376296103 0.00508400192484 -0.033011417836 -0.0568285062909 0.00476831337437 -0.0304189305753 -0.0502045191824 0.00526972860098 -0.0314232259989 0.0354066677392 -0.00278120324947 -0.035688880831 0.0322657860816 -0.00136898260098 -0.0295234173536 0.0311564076692 0.00292094191536 0.0806738361716 -0.071406044066 -0.00161154090893 0.0821102634072 -0.0652388483286 0.00123241753317 0.0870789960027 -0.071105375886 -0.00195318716578 -0.0132942395285 -0.0358155034482 -0.0281592346728 -0.0123113365844 -0.0451743938029 -0.0291688665748 -0.0163839384913 -0.0424141436815 -0.0313666537404 0.0595832765102 -0.0364168584347 -0.0356253683567 0.0514739230275 -0.0248519349843 -0.0344557315111 0.058284394443 -0.0302759688348 -0.0378637500107 0.00798164401203 -0.0143224736676 0.00208706385456 0.0129595380276 -0.00801198463887 0.00812369771302 0.0036262657959 -0.00963021907955 0.00580563070253 0.0520869679749 0.0365738160908 -0.0220487043262 0.0543802753091 0.0360043048859 -0.0105734793469 0.0616061538458 0.0335902385414 -0.0206316392869 -0.0698440819979 0.0758315250278 0.0191484987736 -0.0680218562484 0.0746396705508 0.0158250704408 -0.0731192678213 0.071150586009 0.0126371867955 0.0511791184545 0.0201472640038 0.0112951202318 0.048808876425 0.0252794381231 0.00892338063568 0.0422127395868 0.0228577423841 0.00999154523015 -0.0878021046519 0.033477794379 0.0395364612341 -0.0918235704303 0.0354779586196 0.0378490537405 -0.0891168415546 0.0341397710145 0.0321871414781 -0.02261329256 -0.0363039076328 -0.031984012574 -0.0177184212953 -0.0485680811107 -0.0308595653623 -0.025938231498 -0.03466161713 -0.0289873387665 -0.0646032914519 0.0391447842121 0.00296345702372 -0.0690660402179 0.0400546975434 0.00533317448571 -0.0631757378578 0.0389323234558 -0.00277568120509 0.00139058486093 -0.0184520073235 -0.00464215269312 -0.00657241046429 -0.0163257047534 -0.00350670074113 -0.0143243018538 -0.0176195204258 -0.00678763026372 -0.0232568588108 -0.0392127744853 0.00331220822409 -0.0287229511887 -0.0406478978693 0.00720230536535 -0.0280799940228 -0.055304210633 0.00326808029786 -0.0218933653086 0.0331122018397 -0.000901366525795 -0.0180822927505 0.0280927624553 0.00312319723889 -0.013514730148 0.0359381325543 -0.00797349400818 -0.0745909139514 0.0392232574522 0.0197847299278 -0.0709582194686 0.0410792566836 0.0236960314214 -0.0791537687182 0.0380757376552 0.0263591650873 0.062893807888 -0.0445155724883 -0.0355053693056 0.0595832765102 -0.0364168584347 -0.0356253683567 0.0629565417767 -0.0314845256507 -0.0355579890311 -0.0647223517299 0.066875346005 0.0136522306129 -0.0507754385471 0.0627422481775 0.000138281728141 -0.0606118962169 0.0685695037246 0.00690156780183 -0.0671208351851 0.0611894726753 0.0208555608988 -0.0660762935877 0.0578988678753 0.0189452208579 -0.068583458662 0.0648453906178 0.0195779465139 -0.00823789276183 -0.0588829554617 -0.0338821560144 -0.0123113365844 -0.0451743938029 -0.0291688665748 -0.00612018536776 -0.0598083510995 -0.0299312565476 -0.012282749638 -0.00724740931764 -2.84041252598e-05 -0.00657241046429 -0.0163257047534 -0.00350670074113 0.0036262657959 -0.00963021907955 0.00580563070253 -0.0421986393631 -0.0759468376637 -0.00256403326057 -0.0370292626321 -0.0664719641209 -0.00097158877179 -0.0353057719767 -0.0717958807945 -0.000896993966307 -0.00435515260324 0.025612950325 -0.0247763358057 -0.00350152561441 0.0275401733816 -0.0214963126928 0.0100417975336 0.0276991538703 -0.0274829212576 -0.00245248852298 -0.0189631339163 -0.0111456979066 -0.0143243018538 -0.0176195204258 -0.00678763026372 -0.0216245427728 -0.0176681727171 -0.0117809949443 0.0917203947902 -0.059632204473 0.00453671533614 0.0874174982309 -0.0661162883043 0.0035401773639 0.0924971923232 -0.0599343143404 0.00196787621826 -0.0167125929147 -0.0282691419125 -0.0229621194303 -0.01405338943 -0.0357937663794 -0.0235176254064 -0.0151859046891 -0.0280584804714 -0.02708007209 0.0728958249092 -0.0278529170901 0.00544968014583 0.0795691013336 -0.0337514281273 0.00747773703188 0.0725733786821 -0.0235378220677 0.00565016036853 -0.0236518606544 -0.0245031807572 -0.0287500377744 -0.0208534952253 -0.0113473143429 -0.0300426613539 -0.0182791948318 -0.019947366789 -0.0281752515584 -0.0160560552031 -0.0138077298179 -0.00380461011082 -0.0183539558202 -0.0179969426244 0.000481826165924 -0.0143243018538 -0.0176195204258 -0.00678763026372 0.0585702136159 -0.00812319666147 0.00419311970472 0.0613831505179 -0.0133591145277 0.00220111338422 0.0565774738789 -0.00623939558864 -0.00480305217206 -0.0278973150998 0.0252974294126 0.00663841702044 -0.0221735388041 0.0248348526657 0.00527667580172 -0.0218933653086 0.0331122018397 -0.000901366525795 0.00896836072206 -0.0178859829903 -0.0177515987307 0.0193834770471 -0.0151846110821 -0.0219733603299 0.0117476321757 -0.018792707473 -0.00953142251819 0.0456950850785 0.0270799733698 -0.0398734845221 0.0529840812087 0.0303148236126 -0.0373462289572 0.051399409771 0.0217050053179 -0.0416473895311 -0.0764897316694 0.0619541816413 0.00187294220086 -0.0847748368979 0.0726360008121 0.00620254734531 -0.0771700739861 0.0674374252558 0.00493047386408 0.0689210742712 0.00494329864159 -0.0121464785188 0.0665407851338 0.00159848632757 -0.00484928302467 0.0656465515494 0.00329640181735 -0.0110076479614 -0.068583458662 0.0648453906178 0.0195779465139 -0.0729605704546 0.0639232620597 0.0233493559062 -0.0671208351851 0.0611894726753 0.0208555608988 0.0690664798021 -0.0187059957534 -0.00380081683397 0.0711212977767 -0.0141868051142 -0.00323123089038 0.0798162445426 -0.0199222583324 -0.0021768680308 -0.0779805928469 0.0700702145696 0.00802535843104 -0.0707638338208 0.0720755532384 0.00634763808921 -0.0656885206699 0.0705391466618 -0.000896229466889 -0.0370292626321 -0.0664719641209 -0.00097158877179 -0.0322233475745 -0.0618353933096 -0.00231006834656 -0.033697348088 -0.0659902095795 0.000380541518098 -0.012700362131 0.00108930072747 0.00499837147072 -0.0152684766799 0.00252701970749 0.00554668903351 -0.0160121638328 -0.00505490787327 0.00223769713193 -0.0353057719767 -0.0717958807945 -0.000896993966307 -0.0351863466203 -0.0765412300825 0.000269046984613 -0.0421986393631 -0.0759468376637 -0.00256403326057 -0.0468597859144 -0.0764623731375 0.00214877328835 -0.0391314774752 -0.0653252974153 0.00145680934656 -0.0421986393631 -0.0759468376637 -0.00256403326057 -0.00303131062537 0.0293679926544 -0.0160501208156 0.00260267360136 0.0286370813847 -0.0185407903045 -0.00350152561441 0.0275401733816 -0.0214963126928 0.0623909682035 -0.0175557024777 -4.21606127929e-05 0.0690664798021 -0.0187059957534 -0.00380081683397 0.0697775110602 -0.0257171951234 0.000200476919417 -0.0844842940569 0.0636154636741 0.0146970963106 -0.0785785987973 0.0702539384365 0.013914372772 -0.0836498662829 0.0665282458067 0.00920514483005 0.0874174982309 -0.0661162883043 0.0035401773639 0.0852778479457 -0.0646592378616 0.00590485380962 0.0852193087339 -0.0724491924047 0.00872989185154 -0.0398653969169 -0.00837041065097 -0.00824570097029 -0.0413943827152 -0.00729784090072 -6.61109006614e-05 -0.0451306067407 0.00219421950169 -0.00086157215992 -0.0680218562484 0.0746396705508 0.0158250704408 -0.0698440819979 0.0758315250278 0.0191484987736 -0.0661604255438 0.0740167275071 0.0206666216254 0.0422127395868 0.0228577423841 0.00999154523015 0.0262714382261 0.024643054232 0.00333152944222 0.0317819230258 0.0191275794059 0.00692741526291 -0.0771700739861 0.0674374252558 0.00493047386408 -0.0721085593104 0.0587044768035 -0.00398772349581 -0.0764897316694 0.0619541816413 0.00187294220086 0.0493391752243 -0.0192066207528 -0.0282236821949 0.0432890281081 -0.00950278807431 -0.0291274245828 0.0461902655661 -0.0180508662015 -0.0307826343924 -0.0164575334638 0.0125575726852 0.00686521595344 -0.0152684766799 0.00252701970749 0.00554668903351 -0.0102530596778 0.0101205231622 0.0068081477657 0.0578673295677 -0.03540468961 -0.0308890789747 0.0631862655282 -0.0409915708005 -0.0296012889594 0.0550032742321 -0.029168618843 -0.0278636794537 -0.0493567548692 0.0630387067795 -0.0120902014896 -0.0568155124784 0.0680090263486 -0.00663143396378 -0.0477432273328 0.0646687820554 -0.00769540853798 0.0666749030352 -0.00735376635566 -0.00298768025823 0.0697568207979 -0.00990358553827 -0.00423297146335 0.0613831505179 -0.0133591145277 0.00220111338422 -0.0321282558143 0.0364747196436 -0.0215411484241 -0.0347637310624 0.0273377392441 -0.0288795996457 -0.0412645675242 0.0296845398843 -0.0242789257318 0.0583206899464 -0.00380795542151 -0.00911643542349 0.0665407851338 0.00159848632757 -0.00484928302467 0.0586783364415 -0.0037231084425 -0.00361427664757 0.0385896004736 0.000291874835966 0.00444744434208 0.0458973273635 -0.000604764616583 0.0111892325804 0.0424964986742 0.00406499719247 0.0110576944426 0.0530218444765 0.0132510503754 0.0114024560899 0.0544707849622 0.00255479058251 0.0128589896485 0.0603620111942 0.0056228893809 0.0125811565667 -0.0232568588108 -0.0392127744853 0.00331220822409 -0.0252340473235 -0.0395757183433 -0.00116509769578 -0.022982083261 -0.0271414145827 -3.11253916152e-06 0.0374021679163 0.0269114822149 0.00774644361809 0.0336626619101 0.0301720779389 0.00381930125877 0.0262714382261 0.024643054232 0.00333152944222 -0.0698440819979 0.0758315250278 0.0191484987736 -0.0723753124475 0.0711080506444 0.0155806122348 -0.0661604255438 0.0740167275071 0.0206666216254 0.0317371711135 -0.00692039029673 -0.0261175371706 0.0355566330254 -0.0077797062695 -0.0261449292302 0.0326762907207 -0.00954702217132 -0.0169461015612 0.0839628651738 -0.0313502401114 0.00374555471353 0.0813561230898 -0.0232167206705 0.00461526820436 0.0787196084857 -0.0281961280853 0.00396182155237 0.0694160535932 0.0198294073343 -0.0330271050334 0.065537057817 0.0151417842135 -0.0360496491194 0.0649568662047 0.0234411917627 -0.0345332249999 -0.0754706263542 0.057246144861 0.0283697806299 -0.0739856958389 0.0511276461184 0.0285730604082 -0.070197686553 0.0612765774131 0.0268817096949 -0.00657241046429 -0.0163257047534 -0.00350670074113 -0.016403356567 -0.00939153227955 -0.00214911578223 -0.0160560552031 -0.0138077298179 -0.00380461011082 0.0547827705741 -0.0236574988812 -0.0368034355342 0.058284394443 -0.0302759688348 -0.0378637500107 0.0514739230275 -0.0248519349843 -0.0344557315111 0.0821102634072 -0.0652388483286 0.00123241753317 0.0806738361716 -0.071406044066 -0.00161154090893 0.0769691392779 -0.075030490756 0.002559523331 -0.00435515260324 0.025612950325 -0.0247763358057 -0.012165626511 0.0265315938741 -0.0258189272135 -0.00350152561441 0.0275401733816 -0.0214963126928 0.0514739230275 -0.0248519349843 -0.0344557315111 0.0563069395721 -0.0334836132824 -0.0334919802845 0.0461902655661 -0.0180508662015 -0.0307826343924 0.0725204646587 -0.00817401800305 -0.000920072081499 0.0734314844012 -0.0137593969703 -0.0012326046126 0.0697568207979 -0.00990358553827 -0.00423297146335 0.073333054781 -0.0605282187462 -0.0343145728111 0.068005733192 -0.0399323999882 -0.0337682925165 0.0714298635721 -0.0534965991974 -0.0324478931725 -0.0173668675125 -0.0188725441694 -0.0200483836234 -0.0167125929147 -0.0282691419125 -0.0229621194303 -0.015117011033 -0.0188090614974 -0.0260683037341 0.0782531201839 -0.0722363963723 0.00564285600558 0.0852193087339 -0.0724491924047 0.00872989185154 0.0814371109009 -0.0711335614324 0.00815863255411 -0.0095995971933 -0.0609665364027 -0.0261474959552 -0.0136914439499 -0.0546173118055 -0.0263203363866 -0.0150378849357 -0.0664253011346 -0.026939317584 0.014073533006 -0.000268704665359 0.0117724873126 0.0197910871357 -0.00383032113314 0.00980013236403 0.0248502586037 0.00308163510635 0.0112843178213 0.00936001725495 -0.00345246354118 -0.0368482433259 0.0102474931628 0.0033322009258 -0.0393172241747 0.0182611495256 -0.000761016330216 -0.0385480634868 0.0512403547764 -0.00441910745576 -0.0253982823342 0.0556262694299 -0.00370851811022 -0.0167932603508 0.050373673439 -0.00361777818762 -0.0148283494636 -0.0664762184024 0.0484478063881 0.0224707052112 -0.0643574744463 0.0411904193461 0.0170047152787 -0.0617349892855 0.0479026585817 0.0140177402645 -0.0336843542755 -0.0225331783295 0.00486868014559 -0.0361147299409 -0.0120508149266 0.00691540632397 -0.0363080054522 -0.0172072835267 0.00185445009265 -0.012165626511 0.0265315938741 -0.0258189272135 -0.0111597320065 0.0221810676157 -0.0287142917514 -0.0190910883248 0.0310631301254 -0.0254277605563 -0.0803267359734 0.0397947318852 0.0164727605879 -0.0871415585279 0.0463454276323 0.0139984423295 -0.0825153067708 0.041629076004 0.009376286529 0.0529840812087 0.0303148236126 -0.0373462289572 0.0469395145774 0.0357567593455 -0.0306289978325 0.0584006085992 0.0334684699774 -0.0289356615394 0.0719613134861 -0.0083319125697 0.00354015408084 0.0682200118899 -0.00788477621973 0.00767027726397 0.0705670714378 -0.0142336087301 0.0080666737631 -0.0391113087535 0.0111347045749 -0.0334663800895 -0.0314821787179 0.0163094382733 -0.0335103832185 -0.0329524688423 0.00574303558096 -0.0349177606404 0.0720914751291 0.00546024832875 -0.010403117165 0.0743348672986 0.00406845519319 -0.00470197200775 0.0725204646587 -0.00817401800305 -0.000920072081499 -0.0876308903098 0.0384379811585 0.0428250767291 -0.084793664515 0.0397632867098 0.0391925126314 -0.0824304521084 0.0437800884247 0.0388801582158 0.0420701652765 -0.00905739609152 -0.0347143113613 0.0447440594435 -0.0021612313576 -0.0399010665715 0.0477273277938 -0.00715120695531 -0.0378671810031 0.070414699614 -0.052166569978 -0.0348446667194 0.068005733192 -0.0399323999882 -0.0337682925165 0.073333054781 -0.0605282187462 -0.0343145728111 -0.0351685248315 -0.0100314561278 -0.031182685867 -0.0299632400274 -0.0203246790916 -0.026556706056 -0.0342719815671 -0.0143612474203 -0.0263750851154 -0.015117011033 -0.0188090614974 -0.0260683037341 -0.0182791948318 -0.019947366789 -0.0281752515584 -0.0208534952253 -0.0113473143429 -0.0300426613539 0.0818465650082 -0.0646916031837 0.004601073917 0.0809005796909 -0.0487979911268 0.00395165523514 0.0821102634072 -0.0652388483286 0.00123241753317 -0.0215143859386 -0.019579783082 0.00190126814414 -0.022982083261 -0.0271414145827 -3.11253916152e-06 -0.0222899951041 -0.0185299292207 -0.00168474740349 0.0106190349907 0.02950688079 -0.021063869819 0.00980327930301 0.028790736571 -0.00937963183969 0.0197386574 0.031497310847 -0.0197209063917 0.0627965405583 -0.0644452646375 -0.0340751446784 0.0635530352592 -0.0652642175555 -0.0305935442448 0.0649472996593 -0.0564471706748 -0.0301647242159 -0.0386477708817 0.0575842261314 -0.0097325630486 -0.0214076433331 0.0391493700445 -0.00776851270348 -0.0194173324853 0.0395698286593 -0.0113779669628 -0.0664762184024 0.0484478063881 0.0224707052112 -0.0690960809588 0.0526289157569 0.0247765686363 -0.0712874382734 0.0450919307768 0.0275897365063 0.0650048032403 -0.0514324046671 -0.0363247282803 0.0644002556801 -0.0638302341104 -0.0359592176974 0.063760265708 -0.0543169416487 -0.0341284386814 -0.0707638338208 0.0720755532384 0.00634763808921 -0.0779805928469 0.0700702145696 0.00802535843104 -0.0731192678213 0.071150586009 0.0126371867955 -0.0292823426425 -0.016650011763 -0.0205444768071 -0.0268499869853 -0.016901101917 -0.00799196213484 -0.0325671248138 -0.0150363976136 -0.00584217207506 0.0574748292565 -0.0728332474828 -0.0297978837043 0.0650694146752 -0.0661949291825 -0.0290601216257 0.0635530352592 -0.0652642175555 -0.0305935442448 0.0493391752243 -0.0192066207528 -0.0282236821949 0.0550032742321 -0.029168618843 -0.0278636794537 0.0577097833157 -0.0276579707861 -0.0262848697603 0.0610778927803 -0.0446138717234 -0.033351752907 0.0650048032403 -0.0514324046671 -0.0363247282803 0.063760265708 -0.0543169416487 -0.0341284386814 0.00531596504152 0.00230240589008 0.0111257759854 0.00759891513735 -0.00405428977683 0.00967596191913 0.014073533006 -0.000268704665359 0.0117724873126 -0.0363510511816 -0.059165596962 -0.00133417372126 -0.0300818383694 -0.0526309125125 -0.000229920042329 -0.0322233475745 -0.0618353933096 -0.00231006834656 -0.0216245427728 -0.0176681727171 -0.0117809949443 -0.0268499869853 -0.016901101917 -0.00799196213484 -0.0292823426425 -0.016650011763 -0.0205444768071 0.0226106531918 0.0281752999872 -0.00177787418943 0.0074149183929 0.0266806911677 -0.00382403749973 0.00612076325342 0.0238218400627 0.00119427102618 -0.0529617629945 0.027991278097 -0.0167360249907 -0.0616058968008 0.0379795208573 -0.00707599101588 -0.0594911910594 0.0402323678136 -0.0138589777052 -0.046883624047 0.00825335178524 -0.0246345344931 -0.0444579236209 0.00304091628641 -0.0277560744435 -0.0455527119339 0.00271498505026 -0.0177962481976 -0.0325671248138 -0.0150363976136 -0.00584217207506 -0.0363425649703 -0.0124280676246 -0.00762832257897 -0.0342719815671 -0.0143612474203 -0.0263750851154 0.023355057463 0.00308697414584 -0.0384194366634 0.0249150972813 0.0107660228387 -0.0397206582129 0.0312596708536 0.00204978929833 -0.0341790989041 0.0621104352176 0.00453598890454 -0.0360744036734 0.0566076412797 0.00240896618925 -0.0403126217425 0.0601614937186 0.00885820761323 -0.0400737561285 -0.0721085593104 0.0587044768035 -0.00398772349581 -0.0642814710736 0.0499867610633 -0.00973032042384 -0.074530698359 0.0564152859151 0.000770429789554 0.0458527319133 0.0086536090821 -0.0422105453908 0.0447440594435 -0.0021612313576 -0.0399010665715 0.0384086482227 0.00514970067888 -0.0410340279341 -0.0252824239433 -0.0285316351801 -0.0223765242845 -0.0292823426425 -0.016650011763 -0.0205444768071 -0.0243194364011 -0.0365216545761 -0.0246309749782 -0.0364621728659 -4.06305734941e-05 0.00923090986907 -0.0347196757793 0.00916986633092 0.0111047113314 -0.0409355126321 0.00761891668662 0.00908278673887 -0.0253939889371 -0.00600079027936 -0.0348485857248 -0.0196293834597 -0.0012800822733 -0.0339007712901 -0.0207476206124 -0.00821672473103 -0.0326586216688 -0.0680031627417 0.0388116128743 0.0118984086439 -0.0615916401148 0.041047938168 0.00514492439106 -0.0639205127954 0.0411553606391 0.00809910707176 0.0855331420898 -0.0662355422974 -0.000724932178855 0.0870789960027 -0.071105375886 -0.00195318716578 0.0821102634072 -0.0652388483286 0.00123241753317 -0.0149209909141 -0.0729868486524 -0.0253179427236 -0.0106726381928 -0.0760597363114 -0.026934184134 -0.0150378849357 -0.0664253011346 -0.026939317584 0.0585609525442 0.0207895357162 0.00992077030241 0.0511791184545 0.0201472640038 0.0112951202318 0.0603620111942 0.0056228893809 0.0125811565667 -0.0209524612874 -0.0299015939236 -0.0216957069933 -0.0251056849957 -0.0178923550993 -0.0185515768826 -0.0252824239433 -0.0285316351801 -0.0223765242845 -0.070197686553 0.0612765774131 0.0268817096949 -0.0671717971563 0.0575826056302 0.0252393428236 -0.0671208351851 0.0611894726753 0.0208555608988 -0.0796836540103 0.0613558962941 0.0230027772486 -0.0754706263542 0.057246144861 0.0283697806299 -0.0729605704546 0.0639232620597 0.0233493559062 -0.0281191021204 0.0276504568756 -0.0284133274108 -0.0200255811214 0.0362959876657 -0.0195930022746 -0.0234806258231 0.0277767758816 -0.028870517388 0.0226106531918 0.0281752999872 -0.00177787418943 0.0262714382261 0.024643054232 0.00333152944222 0.0336626619101 0.0301720779389 0.00381930125877 -0.0279543641955 -0.0210831910372 -0.0291056055576 -0.0277498979121 -0.0136683974415 -0.0326979234815 -0.0252810381353 -0.0247280094773 -0.0293741747737 0.0564444027841 -0.00904699414968 -0.0371534638107 0.0504257716238 -0.016582140699 -0.0392883382738 0.0477273277938 -0.00715120695531 -0.0378671810031 -0.0582275763154 0.0486685633659 -0.0147868087515 -0.0562584511936 0.0401268824935 -0.0175458882004 -0.0642814710736 0.0499867610633 -0.00973032042384 -0.00667092064396 0.0126474965364 -0.0336418747902 -0.0122720496729 0.00743707921356 -0.0320300944149 -0.0121780969203 0.0159933697432 -0.0321105457842 -0.084884122014 0.054450109601 0.0266954693943 -0.0864301398396 0.0525974109769 0.0242326129228 -0.0878712534904 0.0469209626317 0.0273304414004 -0.0520143918693 0.0224781483412 -0.0120825413615 -0.0480200089514 0.0139896376058 -0.00342765916139 -0.051504354924 0.0203009396791 -0.0043327328749 -0.0243194364011 -0.0365216545761 -0.0246309749782 -0.0212532021105 -0.0387861654162 -0.0227633845061 -0.0252824239433 -0.0285316351801 -0.0223765242845 -0.0200255811214 0.0362959876657 -0.0195930022746 -0.0272648502141 0.0430674515665 -0.0166629143059 -0.0210811626166 0.0406454503536 -0.0149188544601 0.0578673295677 -0.03540468961 -0.0308890789747 0.0550032742321 -0.029168618843 -0.0278636794537 0.0461902655661 -0.0180508662015 -0.0307826343924 0.0816290304065 -0.0504989773035 0.00101880810689 0.0821102634072 -0.0652388483286 0.00123241753317 0.0809005796909 -0.0487979911268 0.00395165523514 -0.0873394012451 0.0418514795601 0.0236989371479 -0.0891168415546 0.0341397710145 0.0321871414781 -0.0914800092578 0.0378939323127 0.0321716368198 0.0424964986742 0.00406499719247 0.0110576944426 0.0405975058675 0.0136088356376 0.009625967592 0.0378097891808 0.00806960370392 0.00718982517719 0.066437445581 -0.0657137334347 -0.0362850166857 0.0668106079102 -0.070455878973 -0.0377877689898 0.058990996331 -0.0744717493653 -0.0386376976967 0.0393960885704 -0.00669425539672 -0.0340571328998 0.0420701652765 -0.00905739609152 -0.0347143113613 0.0432890281081 -0.00950278807431 -0.0291274245828 -0.010915578343 -0.0659576877952 -0.0315063148737 -0.0101257953793 -0.0729843974113 -0.0340920165181 -0.0141136925668 -0.0657514110208 -0.0340646244586 -0.0781400799751 0.0399436429143 0.0323086194694 -0.0822595283389 0.0367101840675 0.0404899641871 -0.0834547281265 0.0319089256227 0.0349037349224 0.0561687573791 -0.00828116200864 -0.00418273126706 0.0533758401871 -0.00816207565367 0.00332604441792 0.0495941489935 -0.00505510251969 0.00281282933429 -0.0121780969203 0.0159933697432 -0.0321105457842 -0.0181399043649 0.0110053224489 -0.0338392443955 -0.0245153158903 0.0113811995834 -0.0345406457782 0.0629565417767 -0.0314845256507 -0.0355579890311 0.0595832765102 -0.0364168584347 -0.0356253683567 0.058284394443 -0.0302759688348 -0.0378637500107 -0.0878712534904 0.0469209626317 0.0273304414004 -0.0864301398396 0.0525974109769 0.0242326129228 -0.0870704501867 0.052732732147 0.0148491580039 0.0725204646587 -0.00817401800305 -0.000920072081499 0.0697568207979 -0.00990358553827 -0.00423297146335 0.0708144679666 0.000992352026515 -0.00854138284922 0.0798162445426 -0.0199222583324 -0.0021768680308 0.0825529024005 -0.0241261273623 -0.00263262726367 0.0721606612206 -0.022786539048 -0.00311841559596 -0.0418213754892 -0.00742403836921 -0.0206348095089 -0.0393666550517 -0.0106545481831 -0.0230054780841 -0.0398653969169 -0.00837041065097 -0.00824570097029 -0.0867733284831 0.0609648488462 0.0183455236256 -0.0844842940569 0.0636154636741 0.0146970963106 -0.0889886766672 0.0597609467804 0.0147773399949 0.0672499090433 -0.0154510131106 0.00984042230994 0.0728958249092 -0.0278529170901 0.00544968014583 0.0725733786821 -0.0235378220677 0.00565016036853 0.0816290304065 -0.0504989773035 0.00101880810689 0.0784853920341 -0.0390462353826 -2.51326264333e-06 0.0837335810065 -0.0474399290979 -0.000192326551769 0.0720914751291 0.00546024832875 -0.010403117165 0.0713142603636 0.0189655330032 -0.0151191568002 0.0735007673502 0.0154050234705 -0.0108875501901 0.0672754794359 -0.0748537257314 -0.028308160603 0.058990996331 -0.0744717493653 -0.0386376976967 0.0696968138218 -0.0747989788651 -0.0332351587713 0.0795691013336 -0.0337514281273 0.00747773703188 0.0709631517529 -0.0282764472067 0.0042789876461 0.0769165381789 -0.0372788608074 0.004157373216 -0.0791537687182 0.0380757376552 0.0263591650873 -0.0891168415546 0.0341397710145 0.0321871414781 -0.0846682190895 0.0387630797923 0.0249170754105 -0.0379135832191 -0.00708554778248 0.00403723120689 -0.0368985123932 -0.0142811005935 0.00302317948081 -0.0361147299409 -0.0120508149266 0.00691540632397 -0.0342719815671 -0.0143612474203 -0.0263750851154 -0.0414947420359 -0.00295710819773 -0.0225406978279 -0.0383309051394 -0.00495197204873 -0.0253514498472 0.0292802155018 -0.012616426684 -0.0106529798359 0.0200015287846 -0.0166918933392 -0.0100967604667 0.0193834770471 -0.0151846110821 -0.0219733603299 -0.0257243346423 -0.0339680574834 0.00464337132871 -0.0306760501117 -0.0296483207494 0.00481163570657 -0.0287229511887 -0.0406478978693 0.00720230536535 -0.0671208351851 0.0611894726753 0.0208555608988 -0.0729605704546 0.0639232620597 0.0233493559062 -0.070197686553 0.0612765774131 0.0268817096949 -0.0386477708817 0.0575842261314 -0.0097325630486 -0.0210811626166 0.0406454503536 -0.0149188544601 -0.0379568003118 0.0557544454932 -0.013513436541 -0.0690660402179 0.0400546975434 0.00533317448571 -0.0646032914519 0.0391447842121 0.00296345702372 -0.0737345814705 0.038439001888 0.0121840443462 -0.00350152561441 0.0275401733816 -0.0214963126928 -0.0089406510815 0.0311568155885 -0.0189374648035 -0.00303131062537 0.0293679926544 -0.0160501208156 -0.0457198508084 0.01298922766 -0.0289378613234 -0.046883624047 0.00825335178524 -0.0246345344931 -0.0483878850937 0.0177123211324 -0.0199927464128 -0.0712874382734 0.0450919307768 0.0275897365063 -0.0643574744463 0.0411904193461 0.0170047152787 -0.0664762184024 0.0484478063881 0.0224707052112 0.0754076316953 -0.0163155626506 0.0023912510369 0.0831556394696 -0.0222345832735 0.00146252510604 0.0798162445426 -0.0199222583324 -0.0021768680308 0.0821102634072 -0.0652388483286 0.00123241753317 0.0769691392779 -0.075030490756 0.002559523331 0.0818465650082 -0.0646916031837 0.004601073917 -0.00962803792208 0.0256944280118 1.13362448246e-05 -0.00275399489328 0.0223833154887 0.00140674877912 -0.00179908366408 0.0287952888757 -0.0084942439571 -0.0684591978788 0.0459762923419 -0.00223574182019 -0.074530698359 0.0564152859151 0.000770429789554 -0.0652352571487 0.0470017306507 -0.00540793221444 0.0200015287846 -0.0166918933392 -0.0100967604667 0.0233577005565 -0.0130151677877 -0.00125345273409 0.0148993665352 -0.016240587458 -0.00226025399752 0.0572973936796 0.0331504121423 -0.000841111701448 0.0543802753091 0.0360043048859 -0.0105734793469 0.0472641289234 0.0361711494625 -0.00451004551724 0.0679226443172 0.029033575207 -0.0159582849592 0.0709100887179 0.0228983853012 -0.0246697273105 0.0668411776423 0.0276774372905 -0.0293406844139 -0.0347637310624 0.0273377392441 -0.0288795996457 -0.0394592247903 0.021167581901 -0.0313589386642 -0.0412645675242 0.0296845398843 -0.0242789257318 0.0720914751291 0.00546024832875 -0.010403117165 0.0687282457948 0.0103019243106 -0.0152066750452 0.0713142603636 0.0189655330032 -0.0151191568002 -0.0317054688931 -0.00275706243701 0.0117793781683 -0.030961079523 0.00549497501925 0.0111165465787 -0.0364621728659 -4.06305734941e-05 0.00923090986907 0.073333054781 -0.0605282187462 -0.0343145728111 0.0703099220991 -0.0588180907071 -0.0379472970963 0.070414699614 -0.052166569978 -0.0348446667194 -0.0222899951041 -0.0185299292207 -0.00168474740349 -0.0281935930252 -0.027320895344 -0.00371586484835 -0.0262299366295 -0.017805961892 -0.00452885916457 0.0447547212243 0.0325945727527 0.00325110368431 0.0555975027382 0.0286405310035 0.00528475176543 0.0572973936796 0.0331504121423 -0.000841111701448 0.0709631517529 -0.0282764472067 0.0042789876461 0.0697775110602 -0.0257171951234 0.000200476919417 0.0784853920341 -0.0390462353826 -2.51326264333e-06 0.0631862655282 -0.0409915708005 -0.0296012889594 0.0665984004736 -0.0429504625499 -0.0306054838002 0.0656110197306 -0.0270528197289 -0.0288560781628 -0.0150378849357 -0.0664253011346 -0.026939317584 -0.0106726381928 -0.0760597363114 -0.026934184134 -0.0120546650141 -0.0671373382211 -0.028148625046 -0.0120546650141 -0.0671373382211 -0.028148625046 -0.010915578343 -0.0659576877952 -0.0315063148737 -0.00612018536776 -0.0598083510995 -0.0299312565476 0.0719613134861 -0.0083319125697 0.00354015408084 0.0704541727901 -0.00372631778009 0.00695726461709 0.0682200118899 -0.00788477621973 0.00767027726397 -0.0503109395504 0.0511616170406 -0.0167050715536 -0.0509763434529 0.0460034161806 -0.0177591200918 -0.0572370886803 0.056576654315 -0.0142777692527 0.0767389982939 -0.0762002691627 0.00715722655877 0.0782531201839 -0.0722363963723 0.00564285600558 0.0769691392779 -0.075030490756 0.002559523331 0.0400369912386 -0.00509023061022 -0.0284015871584 0.0393960885704 -0.00669425539672 -0.0340571328998 0.0432890281081 -0.00950278807431 -0.0291274245828 -0.00667092064396 0.0126474965364 -0.0336418747902 0.0116761699319 0.0192524939775 -0.0374199561775 0.00984027050436 0.0123339463025 -0.039398021996 -0.00179908366408 0.0287952888757 -0.0084942439571 -0.00275399489328 0.0223833154887 0.00140674877912 0.00612076325342 0.0238218400627 0.00119427102618 -0.0889886766672 0.0597609467804 0.0147773399949 -0.0864301398396 0.0525974109769 0.0242326129228 -0.0867733284831 0.0609648488462 0.0183455236256 0.0858451649547 -0.0757835879922 -0.00102130440064 0.0870789960027 -0.071105375886 -0.00195318716578 0.0891577154398 -0.0754422545433 0.00495696906 0.0668411776423 0.0276774372905 -0.0293406844139 0.0649568662047 0.0234411917627 -0.0345332249999 0.0579114630818 0.0292274262756 -0.0363327749074 0.0102474931628 0.0033322009258 -0.0393172241747 0.00984027050436 0.0123339463025 -0.039398021996 0.0173371694982 0.00796426646411 -0.0401615314186 -0.0228501725942 -0.016540652141 0.00465866690502 -0.0230524111539 -0.0281649492681 0.00343656959012 -0.0215143859386 -0.019579783082 0.00190126814414 0.0550032742321 -0.029168618843 -0.0278636794537 0.0493391752243 -0.0192066207528 -0.0282236821949 0.0461902655661 -0.0180508662015 -0.0307826343924 0.0197386574 0.031497310847 -0.0197209063917 0.00980327930301 0.028790736571 -0.00937963183969 0.0206307601184 0.0312986150384 -0.0104876076803 -0.0635816827416 0.0559310466051 0.0108306938782 -0.0660762935877 0.0578988678753 0.0189452208579 -0.0617349892855 0.0479026585817 0.0140177402645 -0.00962803792208 0.0256944280118 1.13362448246e-05 -0.00733371544629 0.0176978223026 0.00535471225157 -0.00275399489328 0.0223833154887 0.00140674877912 -0.0871415585279 0.0463454276323 0.0139984423295 -0.0878712534904 0.0469209626317 0.0273304414004 -0.0870704501867 0.052732732147 0.0148491580039 -0.0379568003118 0.0557544454932 -0.013513436541 -0.0477432273328 0.0646687820554 -0.00769540853798 -0.0386477708817 0.0575842261314 -0.0097325630486 -0.0262746438384 0.00359826651402 -0.0357828103006 -0.0196293834597 -0.0012800822733 -0.0339007712901 -0.0253939889371 -0.00600079027936 -0.0348485857248 0.062893807888 -0.0445155724883 -0.0355053693056 0.0650048032403 -0.0514324046671 -0.0363247282803 0.0610778927803 -0.0446138717234 -0.033351752907 0.0129595380276 -0.00801198463887 0.00812369771302 0.00759891513735 -0.00405428977683 0.00967596191913 0.0036262657959 -0.00963021907955 0.00580563070253 0.0917203947902 -0.059632204473 0.00453671533614 0.089011117816 -0.049574509263 0.0023703314364 0.0842658951879 -0.0518600940704 0.00558784976602 0.0206307601184 0.0312986150384 -0.0104876076803 0.00980327930301 0.028790736571 -0.00937963183969 0.0210444331169 0.030273001641 -0.00706696789712 -0.00338091235608 0.0199865065515 -0.0312582217157 -0.0121780969203 0.0159933697432 -0.0321105457842 -0.0111597320065 0.0221810676157 -0.0287142917514 -0.0295641105622 -0.0183343663812 -0.00392657378688 -0.0281935930252 -0.027320895344 -0.00371586484835 -0.0334986448288 -0.0282570403069 -0.00100804551039 0.0159195382148 0.00791420787573 0.0129859792069 0.00083865004126 0.00723583996296 0.0104546826333 0.00531596504152 0.00230240589008 0.0111257759854 -0.0300818383694 -0.0526309125125 -0.000229920042329 -0.0363510511816 -0.059165596962 -0.00133417372126 -0.0345421843231 -0.0537865906954 -3.66152089555e-05 -0.0454866327345 0.0472589023411 0.000885553658009 -0.0614165142179 0.0504314303398 0.0064328792505 -0.0542124435306 0.0460482388735 0.00364887295291 -0.0363080054522 -0.0172072835267 0.00185445009265 -0.0342002138495 -0.0296476446092 0.0025749206543 -0.0336843542755 -0.0225331783295 0.00486868014559 0.00794281996787 -0.0124184312299 -0.0286089126021 0.00573587883264 -0.00811646040529 -0.0328484363854 0.0144752385095 -0.0070848762989 -0.034492328763 0.00139058486093 -0.0184520073235 -0.00464215269312 0.00798164401203 -0.0143224736676 0.00208706385456 -0.00657241046429 -0.0163257047534 -0.00350670074113 0.0422127395868 0.0228577423841 0.00999154523015 0.0374021679163 0.0269114822149 0.00774644361809 0.0262714382261 0.024643054232 0.00333152944222 -0.0317054688931 -0.00275706243701 0.0117793781683 -0.0282046999782 -0.011605437845 0.00933898519725 -0.0247759241611 -0.0095595670864 0.010169220157 0.0600458458066 -0.00507800886407 -0.0249140188098 0.0556262694299 -0.00370851811022 -0.0167932603508 0.0512403547764 -0.00441910745576 -0.0253982823342 -0.0214076433331 0.0391493700445 -0.00776851270348 -0.0322041623294 0.0489787906408 -0.00726023875177 -0.0252886787057 0.0373778603971 -0.00468408223242 -0.0366347320378 0.0017704871716 -0.0322802253067 -0.0381523445249 -0.00304430956021 -0.029428480193 -0.0409325622022 0.00616488046944 -0.0320984274149 -0.0914800092578 0.0378939323127 0.0321716368198 -0.0891168415546 0.0341397710145 0.0321871414781 -0.0918235704303 0.0354779586196 0.0378490537405 -0.0194173324853 0.0395698286593 -0.0113779669628 -0.00303131062537 0.0293679926544 -0.0160501208156 -0.0210811626166 0.0406454503536 -0.0149188544601 -0.0143243018538 -0.0176195204258 -0.00678763026372 -0.0222899951041 -0.0185299292207 -0.00168474740349 -0.0262299366295 -0.017805961892 -0.00452885916457 0.0148993665352 -0.016240587458 -0.00226025399752 0.0163268223405 -0.0114426901564 0.00449271220714 0.00798164401203 -0.0143224736676 0.00208706385456 0.0458527319133 0.0086536090821 -0.0422105453908 0.0504528395832 0.0169036872685 -0.0417337194085 0.0504876486957 0.00247842236422 -0.0409079678357 0.0418007522821 -0.00242996658199 -0.00372027652338 0.0395265072584 -0.00346563779749 -0.0100429235026 0.0438971333206 -0.00236639939249 -0.0157472789288 -0.0370292626321 -0.0664719641209 -0.00097158877179 -0.0421986393631 -0.0759468376637 -0.00256403326057 -0.0391314774752 -0.0653252974153 0.00145680934656 0.0197910871357 -0.00383032113314 0.00980013236403 0.0222891252488 -0.0072523932904 0.00697265705094 0.0290143638849 -0.000971557688899 0.00837186072022 0.00347416335717 0.00105483434163 -0.0370849557221 0.0102474931628 0.0033322009258 -0.0393172241747 0.00936001725495 -0.00345246354118 -0.0368482433259 0.0319815389812 0.0352998748422 -0.0105315605178 0.0210444331169 0.030273001641 -0.00706696789712 0.0347373187542 0.0342971347272 -0.00132000620943 -0.0287229511887 -0.0406478978693 0.00720230536535 -0.0232568588108 -0.0392127744853 0.00331220822409 -0.0257243346423 -0.0339680574834 0.00464337132871 0.0495941489935 -0.00505510251969 0.00281282933429 0.0584446676075 -0.0131367407739 0.00669920956716 0.054339889437 -0.00603239936754 0.00829239841551 0.0350222811103 0.011573231779 0.00757634406909 0.0317819230258 0.0191275794059 0.00692741526291 0.0278224293143 0.0121009163558 0.0102275209501 0.0603620111942 0.0056228893809 0.0125811565667 0.0511791184545 0.0201472640038 0.0112951202318 0.0530218444765 0.0132510503754 0.0114024560899 0.0706675350666 0.0171718429774 0.00147831998765 0.0680408999324 0.0129547277465 0.00671541178599 0.0665930733085 -0.00253646099009 0.00908811856061 0.0769819021225 -0.0300572067499 -0.00352066406049 0.0837335810065 -0.0474399290979 -0.000192326551769 0.075530923903 -0.0326715037227 -0.00224941363558 0.0713142603636 0.0189655330032 -0.0151191568002 0.0709100887179 0.0228983853012 -0.0246697273105 0.0696674659848 0.0262376572937 -0.0128174172714 -0.0647223517299 0.066875346005 0.0136522306129 -0.0649350732565 0.0619430281222 0.0139570971951 -0.0497838333249 0.0559922680259 0.00280405301601 0.0605648122728 -0.0226184297353 -0.0335885249078 0.0629565417767 -0.0314845256507 -0.0355579890311 0.0575529150665 -0.022210219875 -0.0367079302669 -0.0334314070642 -0.0711332336068 0.00546559365466 -0.0349772572517 -0.0686603486538 0.00445972103626 -0.0377826243639 -0.0715468376875 0.00810029637069 -0.0356382876635 -0.0533351935446 0.00276124686934 -0.0329865068197 -0.039376296103 0.00508400192484 -0.0342002138495 -0.0296476446092 0.0025749206543 -0.0803875476122 0.0608413852751 0.00552876945585 -0.0859415605664 0.0709468647838 0.00737344101071 -0.0764897316694 0.0619541816413 0.00187294220086 -0.0656885206699 0.0705391466618 -0.000896229466889 -0.0699571371078 0.0646914690733 -0.00456732325256 -0.0779805928469 0.0700702145696 0.00802535843104 -0.0684591978788 0.0459762923419 -0.00223574182019 -0.0616058968008 0.0379795208573 -0.00707599101588 -0.0631757378578 0.0389323234558 -0.00277568120509 0.0578673295677 -0.03540468961 -0.0308890789747 0.0664181634784 -0.0537509098649 -0.0296265520155 0.0631862655282 -0.0409915708005 -0.0296012889594 -0.0123113365844 -0.0451743938029 -0.0291688665748 -0.0124928699806 -0.05317331478 -0.032723814249 -0.0163839384913 -0.0424141436815 -0.0313666537404 0.0380213819444 -0.000848870549817 -0.0383270718157 0.0384086482227 0.00514970067888 -0.0410340279341 0.0447440594435 -0.0021612313576 -0.0399010665715 0.0649568662047 0.0234411917627 -0.0345332249999 0.065537057817 0.0151417842135 -0.0360496491194 0.060431715101 0.0229300074279 -0.0391151756048 0.0703099220991 -0.0588180907071 -0.0379472970963 0.0644002556801 -0.0638302341104 -0.0359592176974 0.066364556551 -0.058229021728 -0.0376847051084 -0.0089406510815 0.0311568155885 -0.0189374648035 -0.0200255811214 0.0362959876657 -0.0195930022746 -0.0210811626166 0.0406454503536 -0.0149188544601 -0.0194173324853 0.0395698286593 -0.0113779669628 -0.013514730148 0.0359381325543 -0.00797349400818 -0.00303131062537 0.0293679926544 -0.0160501208156 -0.0381806567311 0.0407792925835 -0.019973538816 -0.0412535518408 0.055965680629 -0.0148494942114 -0.0272648502141 0.0430674515665 -0.0166629143059 0.0705670714378 -0.0142336087301 0.0080666737631 0.0672499090433 -0.0154510131106 0.00984042230994 0.0725733786821 -0.0235378220677 0.00565016036853 0.0689210742712 0.00494329864159 -0.0121464785188 0.0687282457948 0.0103019243106 -0.0152066750452 0.0720914751291 0.00546024832875 -0.010403117165 0.0627590343356 0.0254758372903 0.00493016792461 0.0572973936796 0.0331504121423 -0.000841111701448 0.0555975027382 0.0286405310035 0.00528475176543 0.0301524624228 -0.00372725259513 -0.0304073672742 0.0312596708536 0.00204978929833 -0.0341790989041 0.0380213819444 -0.000848870549817 -0.0383270718157 0.0601614937186 0.00885820761323 -0.0400737561285 0.0566076412797 0.00240896618925 -0.0403126217425 0.0554471611977 0.0115022500977 -0.042177259922 -0.0391113087535 0.0111347045749 -0.0334663800895 -0.0457198508084 0.01298922766 -0.0289378613234 -0.043179307133 0.0188505705446 -0.0299622882158 0.0613831505179 -0.0133591145277 0.00220111338422 0.0643664449453 -0.0178353134543 0.000139734620461 0.0565774738789 -0.00623939558864 -0.00480305217206 -0.0167125929147 -0.0282691419125 -0.0229621194303 -0.0151859046891 -0.0280584804714 -0.02708007209 -0.015117011033 -0.0188090614974 -0.0260683037341 0.0210444331169 0.030273001641 -0.00706696789712 0.0226106531918 0.0281752999872 -0.00177787418943 0.0347373187542 0.0342971347272 -0.00132000620943 -0.0822595283389 0.0367101840675 0.0404899641871 -0.0824304521084 0.0437800884247 0.0388801582158 -0.084793664515 0.0397632867098 0.0391925126314 -0.0344442464411 -0.00569979753345 0.00958135258406 -0.0282046999782 -0.011605437845 0.00933898519725 -0.0317054688931 -0.00275706243701 0.0117793781683 0.0102474931628 0.0033322009258 -0.0393172241747 0.00347416335717 0.00105483434163 -0.0370849557221 0.00251939380541 0.00667322427034 -0.0372835509479 0.0278224293143 0.0121009163558 0.0102275209501 0.023449998349 0.0172611102462 0.00965423230082 0.0159195382148 0.00791420787573 0.0129859792069 0.00139058486093 -0.0184520073235 -0.00464215269312 -0.0143243018538 -0.0176195204258 -0.00678763026372 -0.00245248852298 -0.0189631339163 -0.0111456979066 0.0754076316953 -0.0163155626506 0.0023912510369 0.0813561230898 -0.0232167206705 0.00461526820436 0.0831556394696 -0.0222345832735 0.00146252510604 0.0249150972813 0.0107660228387 -0.0397206582129 0.0300506222993 0.0192408021539 -0.0371698960662 0.032403241843 0.0125592537224 -0.0369361899793 -0.0851302146912 0.0776748657227 0.00939944665879 -0.0847748368979 0.0726360008121 0.00620254734531 -0.0859415605664 0.0709468647838 0.00737344101071 0.0495941489935 -0.00505510251969 0.00281282933429 0.0385896004736 0.000291874835966 0.00444744434208 0.0418007522821 -0.00242996658199 -0.00372027652338 -0.0300818383694 -0.0526309125125 -0.000229920042329 -0.0269599817693 -0.0495302826166 0.00157890853006 -0.0297503750771 -0.0631617978215 0.000223000184633 0.0504876486957 0.00247842236422 -0.0409079678357 0.0554471611977 0.0115022500977 -0.042177259922 0.0566076412797 0.00240896618925 -0.0403126217425 0.00251939380541 0.00667322427034 -0.0372835509479 -0.0122720496729 0.00743707921356 -0.0320300944149 -0.00667092064396 0.0126474965364 -0.0336418747902 -0.0444244034588 0.0199280641973 0.00183702539653 -0.0434098988771 0.0118352910504 0.00674757128581 -0.0395993106067 0.0181454904377 0.00810672808439 -0.0800869390368 0.0719245970249 0.00986813567579 -0.0779805928469 0.0700702145696 0.00802535843104 -0.0851302146912 0.0776748657227 0.00939944665879 0.0020479504019 0.0282315947115 -0.00889560487121 0.0074149183929 0.0266806911677 -0.00382403749973 0.00980327930301 0.028790736571 -0.00937963183969 -0.0124928699806 -0.05317331478 -0.032723814249 -0.0123113365844 -0.0451743938029 -0.0291688665748 -0.00823789276183 -0.0588829554617 -0.0338821560144 -0.0779805928469 0.0700702145696 0.00802535843104 -0.0771700739861 0.0674374252558 0.00493047386408 -0.0851302146912 0.0776748657227 0.00939944665879 -0.0834547281265 0.0319089256227 0.0349037349224 -0.0791537687182 0.0380757376552 0.0263591650873 -0.0781400799751 0.0399436429143 0.0323086194694 -0.0409355126321 0.00761891668662 0.00908278673887 -0.042618881911 -0.00202813232318 0.0045546782203 -0.0364621728659 -4.06305734941e-05 0.00923090986907 -0.0473444573581 0.0222850441933 -0.0223869681358 -0.0457198508084 0.01298922766 -0.0289378613234 -0.0483878850937 0.0177123211324 -0.0199927464128 0.0700432732701 0.0106197241694 -0.0254250112921 0.0717578157783 0.015957320109 -0.0263392049819 0.0713142603636 0.0189655330032 -0.0151191568002 -0.0834547281265 0.0319089256227 0.0349037349224 -0.0891168415546 0.0341397710145 0.0321871414781 -0.0791537687182 0.0380757376552 0.0263591650873 -0.0269599817693 -0.0495302826166 0.00157890853006 -0.0252340473235 -0.0395757183433 -0.00116509769578 -0.0232568588108 -0.0392127744853 0.00331220822409 -0.0281191021204 0.0276504568756 -0.0284133274108 -0.0347637310624 0.0273377392441 -0.0288795996457 -0.0321282558143 0.0364747196436 -0.0215411484241 -0.0497838333249 0.0559922680259 0.00280405301601 -0.0454866327345 0.0472589023411 0.000885553658009 -0.0384465046227 0.0464623495936 -0.00204830104485 0.0129595380276 -0.00801198463887 0.00812369771302 0.014073533006 -0.000268704665359 0.0117724873126 0.00759891513735 -0.00405428977683 0.00967596191913 -0.0171094201505 0.00397073151544 -0.0321909785271 -0.0133467214182 0.00200212979689 -0.0306190121919 -0.0137986438349 -0.001824338804 -0.028773734346 -0.0321104675531 -0.00197183503769 -0.03518948704 -0.0242858082056 -0.0106024397537 -0.0329431965947 -0.0333822481334 -0.00652450276539 -0.0326015427709 -0.020613707602 -0.0750931277871 -0.0343296974897 -0.0162332504988 -0.0693076774478 -0.0359947793186 -0.0151771679521 -0.0751925632358 -0.0358218029141 -0.0228501725942 -0.016540652141 0.00465866690502 -0.0215143859386 -0.019579783082 0.00190126814414 -0.0183539558202 -0.0179969426244 0.000481826165924 -0.00354365818202 -0.0125812022015 -0.0245189536363 -0.000636835582554 -0.00549022806808 -0.0329833440483 0.00573587883264 -0.00811646040529 -0.0328484363854 -0.0622629895806 0.0477367714047 0.00780300609767 -0.0615916401148 0.041047938168 0.00514492439106 -0.0614165142179 0.0504314303398 0.0064328792505 -0.0164575334638 0.0125575726852 0.00686521595344 -0.0102530596778 0.0101205231622 0.0068081477657 -0.00733371544629 0.0176978223026 0.00535471225157 -0.0779805928469 0.0700702145696 0.00802535843104 -0.0785785987973 0.0702539384365 0.013914372772 -0.0731192678213 0.071150586009 0.0126371867955 -0.0434098988771 0.0118352910504 0.00674757128581 -0.0444244034588 0.0199280641973 0.00183702539653 -0.0480200089514 0.0139896376058 -0.00342765916139 -0.042618881911 -0.00202813232318 0.0045546782203 -0.0451306067407 0.00219421950169 -0.00086157215992 -0.0413943827152 -0.00729784090072 -6.61109006614e-05 0.0530218444765 0.0132510503754 0.0114024560899 0.0511791184545 0.0201472640038 0.0112951202318 0.0422127395868 0.0228577423841 0.00999154523015 0.0672499090433 -0.0154510131106 0.00984042230994 0.0705670714378 -0.0142336087301 0.0080666737631 0.0682200118899 -0.00788477621973 0.00767027726397 0.0336626619101 0.0301720779389 0.00381930125877 0.0374021679163 0.0269114822149 0.00774644361809 0.0447547212243 0.0325945727527 0.00325110368431 0.0664181634784 -0.0537509098649 -0.0296265520155 0.0578673295677 -0.03540468961 -0.0308890789747 0.0610778927803 -0.0446138717234 -0.033351752907 0.0563069395721 -0.0334836132824 -0.0334919802845 0.0514739230275 -0.0248519349843 -0.0344557315111 0.0595832765102 -0.0364168584347 -0.0356253683567 -0.0363425649703 -0.0124280676246 -0.00762832257897 -0.0393666550517 -0.0106545481831 -0.0230054780841 -0.0342719815671 -0.0143612474203 -0.0263750851154 -0.0877385362983 0.0418181084096 0.0414660908282 -0.0876308903098 0.0384379811585 0.0428250767291 -0.0824304521084 0.0437800884247 0.0388801582158 -0.0349772572517 -0.0686603486538 0.00445972103626 -0.0353057719767 -0.0717958807945 -0.000896993966307 -0.033697348088 -0.0659902095795 0.000380541518098 0.0655900388956 0.0184390544891 0.00736436760053 0.0627590343356 0.0254758372903 0.00493016792461 0.0585609525442 0.0207895357162 0.00992077030241 0.0703099220991 -0.0588180907071 -0.0379472970963 0.0650048032403 -0.0514324046671 -0.0363247282803 0.070414699614 -0.052166569978 -0.0348446667194 -0.0353057719767 -0.0717958807945 -0.000896993966307 -0.0349772572517 -0.0686603486538 0.00445972103626 -0.0334314070642 -0.0711332336068 0.00546559365466 0.073333054781 -0.0605282187462 -0.0343145728111 0.0714298635721 -0.0534965991974 -0.0324478931725 0.072433590889 -0.0604861862957 -0.0301266685128 -0.0918235704303 0.0354779586196 0.0378490537405 -0.0878021046519 0.033477794379 0.0395364612341 -0.0876308903098 0.0384379811585 0.0428250767291 -0.0163839384913 -0.0424141436815 -0.0313666537404 -0.0177184212953 -0.0485680811107 -0.0308595653623 -0.02261329256 -0.0363039076328 -0.031984012574 -0.020558193326 -0.0756064131856 -0.0252870116383 -0.0150378849357 -0.0664253011346 -0.026939317584 -0.0228810738772 -0.0747795701027 -0.0308283958584 0.0438971333206 -0.00236639939249 -0.0157472789288 0.050373673439 -0.00361777818762 -0.0148283494636 0.0418007522821 -0.00242996658199 -0.00372027652338 0.0504528395832 0.0169036872685 -0.0417337194085 0.0441741943359 0.0165656842291 -0.0412995144725 0.035920906812 0.0201838165522 -0.0398071408272 -0.0160560552031 -0.0138077298179 -0.00380461011082 -0.016403356567 -0.00939153227955 -0.00214911578223 -0.0228501725942 -0.016540652141 0.00465866690502 -0.020558193326 -0.0756064131856 -0.0252870116383 -0.0149209909141 -0.0729868486524 -0.0253179427236 -0.0150378849357 -0.0664253011346 -0.026939317584 -0.0741797760129 0.0406901091337 0.00791172217578 -0.0789092034101 0.0428538136184 0.00382694439031 -0.0736100152135 0.0473809987307 0.00139974115882 0.0675277709961 0.00351993273944 -0.0305571258068 0.0700432732701 0.0106197241694 -0.0254250112921 0.0600458458066 -0.00507800886407 -0.0249140188098 0.068222001195 -0.0600504949689 -0.0280908290297 0.0664181634784 -0.0537509098649 -0.0296265520155 0.0649472996593 -0.0564471706748 -0.0301647242159 0.0665984004736 -0.0429504625499 -0.0306054838002 0.0714298635721 -0.0534965991974 -0.0324478931725 0.0669340640306 -0.0361999757588 -0.031447045505 0.0339798666537 0.0333764031529 -0.0331196337938 0.0348762497306 0.0356907360256 -0.0298048574477 0.0434862487018 0.032226357609 -0.036325853318 -0.0097112422809 -0.0125950593501 -0.0233362093568 -0.0144916502759 -0.00543817412108 -0.0263480637223 -0.00354365818202 -0.0125812022015 -0.0245189536363 0.0769819021225 -0.0300572067499 -0.00352066406049 0.0825529024005 -0.0241261273623 -0.00263262726367 0.0839467793703 -0.0338173881173 -0.0013689303305 -0.0196293834597 -0.0012800822733 -0.0339007712901 -0.0245153158903 0.0113811995834 -0.0345406457782 -0.0171094201505 0.00397073151544 -0.0321909785271 0.0684423595667 0.00943436846137 -0.0206362344325 0.0600458458066 -0.00507800886407 -0.0249140188098 0.0700432732701 0.0106197241694 -0.0254250112921 -0.0272648502141 0.0430674515665 -0.0166629143059 -0.0281191021204 0.0276504568756 -0.0284133274108 -0.0321282558143 0.0364747196436 -0.0215411484241 0.0504257716238 -0.016582140699 -0.0392883382738 0.0574302785099 -0.0164519175887 -0.0373466834426 0.0575529150665 -0.022210219875 -0.0367079302669 -0.0287229511887 -0.0406478978693 0.00720230536535 -0.0304189305753 -0.0502045191824 0.00526972860098 -0.0280799940228 -0.055304210633 0.00326808029786 -0.0594911910594 0.0402323678136 -0.0138589777052 -0.0642814710736 0.0499867610633 -0.00973032042384 -0.0562584511936 0.0401268824935 -0.0175458882004 -0.0448838733137 0.00699473544955 0.00491576688364 -0.042618881911 -0.00202813232318 0.0045546782203 -0.0409355126321 0.00761891668662 0.00908278673887 -0.0529348887503 0.0521784499288 0.0046043144539 -0.0614165142179 0.0504314303398 0.0064328792505 -0.0454866327345 0.0472589023411 0.000885553658009 0.0163268223405 -0.0114426901564 0.00449271220714 0.0129595380276 -0.00801198463887 0.00812369771302 0.00798164401203 -0.0143224736676 0.00208706385456 -0.0381523445249 -0.00304430956021 -0.029428480193 -0.0366347320378 0.0017704871716 -0.0322802253067 -0.0333822481334 -0.00652450276539 -0.0326015427709 0.0222891252488 -0.0072523932904 0.00697265705094 0.0163268223405 -0.0114426901564 0.00449271220714 0.0233577005565 -0.0130151677877 -0.00125345273409 0.0380213819444 -0.000848870549817 -0.0383270718157 0.0447440594435 -0.0021612313576 -0.0399010665715 0.0420701652765 -0.00905739609152 -0.0347143113613 0.0711212977767 -0.0141868051142 -0.00323123089038 0.0697568207979 -0.00990358553827 -0.00423297146335 0.0734314844012 -0.0137593969703 -0.0012326046126 0.0575529150665 -0.022210219875 -0.0367079302669 0.058284394443 -0.0302759688348 -0.0378637500107 0.0547827705741 -0.0236574988812 -0.0368034355342 -0.0138331912458 -0.0603952333331 -0.0342085324228 -0.00823789276183 -0.0588829554617 -0.0338821560144 -0.0141136925668 -0.0657514110208 -0.0340646244586 -0.0444217473269 0.0602424219251 -0.00442242622375 -0.0384465046227 0.0464623495936 -0.00204830104485 -0.0322041623294 0.0489787906408 -0.00726023875177 -0.030961079523 0.00549497501925 0.0111165465787 -0.0306212659925 0.0172087047249 0.0100856469944 -0.0347196757793 0.00916986633092 0.0111047113314 -0.0480200089514 0.0139896376058 -0.00342765916139 -0.0448838733137 0.00699473544955 0.00491576688364 -0.0434098988771 0.0118352910504 0.00674757128581 -0.0138331912458 -0.0603952333331 -0.0342085324228 -0.0124928699806 -0.05317331478 -0.032723814249 -0.00823789276183 -0.0588829554617 -0.0338821560144 -0.0652576088905 0.0690394937992 0.0202288385481 -0.0723753124475 0.0711080506444 0.0155806122348 -0.068583458662 0.0648453906178 0.0195779465139 0.00984027050436 0.0123339463025 -0.039398021996 0.0203334894031 0.0168978217989 -0.039546135813 0.0173371694982 0.00796426646411 -0.0401615314186 0.0697568207979 -0.00990358553827 -0.00423297146335 0.0711212977767 -0.0141868051142 -0.00323123089038 0.0643664449453 -0.0178353134543 0.000139734620461 -0.0174274221063 -0.0494892522693 -0.0261709243059 -0.0166946779937 -0.0649743825197 -0.0305308811367 -0.0150378849357 -0.0664253011346 -0.026939317584 0.0347270555794 0.027764454484 -0.0385278463364 0.0456950850785 0.0270799733698 -0.0398734845221 0.051399409771 0.0217050053179 -0.0416473895311 0.0530218444765 0.0132510503754 0.0114024560899 0.0491875857115 0.00344011140987 0.0133795123547 0.0544707849622 0.00255479058251 0.0128589896485 0.0687282457948 0.0103019243106 -0.0152066750452 0.0622391216457 0.00130491273012 -0.0162859521806 0.0684423595667 0.00943436846137 -0.0206362344325 0.0616061538458 0.0335902385414 -0.0206316392869 0.0668411776423 0.0276774372905 -0.0293406844139 0.0584006085992 0.0334684699774 -0.0289356615394 -0.0136914439499 -0.0546173118055 -0.0263203363866 -0.0095995971933 -0.0609665364027 -0.0261474959552 -0.00944765098393 -0.0544546283782 -0.0279082152992 -0.00823789276183 -0.0588829554617 -0.0338821560144 -0.010915578343 -0.0659576877952 -0.0315063148737 -0.0141136925668 -0.0657514110208 -0.0340646244586 -0.0173668675125 -0.0188725441694 -0.0200483836234 -0.0251056849957 -0.0178923550993 -0.0185515768826 -0.0209524612874 -0.0299015939236 -0.0216957069933 0.0317371711135 -0.00692039029673 -0.0261175371706 0.0326762907207 -0.00954702217132 -0.0169461015612 0.0193834770471 -0.0151846110821 -0.0219733603299 -0.0252810381353 -0.0247280094773 -0.0293741747737 -0.025938231498 -0.03466161713 -0.0289873387665 -0.0279543641955 -0.0210831910372 -0.0291056055576 -0.0160121638328 -0.00505490787327 0.00223769713193 -0.016403356567 -0.00939153227955 -0.00214911578223 -0.012282749638 -0.00724740931764 -2.84041252598e-05 0.089011117816 -0.049574509263 0.0023703314364 0.0819737017155 -0.0426090881228 0.00562430731952 0.0842658951879 -0.0518600940704 0.00558784976602 0.0605648122728 -0.0226184297353 -0.0335885249078 0.0575529150665 -0.022210219875 -0.0367079302669 0.0574302785099 -0.0164519175887 -0.0373466834426 0.00794281996787 -0.0124184312299 -0.0286089126021 0.00320344255306 -0.0166476164013 -0.0215023886412 -0.00354365818202 -0.0125812022015 -0.0245189536363 -0.0680031627417 0.0388116128743 0.0118984086439 -0.0639205127954 0.0411553606391 0.00809910707176 -0.0643574744463 0.0411904193461 0.0170047152787 -0.0252810381353 -0.0247280094773 -0.0293741747737 -0.0277498979121 -0.0136683974415 -0.0326979234815 -0.0242858082056 -0.0106024397537 -0.0329431965947 -0.0455527119339 0.00271498505026 -0.0177962481976 -0.0414947420359 -0.00295710819773 -0.0225406978279 -0.0418213754892 -0.00742403836921 -0.0206348095089 0.0621104352176 0.00453598890454 -0.0360744036734 0.0600861534476 -0.00594944367185 -0.0371469557285 0.0566076412797 0.00240896618925 -0.0403126217425 -0.0106726381928 -0.0760597363114 -0.026934184134 -0.020558193326 -0.0756064131856 -0.0252870116383 -0.0151771679521 -0.0751925632358 -0.0358218029141 -0.0268499869853 -0.016901101917 -0.00799196213484 -0.0143243018538 -0.0176195204258 -0.00678763026372 -0.0262299366295 -0.017805961892 -0.00452885916457 0.072433590889 -0.0604861862957 -0.0301266685128 0.0683374479413 -0.0672494471073 -0.0335944145918 0.073333054781 -0.0605282187462 -0.0343145728111 0.0579114630818 0.0292274262756 -0.0363327749074 0.0649568662047 0.0234411917627 -0.0345332249999 0.060431715101 0.0229300074279 -0.0391151756048 -0.0503109395504 0.0511616170406 -0.0167050715536 -0.0493567548692 0.0630387067795 -0.0120902014896 -0.0412535518408 0.055965680629 -0.0148494942114 0.0278224293143 0.0121009163558 0.0102275209501 0.0248502586037 0.00308163510635 0.0112843178213 0.0350222811103 0.011573231779 0.00757634406909 0.0648333206773 -0.0193411242217 0.00892408099025 0.0584446676075 -0.0131367407739 0.00669920956716 0.0622591376305 -0.020616741851 0.00515010254458 0.0126880630851 0.0209196154028 0.00636390130967 0.0122077101842 0.0155965732411 0.0104404864833 0.023449998349 0.0172611102462 0.00965423230082 -0.0615916401148 0.041047938168 0.00514492439106 -0.0572749041021 0.046243134886 0.00337952957489 -0.0614165142179 0.0504314303398 0.0064328792505 0.0563069395721 -0.0334836132824 -0.0334919802845 0.0595832765102 -0.0364168584347 -0.0356253683567 0.062893807888 -0.0445155724883 -0.0355053693056 -0.0215143859386 -0.019579783082 0.00190126814414 -0.0222899951041 -0.0185299292207 -0.00168474740349 -0.0183539558202 -0.0179969426244 0.000481826165924 0.0855331420898 -0.0662355422974 -0.000724932178855 0.0838564783335 -0.0586024485528 -0.00192842667457 0.0889291241765 -0.0584181435406 -0.00213292124681 0.0461902655661 -0.0180508662015 -0.0307826343924 0.0432890281081 -0.00950278807431 -0.0291274245828 0.0420701652765 -0.00905739609152 -0.0347143113613 -0.070197686553 0.0612765774131 0.0268817096949 -0.0729605704546 0.0639232620597 0.0233493559062 -0.0754706263542 0.057246144861 0.0283697806299 -0.012165626511 0.0265315938741 -0.0258189272135 -0.0089406510815 0.0311568155885 -0.0189374648035 -0.00350152561441 0.0275401733816 -0.0214963126928 -0.0434098988771 0.0118352910504 0.00674757128581 -0.0409355126321 0.00761891668662 0.00908278673887 -0.0395993106067 0.0181454904377 0.00810672808439 -0.00599885499105 -0.0184409264475 -0.015290110372 -0.0251056849957 -0.0178923550993 -0.0185515768826 -0.0173668675125 -0.0188725441694 -0.0200483836234 -0.051504354924 0.0203009396791 -0.0043327328749 -0.0604898072779 0.0358108095825 -0.000405532977311 -0.0631757378578 0.0389323234558 -0.00277568120509 -0.00354365818202 -0.0125812022015 -0.0245189536363 0.00573587883264 -0.00811646040529 -0.0328484363854 0.00794281996787 -0.0124184312299 -0.0286089126021 -0.0846682190895 0.0387630797923 0.0249170754105 -0.0891168415546 0.0341397710145 0.0321871414781 -0.0873394012451 0.0418514795601 0.0236989371479 -0.0803267359734 0.0397947318852 0.0164727605879 -0.0737345814705 0.038439001888 0.0121840443462 -0.0745909139514 0.0392232574522 0.0197847299278 0.0579114630818 0.0292274262756 -0.0363327749074 0.0529840812087 0.0303148236126 -0.0373462289572 0.0584006085992 0.0334684699774 -0.0289356615394 0.0491875857115 0.00344011140987 0.0133795123547 0.0530218444765 0.0132510503754 0.0114024560899 0.0477037541568 0.0117063503712 0.0115124462172 0.0754076316953 -0.0163155626506 0.0023912510369 0.0798162445426 -0.0199222583324 -0.0021768680308 0.0734314844012 -0.0137593969703 -0.0012326046126 -0.0163839384913 -0.0424141436815 -0.0313666537404 -0.017013894394 -0.0347188636661 -0.030624659732 -0.0132942395285 -0.0358155034482 -0.0281592346728 0.089011117816 -0.049574509263 0.0023703314364 0.0924971923232 -0.0599343143404 0.00196787621826 0.0870006456971 -0.046893555671 0.000147852333612 0.0795691013336 -0.0337514281273 0.00747773703188 0.0843727812171 -0.0348832271993 0.00490371277556 0.0839628651738 -0.0313502401114 0.00374555471353 -0.0440319478512 0.0313652604818 0.0016547862906 -0.0572749041021 0.046243134886 0.00337952957489 -0.0453874990344 0.0272053200752 0.00166924321093 -0.0324501506984 -0.014356858097 0.00908888690174 -0.0344442464411 -0.00569979753345 0.00958135258406 -0.0361147299409 -0.0120508149266 0.00691540632397 -0.0409485846758 0.0245515368879 0.00264321570285 -0.035688880831 0.0322657860816 -0.00136898260098 -0.0440319478512 0.0313652604818 0.0016547862906 0.0512403547764 -0.00441910745576 -0.0253982823342 0.0438971333206 -0.00236639939249 -0.0157472789288 0.0442871451378 -0.00394489476457 -0.0271087102592 0.0563069395721 -0.0334836132824 -0.0334919802845 0.0578673295677 -0.03540468961 -0.0308890789747 0.0461902655661 -0.0180508662015 -0.0307826343924 0.00324054062366 0.0132272355258 0.00995828956366 0.00083865004126 0.00723583996296 0.0104546826333 0.0159195382148 0.00791420787573 0.0129859792069 0.00759891513735 -0.00405428977683 0.00967596191913 -0.00387709727511 -0.00124160922132 0.00781158497557 0.0036262657959 -0.00963021907955 0.00580563070253 0.0301524624228 -0.00372725259513 -0.0304073672742 0.0240502078086 -0.00868919305503 -0.0290734339505 0.0182611495256 -0.000761016330216 -0.0385480634868 0.00531596504152 0.00230240589008 0.0111257759854 0.014073533006 -0.000268704665359 0.0117724873126 0.0159195382148 0.00791420787573 0.0129859792069 -0.0253939889371 -0.00600079027936 -0.0348485857248 -0.0242858082056 -0.0106024397537 -0.0329431965947 -0.0321104675531 -0.00197183503769 -0.03518948704 -0.0166946779937 -0.0649743825197 -0.0305308811367 -0.0162332504988 -0.0693076774478 -0.0359947793186 -0.0228810738772 -0.0747795701027 -0.0308283958584 0.0564444027841 -0.00904699414968 -0.0371534638107 0.0600861534476 -0.00594944367185 -0.0371469557285 0.0602696500719 -0.00843365583569 -0.0316083207726 -0.0582275763154 0.0486685633659 -0.0147868087515 -0.0521116182208 0.0417004041374 -0.0192602630705 -0.0562584511936 0.0401268824935 -0.0175458882004 -0.0413943827152 -0.00729784090072 -6.61109006614e-05 -0.0368985123932 -0.0142811005935 0.00302317948081 -0.0379135832191 -0.00708554778248 0.00403723120689 0.0574302785099 -0.0164519175887 -0.0373466834426 0.0564444027841 -0.00904699414968 -0.0371534638107 0.0605014450848 -0.0130754690617 -0.0308906380087 -0.0182791948318 -0.019947366789 -0.0281752515584 -0.017013894394 -0.0347188636661 -0.030624659732 -0.0236518606544 -0.0245031807572 -0.0287500377744 0.0447440594435 -0.0021612313576 -0.0399010665715 0.0458527319133 0.0086536090821 -0.0422105453908 0.0504876486957 0.00247842236422 -0.0409079678357 -0.046883624047 0.00825335178524 -0.0246345344931 -0.0455527119339 0.00271498505026 -0.0177962481976 -0.0473023541272 0.0122802108526 -0.0124696297571 0.0350222811103 0.011573231779 0.00757634406909 0.0378097891808 0.00806960370392 0.00718982517719 0.0405975058675 0.0136088356376 0.009625967592 -0.0262299366295 -0.017805961892 -0.00452885916457 -0.0295641105622 -0.0183343663812 -0.00392657378688 -0.0325671248138 -0.0150363976136 -0.00584217207506 0.0403502397239 -0.00280293240212 -0.0176492966712 0.0395265072584 -0.00346563779749 -0.0100429235026 0.0326762907207 -0.00954702217132 -0.0169461015612 -0.0218933653086 0.0331122018397 -0.000901366525795 -0.0221735388041 0.0248348526657 0.00527667580172 -0.0180822927505 0.0280927624553 0.00312319723889 0.0405975058675 0.0136088356376 0.009625967592 0.0317819230258 0.0191275794059 0.00692741526291 0.0350222811103 0.011573231779 0.00757634406909 -0.0282046999782 -0.011605437845 0.00933898519725 -0.0212301537395 -0.0123340161517 0.00449436390772 -0.0247759241611 -0.0095595670864 0.010169220157 -0.0582275763154 0.0486685633659 -0.0147868087515 -0.0509763434529 0.0460034161806 -0.0177591200918 -0.0521116182208 0.0417004041374 -0.0192602630705 -0.0221735388041 0.0248348526657 0.00527667580172 -0.0181487128139 0.0191756095737 0.00725237466395 -0.0180822927505 0.0280927624553 0.00312319723889 -0.0737345814705 0.038439001888 0.0121840443462 -0.0803267359734 0.0397947318852 0.0164727605879 -0.0825153067708 0.041629076004 0.009376286529 0.0248502586037 0.00308163510635 0.0112843178213 0.0159195382148 0.00791420787573 0.0129859792069 0.014073533006 -0.000268704665359 0.0117724873126 0.0300506222993 0.0192408021539 -0.0371698960662 0.0203334894031 0.0168978217989 -0.039546135813 0.0261729359627 0.0250671524554 -0.0349872037768 -0.0277498979121 -0.0136683974415 -0.0326979234815 -0.0351685248315 -0.0100314561278 -0.031182685867 -0.0333822481334 -0.00652450276539 -0.0326015427709 -0.01405338943 -0.0357937663794 -0.0235176254064 -0.0167125929147 -0.0282691419125 -0.0229621194303 -0.0171560104936 -0.0368272177875 -0.0223063975573 0.050373673439 -0.00361777818762 -0.0148283494636 0.0556262694299 -0.00370851811022 -0.0167932603508 0.0539004430175 -0.00540307629853 -0.00894699897617 0.00260267360136 0.0286370813847 -0.0185407903045 0.00980327930301 0.028790736571 -0.00937963183969 0.0106190349907 0.02950688079 -0.021063869819 0.0665407851338 0.00159848632757 -0.00484928302467 0.0697568207979 -0.00990358553827 -0.00423297146335 0.0666749030352 -0.00735376635566 -0.00298768025823 0.0609902478755 0.0335634611547 -0.0094895362854 0.065167747438 0.0291529707611 -0.00138657167554 0.0672424212098 0.0293734762818 -0.00736515130848 0.064685754478 -0.0747605413198 -0.0385285392404 0.058990996331 -0.0744717493653 -0.0386376976967 0.0668106079102 -0.070455878973 -0.0377877689898 -0.0652352571487 0.0470017306507 -0.00540793221444 -0.074530698359 0.0564152859151 0.000770429789554 -0.0642814710736 0.0499867610633 -0.00973032042384 -0.0395993106067 0.0181454904377 0.00810672808439 -0.0352949239314 0.0277008544654 0.00385763379745 -0.0409485846758 0.0245515368879 0.00264321570285 0.0624106451869 -0.00390694057569 -0.00189818732906 0.0666749030352 -0.00735376635566 -0.00298768025823 0.0585702136159 -0.00812319666147 0.00419311970472 -0.0477432273328 0.0646687820554 -0.00769540853798 -0.0568155124784 0.0680090263486 -0.00663143396378 -0.059178583324 0.0700522735715 -0.00168476975523 -0.0329524688423 0.00574303558096 -0.0349177606404 -0.0314821787179 0.0163094382733 -0.0335103832185 -0.0276381317526 0.0215675216168 -0.0321951173246 -0.0395993106067 0.0181454904377 0.00810672808439 -0.0347196757793 0.00916986633092 0.0111047113314 -0.0355813615024 0.0215603802353 0.00780190061778 0.0491875857115 0.00344011140987 0.0133795123547 0.0458973273635 -0.000604764616583 0.0111892325804 0.0544707849622 0.00255479058251 0.0128589896485 0.0447547212243 0.0325945727527 0.00325110368431 0.0572973936796 0.0331504121423 -0.000841111701448 0.0472641289234 0.0361711494625 -0.00451004551724 -0.012700362131 0.00108930072747 0.00499837147072 -0.00387709727511 -0.00124160922132 0.00781158497557 0.00083865004126 0.00723583996296 0.0104546826333 -0.0150378849357 -0.0664253011346 -0.026939317584 -0.0120546650141 -0.0671373382211 -0.028148625046 -0.0095995971933 -0.0609665364027 -0.0261474959552 0.0648333206773 -0.0193411242217 0.00892408099025 0.0604619793594 -0.0071836868301 0.00888195540756 0.0584446676075 -0.0131367407739 0.00669920956716 0.0609902478755 0.0335634611547 -0.0094895362854 0.0679226443172 0.029033575207 -0.0159582849592 0.0616061538458 0.0335902385414 -0.0206316392869 -0.0480200089514 0.0139896376058 -0.00342765916139 -0.0484882481396 0.0242904219776 -0.00165670679417 -0.051504354924 0.0203009396791 -0.0043327328749 0.0622391216457 0.00130491273012 -0.0162859521806 0.0583206899464 -0.00380795542151 -0.00911643542349 0.0556262694299 -0.00370851811022 -0.0167932603508 0.0197386574 0.031497310847 -0.0197209063917 0.0206307601184 0.0312986150384 -0.0104876076803 0.0319815389812 0.0352998748422 -0.0105315605178 -0.0379135832191 -0.00708554778248 0.00403723120689 -0.042618881911 -0.00202813232318 0.0045546782203 -0.0413943827152 -0.00729784090072 -6.61109006614e-05 -0.0247759241611 -0.0095595670864 0.010169220157 -0.0272913184017 0.00181260507088 0.0117624523118 -0.0317054688931 -0.00275706243701 0.0117793781683 0.0738353580236 0.00327896140516 0.000486694596475 0.0706675350666 0.0171718429774 0.00147831998765 0.0688916966319 0.00488886702806 0.00309444754384 -0.0473444573581 0.0222850441933 -0.0223869681358 -0.0594911910594 0.0402323678136 -0.0138589777052 -0.0562584511936 0.0401268824935 -0.0175458882004 -0.0386477708817 0.0575842261314 -0.0097325630486 -0.0194173324853 0.0395698286593 -0.0113779669628 -0.0210811626166 0.0406454503536 -0.0149188544601 -0.0643574744463 0.0411904193461 0.0170047152787 -0.0639205127954 0.0411553606391 0.00809910707176 -0.0617349892855 0.0479026585817 0.0140177402645 0.0570097789168 -0.0103748161346 -0.028088811785 0.0493391752243 -0.0192066207528 -0.0282236821949 0.0564465895295 -0.0182245336473 -0.0300305616111 0.0787196084857 -0.0281961280853 0.00396182155237 0.0725733786821 -0.0235378220677 0.00565016036853 0.0795691013336 -0.0337514281273 0.00747773703188 -0.00733371544629 0.0176978223026 0.00535471225157 0.0122077101842 0.0155965732411 0.0104404864833 0.0126880630851 0.0209196154028 0.00636390130967 0.0347373187542 0.0342971347272 -0.00132000620943 0.0447547212243 0.0325945727527 0.00325110368431 0.0472641289234 0.0361711494625 -0.00451004551724 -0.043179307133 0.0188505705446 -0.0299622882158 -0.0457198508084 0.01298922766 -0.0289378613234 -0.0473444573581 0.0222850441933 -0.0223869681358 0.0706675350666 0.0171718429774 0.00147831998765 0.0711485892534 0.0247458182275 -0.00319633609615 0.0655900388956 0.0184390544891 0.00736436760053 0.0312596708536 0.00204978929833 -0.0341790989041 0.0301524624228 -0.00372725259513 -0.0304073672742 0.0182611495256 -0.000761016330216 -0.0385480634868 -0.0299632400274 -0.0203246790916 -0.026556706056 -0.0279543641955 -0.0210831910372 -0.0291056055576 -0.025938231498 -0.03466161713 -0.0289873387665 -0.0477432273328 0.0646687820554 -0.00769540853798 -0.0507754385471 0.0627422481775 0.000138281728141 -0.0444217473269 0.0602424219251 -0.00442242622375 0.0672754794359 -0.0748537257314 -0.028308160603 0.0609687641263 -0.0750585868955 -0.0276746544987 0.058990996331 -0.0744717493653 -0.0386376976967 -0.0889886766672 0.0597609467804 0.0147773399949 -0.0870704501867 0.052732732147 0.0148491580039 -0.0864301398396 0.0525974109769 0.0242326129228 -0.0347196757793 0.00916986633092 0.0111047113314 -0.0364621728659 -4.06305734941e-05 0.00923090986907 -0.030961079523 0.00549497501925 0.0111165465787 0.0725204646587 -0.00817401800305 -0.000920072081499 0.0708144679666 0.000992352026515 -0.00854138284922 0.0720914751291 0.00546024832875 -0.010403117165 0.0434862487018 0.032226357609 -0.036325853318 0.0347270555794 0.027764454484 -0.0385278463364 0.0339798666537 0.0333764031529 -0.0331196337938 -0.0193056520075 -0.00640912586823 0.00741031300277 -0.0247759241611 -0.0095595670864 0.010169220157 -0.0212301537395 -0.0123340161517 0.00449436390772 0.0782531201839 -0.0722363963723 0.00564285600558 0.0818465650082 -0.0646916031837 0.004601073917 0.0769691392779 -0.075030490756 0.002559523331 0.0769165381789 -0.0372788608074 0.004157373216 0.0709631517529 -0.0282764472067 0.0042789876461 0.0784853920341 -0.0390462353826 -2.51326264333e-06 0.0292802155018 -0.012616426684 -0.0106529798359 0.0309519954026 -0.00872462801635 -0.00288755609654 0.0233577005565 -0.0130151677877 -0.00125345273409 0.0609902478755 0.0335634611547 -0.0094895362854 0.0672424212098 0.0293734762818 -0.00736515130848 0.0679226443172 0.029033575207 -0.0159582849592 -0.0232568588108 -0.0392127744853 0.00331220822409 -0.0230524111539 -0.0281649492681 0.00343656959012 -0.0257243346423 -0.0339680574834 0.00464337132871 -0.0604898072779 0.0358108095825 -0.000405532977311 -0.051504354924 0.0203009396791 -0.0043327328749 -0.0584039799869 0.0388491488993 0.00238339230418 -0.0300818383694 -0.0526309125125 -0.000229920042329 -0.0337675176561 -0.0367394164205 -0.00267041777261 -0.0305029321462 -0.0379659235477 -0.00386006757617 -0.0171560104936 -0.0368272177875 -0.0223063975573 -0.0209524612874 -0.0299015939236 -0.0216957069933 -0.0212532021105 -0.0387861654162 -0.0227633845061 0.0610778927803 -0.0446138717234 -0.033351752907 0.0563069395721 -0.0334836132824 -0.0334919802845 0.062893807888 -0.0445155724883 -0.0355053693056 -0.000636835582554 -0.00549022806808 -0.0329833440483 0.00347416335717 0.00105483434163 -0.0370849557221 0.00936001725495 -0.00345246354118 -0.0368482433259 0.0544707849622 0.00255479058251 0.0128589896485 0.0636151582003 -0.00344871124253 0.0112777063623 0.0603620111942 0.0056228893809 0.0125811565667 -0.0800869390368 0.0719245970249 0.00986813567579 -0.0836498662829 0.0665282458067 0.00920514483005 -0.0785785987973 0.0702539384365 0.013914372772 -0.0295641105622 -0.0183343663812 -0.00392657378688 -0.0334986448288 -0.0282570403069 -0.00100804551039 -0.0363080054522 -0.0172072835267 0.00185445009265 -0.0128323584795 -0.0432547368109 -0.0266204569489 -0.01405338943 -0.0357937663794 -0.0235176254064 -0.0149490861222 -0.0439244024456 -0.0251441132277 -0.00657241046429 -0.0163257047534 -0.00350670074113 -0.0160560552031 -0.0138077298179 -0.00380461011082 -0.0143243018538 -0.0176195204258 -0.00678763026372 -0.0584039799869 0.0388491488993 0.00238339230418 -0.0484882481396 0.0242904219776 -0.00165670679417 -0.0572749041021 0.046243134886 0.00337952957489 -0.0617349892855 0.0479026585817 0.0140177402645 -0.0622629895806 0.0477367714047 0.00780300609767 -0.0635816827416 0.0559310466051 0.0108306938782 -0.0867733284831 0.0609648488462 0.0183455236256 -0.0864301398396 0.0525974109769 0.0242326129228 -0.084884122014 0.054450109601 0.0266954693943 0.0683374479413 -0.0672494471073 -0.0335944145918 0.0696968138218 -0.0747989788651 -0.0332351587713 0.0668106079102 -0.070455878973 -0.0377877689898 -0.00552152097225 -0.0164588801563 -0.0201352518052 -0.00599885499105 -0.0184409264475 -0.015290110372 -0.015117011033 -0.0188090614974 -0.0260683037341 0.0182611495256 -0.000761016330216 -0.0385480634868 0.0249150972813 0.0107660228387 -0.0397206582129 0.023355057463 0.00308697414584 -0.0384194366634 0.0738353580236 0.00327896140516 0.000486694596475 0.0743348672986 0.00406845519319 -0.00470197200775 0.0747518464923 0.0119193941355 -0.00421227002516 0.0533758401871 -0.00816207565367 0.00332604441792 0.0622591376305 -0.020616741851 0.00515010254458 0.0584446676075 -0.0131367407739 0.00669920956716 -0.0314232259989 0.0354066677392 -0.00278120324947 -0.0384465046227 0.0464623495936 -0.00204830104485 -0.0454866327345 0.0472589023411 0.000885553658009 0.00251939380541 0.00667322427034 -0.0372835509479 0.00984027050436 0.0123339463025 -0.039398021996 0.0102474931628 0.0033322009258 -0.0393172241747 0.0393960885704 -0.00669425539672 -0.0340571328998 0.0400369912386 -0.00509023061022 -0.0284015871584 0.0355566330254 -0.0077797062695 -0.0261449292302 -0.0236518606544 -0.0245031807572 -0.0287500377744 -0.0201097112149 -0.0344850867987 -0.0324018150568 -0.0252810381353 -0.0247280094773 -0.0293741747737 -0.0282046999782 -0.011605437845 0.00933898519725 -0.0270780660212 -0.0214741248637 0.00396085856482 -0.0212301537395 -0.0123340161517 0.00449436390772 0.0924971923232 -0.0599343143404 0.00196787621826 0.0874174982309 -0.0661162883043 0.0035401773639 0.0855331420898 -0.0662355422974 -0.000724932178855 0.0477273277938 -0.00715120695531 -0.0378671810031 0.0504257716238 -0.016582140699 -0.0392883382738 0.0420701652765 -0.00905739609152 -0.0347143113613 -0.0336843542755 -0.0225331783295 0.00486868014559 -0.0306760501117 -0.0296483207494 0.00481163570657 -0.0289716441184 -0.0219964478165 0.00619356799871 -0.0377826243639 -0.0715468376875 0.00810029637069 -0.0390509627759 -0.0665916949511 0.00520234834403 -0.04279313609 -0.0713429301977 0.00690987613052 0.0837335810065 -0.0474399290979 -0.000192326551769 0.0769819021225 -0.0300572067499 -0.00352066406049 0.0870006456971 -0.046893555671 0.000147852333612 0.0447440594435 -0.0021612313576 -0.0399010665715 0.0566076412797 0.00240896618925 -0.0403126217425 0.052886724472 -0.00321291876025 -0.0398595966399 0.0648333206773 -0.0193411242217 0.00892408099025 0.0728958249092 -0.0278529170901 0.00544968014583 0.0672499090433 -0.0154510131106 0.00984042230994 -0.0300818383694 -0.0526309125125 -0.000229920042329 -0.0305029321462 -0.0379659235477 -0.00386006757617 -0.0252340473235 -0.0395757183433 -0.00116509769578 0.0301524624228 -0.00372725259513 -0.0304073672742 0.0317371711135 -0.00692039029673 -0.0261175371706 0.0240502078086 -0.00868919305503 -0.0290734339505 -0.0739856958389 0.0511276461184 0.0285730604082 -0.0671717971563 0.0575826056302 0.0252393428236 -0.070197686553 0.0612765774131 0.0268817096949 0.0294583402574 -0.00562923774123 0.00414758501574 0.0233577005565 -0.0130151677877 -0.00125345273409 0.0309519954026 -0.00872462801635 -0.00288755609654 -0.0101257953793 -0.0729843974113 -0.0340920165181 -0.0151771679521 -0.0751925632358 -0.0358218029141 -0.0162332504988 -0.0693076774478 -0.0359947793186 0.00347416335717 0.00105483434163 -0.0370849557221 -0.000636835582554 -0.00549022806808 -0.0329833440483 -0.0133467214182 0.00200212979689 -0.0306190121919 0.0664181634784 -0.0537509098649 -0.0296265520155 0.072433590889 -0.0604861862957 -0.0301266685128 0.0665984004736 -0.0429504625499 -0.0306054838002 0.0656465515494 0.00329640181735 -0.0110076479614 0.0665407851338 0.00159848632757 -0.00484928302467 0.0583206899464 -0.00380795542151 -0.00911643542349 0.0806738361716 -0.071406044066 -0.00161154090893 0.0870789960027 -0.071105375886 -0.00195318716578 0.0805870443583 -0.075636588037 -0.00136879249476 0.054339889437 -0.00603239936754 0.00829239841551 0.0636151582003 -0.00344871124253 0.0112777063623 0.0458973273635 -0.000604764616583 0.0111892325804 0.0438260957599 0.0375089608133 -0.0233849193901 0.0447824895382 0.0373931154609 -0.0133784590289 0.0520869679749 0.0365738160908 -0.0220487043262 0.0495941489935 -0.00505510251969 0.00281282933429 0.054339889437 -0.00603239936754 0.00829239841551 0.0458973273635 -0.000604764616583 0.0111892325804 -0.0207476206124 -0.00821672473103 -0.0326586216688 -0.0242858082056 -0.0106024397537 -0.0329431965947 -0.0253939889371 -0.00600079027936 -0.0348485857248 0.0665984004736 -0.0429504625499 -0.0306054838002 0.0631862655282 -0.0409915708005 -0.0296012889594 0.0664181634784 -0.0537509098649 -0.0296265520155 -0.0356382876635 -0.0533351935446 0.00276124686934 -0.033011417836 -0.0568285062909 0.00476831337437 -0.0329865068197 -0.039376296103 0.00508400192484 -0.0207476206124 -0.00821672473103 -0.0326586216688 -0.0144916502759 -0.00543817412108 -0.0263480637223 -0.0208534952253 -0.0113473143429 -0.0300426613539 0.0613831505179 -0.0133591145277 0.00220111338422 0.0697568207979 -0.00990358553827 -0.00423297146335 0.0643664449453 -0.0178353134543 0.000139734620461 0.0520869679749 0.0365738160908 -0.0220487043262 0.0447824895382 0.0373931154609 -0.0133784590289 0.0543802753091 0.0360043048859 -0.0105734793469 0.0438971333206 -0.00236639939249 -0.0157472789288 0.0403502397239 -0.00280293240212 -0.0176492966712 0.0442871451378 -0.00394489476457 -0.0271087102592 -0.0731192678213 0.071150586009 0.0126371867955 -0.0785785987973 0.0702539384365 0.013914372772 -0.0723753124475 0.0711080506444 0.0155806122348 0.0784853920341 -0.0390462353826 -2.51326264333e-06 0.075530923903 -0.0326715037227 -0.00224941363558 0.0837335810065 -0.0474399290979 -0.000192326551769 -0.0162332504988 -0.0693076774478 -0.0359947793186 -0.020613707602 -0.0750931277871 -0.0343296974897 -0.0228810738772 -0.0747795701027 -0.0308283958584 0.0738353580236 0.00327896140516 0.000486694596475 0.0725204646587 -0.00817401800305 -0.000920072081499 0.0743348672986 0.00406845519319 -0.00470197200775 -0.0245153158903 0.0113811995834 -0.0345406457782 -0.0276381317526 0.0215675216168 -0.0321951173246 -0.0121780969203 0.0159933697432 -0.0321105457842 -0.0334314070642 -0.0711332336068 0.00546559365466 -0.0351863466203 -0.0765412300825 0.000269046984613 -0.0353057719767 -0.0717958807945 -0.000896993966307 0.073333054781 -0.0605282187462 -0.0343145728111 0.0683374479413 -0.0672494471073 -0.0335944145918 0.0703099220991 -0.0588180907071 -0.0379472970963 -0.0128323584795 -0.0432547368109 -0.0266204569489 -0.0132942395285 -0.0358155034482 -0.0281592346728 -0.01405338943 -0.0357937663794 -0.0235176254064 -0.0656885206699 0.0705391466618 -0.000896229466889 -0.0707638338208 0.0720755532384 0.00634763808921 -0.059178583324 0.0700522735715 -0.00168476975523 0.0821102634072 -0.0652388483286 0.00123241753317 0.0816290304065 -0.0504989773035 0.00101880810689 0.0838564783335 -0.0586024485528 -0.00192842667457 -0.0351685248315 -0.0100314561278 -0.031182685867 -0.0381523445249 -0.00304430956021 -0.029428480193 -0.0333822481334 -0.00652450276539 -0.0326015427709 0.0290143638849 -0.000971557688899 0.00837186072022 0.0294583402574 -0.00562923774123 0.00414758501574 0.0354121625423 -0.000109013933979 0.00450044637546 -0.0353548154235 -0.0399240851402 0.000486829754664 -0.0356382876635 -0.0533351935446 0.00276124686934 -0.0342002138495 -0.0296476446092 0.0025749206543 -0.0354872792959 -0.0626792758703 0.00653606234118 -0.0349772572517 -0.0686603486538 0.00445972103626 -0.0294777080417 -0.0637631043792 0.00494760088623 -0.0824304521084 0.0437800884247 0.0388801582158 -0.0822595283389 0.0367101840675 0.0404899641871 -0.0812487527728 0.04326537624 0.0373680815101 -0.0572370886803 0.056576654315 -0.0142777692527 -0.0582275763154 0.0486685633659 -0.0147868087515 -0.0634358376265 0.0546735934913 -0.0118903163821 -0.0368985123932 -0.0142811005935 0.00302317948081 -0.0413943827152 -0.00729784090072 -6.61109006614e-05 -0.0398653969169 -0.00837041065097 -0.00824570097029 -0.0277498979121 -0.0136683974415 -0.0326979234815 -0.0279543641955 -0.0210831910372 -0.0291056055576 -0.0351685248315 -0.0100314561278 -0.031182685867 -0.0867733284831 0.0609648488462 0.0183455236256 -0.0785785987973 0.0702539384365 0.013914372772 -0.0844842940569 0.0636154636741 0.0146970963106 -0.0572370886803 0.056576654315 -0.0142777692527 -0.0509763434529 0.0460034161806 -0.0177591200918 -0.0582275763154 0.0486685633659 -0.0147868087515 -0.0507754385471 0.0627422481775 0.000138281728141 -0.0497838333249 0.0559922680259 0.00280405301601 -0.0444217473269 0.0602424219251 -0.00442242622375 -0.068583458662 0.0648453906178 0.0195779465139 -0.0649350732565 0.0619430281222 0.0139570971951 -0.0652576088905 0.0690394937992 0.0202288385481 -0.0448838733137 0.00699473544955 0.00491576688364 -0.0480200089514 0.0139896376058 -0.00342765916139 -0.0451306067407 0.00219421950169 -0.00086157215992 0.0564465895295 -0.0182245336473 -0.0300305616111 0.0577224753797 -0.020547606051 -0.0277862790972 0.0570097789168 -0.0103748161346 -0.028088811785 0.0605431683362 0.0167442467064 -0.0403055250645 0.060431715101 0.0229300074279 -0.0391151756048 0.065537057817 0.0151417842135 -0.0360496491194 0.0117476321757 -0.018792707473 -0.00953142251819 0.0200015287846 -0.0166918933392 -0.0100967604667 0.0148993665352 -0.016240587458 -0.00226025399752 0.00324054062366 0.0132272355258 0.00995828956366 -0.00733371544629 0.0176978223026 0.00535471225157 -0.0102530596778 0.0101205231622 0.0068081477657 -0.00338091235608 0.0199865065515 -0.0312582217157 -0.00435515260324 0.025612950325 -0.0247763358057 0.010564208962 0.0239719748497 -0.0332786925137 -0.046883624047 0.00825335178524 -0.0246345344931 -0.0457198508084 0.01298922766 -0.0289378613234 -0.0444579236209 0.00304091628641 -0.0277560744435 0.0301524624228 -0.00372725259513 -0.0304073672742 0.0393960885704 -0.00669425539672 -0.0340571328998 0.0317371711135 -0.00692039029673 -0.0261175371706 -0.0356450267136 -0.0763839781284 0.00708497595042 -0.0334314070642 -0.0711332336068 0.00546559365466 -0.0377826243639 -0.0715468376875 0.00810029637069 -0.0164575334638 0.0125575726852 0.00686521595344 -0.0131820514798 0.0203566141427 0.00420704483986 -0.0181487128139 0.0191756095737 0.00725237466395 -0.0305029321462 -0.0379659235477 -0.00386006757617 -0.0281935930252 -0.027320895344 -0.00371586484835 -0.0252340473235 -0.0395757183433 -0.00116509769578 -0.0356450267136 -0.0763839781284 0.00708497595042 -0.0426602773368 -0.0763025358319 0.00917287915945 -0.0351863466203 -0.0765412300825 0.000269046984613 -0.0252810381353 -0.0247280094773 -0.0293741747737 -0.0242858082056 -0.0106024397537 -0.0329431965947 -0.0236518606544 -0.0245031807572 -0.0287500377744 0.0290143638849 -0.000971557688899 0.00837186072022 0.0354121625423 -0.000109013933979 0.00450044637546 0.0350222811103 0.011573231779 0.00757634406909 0.0675886794925 0.00517250876874 -0.0339571870863 0.0710225701332 0.0116371689364 -0.0303473770618 0.0675277709961 0.00351993273944 -0.0305571258068 0.0356194712222 0.0371010899544 -0.0225756745785 0.0197386574 0.031497310847 -0.0197209063917 0.0319815389812 0.0352998748422 -0.0105315605178 -0.0584039799869 0.0388491488993 0.00238339230418 -0.0615916401148 0.041047938168 0.00514492439106 -0.0646032914519 0.0391447842121 0.00296345702372 0.0668411776423 0.0276774372905 -0.0293406844139 0.0616061538458 0.0335902385414 -0.0206316392869 0.0679226443172 0.029033575207 -0.0159582849592 -0.0529617629945 0.027991278097 -0.0167360249907 -0.0520143918693 0.0224781483412 -0.0120825413615 -0.0616058968008 0.0379795208573 -0.00707599101588 0.0579114630818 0.0292274262756 -0.0363327749074 0.0584006085992 0.0334684699774 -0.0289356615394 0.0668411776423 0.0276774372905 -0.0293406844139 0.00083865004126 0.00723583996296 0.0104546826333 0.00324054062366 0.0132272355258 0.00995828956366 -0.0102530596778 0.0101205231622 0.0068081477657 0.0583206899464 -0.00380795542151 -0.00911643542349 0.0539004430175 -0.00540307629853 -0.00894699897617 0.0556262694299 -0.00370851811022 -0.0167932603508 -0.0243194364011 -0.0365216545761 -0.0246309749782 -0.0187529120594 -0.0492971539497 -0.0287065207958 -0.0174274221063 -0.0494892522693 -0.0261709243059 -0.0647223517299 0.066875346005 0.0136522306129 -0.0606118962169 0.0685695037246 0.00690156780183 -0.0686424598098 0.0706430822611 0.0113607710227 -0.0690960809588 0.0526289157569 0.0247765686363 -0.0781400799751 0.0399436429143 0.0323086194694 -0.0712874382734 0.0450919307768 0.0275897365063 -0.0391113087535 0.0111347045749 -0.0334663800895 -0.0409325622022 0.00616488046944 -0.0320984274149 -0.0457198508084 0.01298922766 -0.0289378613234 -0.00338091235608 0.0199865065515 -0.0312582217157 0.010564208962 0.0239719748497 -0.0332786925137 0.0116761699319 0.0192524939775 -0.0374199561775 0.035920906812 0.0201838165522 -0.0398071408272 0.0347270555794 0.027764454484 -0.0385278463364 0.0504528395832 0.0169036872685 -0.0417337194085 -0.0342002138495 -0.0296476446092 0.0025749206543 -0.0363080054522 -0.0172072835267 0.00185445009265 -0.0334986448288 -0.0282570403069 -0.00100804551039 -0.0723753124475 0.0711080506444 0.0155806122348 -0.0698440819979 0.0758315250278 0.0191484987736 -0.0731192678213 0.071150586009 0.0126371867955 0.0469395145774 0.0357567593455 -0.0306289978325 0.0348762497306 0.0356907360256 -0.0298048574477 0.0438260957599 0.0375089608133 -0.0233849193901 0.0384086482227 0.00514970067888 -0.0410340279341 0.0441741943359 0.0165656842291 -0.0412995144725 0.0458527319133 0.0086536090821 -0.0422105453908 -0.0276381317526 0.0215675216168 -0.0321951173246 -0.0245153158903 0.0113811995834 -0.0345406457782 -0.0329524688423 0.00574303558096 -0.0349177606404 0.0852193087339 -0.0724491924047 0.00872989185154 0.0852778479457 -0.0646592378616 0.00590485380962 0.0814371109009 -0.0711335614324 0.00815863255411 -0.0181399043649 0.0110053224489 -0.0338392443955 -0.0121780969203 0.0159933697432 -0.0321105457842 -0.0122720496729 0.00743707921356 -0.0320300944149 -0.0455527119339 0.00271498505026 -0.0177962481976 -0.0418213754892 -0.00742403836921 -0.0206348095089 -0.0434817001224 0.000976005452685 -0.00737236114219 -0.0721085593104 0.0587044768035 -0.00398772349581 -0.0771700739861 0.0674374252558 0.00493047386408 -0.0699571371078 0.0646914690733 -0.00456732325256 0.0148993665352 -0.016240587458 -0.00226025399752 0.00798164401203 -0.0143224736676 0.00208706385456 0.00139058486093 -0.0184520073235 -0.00464215269312 0.0240502078086 -0.00868919305503 -0.0290734339505 0.0193834770471 -0.0151846110821 -0.0219733603299 0.0147100556642 -0.0147073278204 -0.0253510158509 0.0395265072584 -0.00346563779749 -0.0100429235026 0.0366409905255 -0.00356341060251 -0.00233652559109 0.0309519954026 -0.00872462801635 -0.00288755609654 0.0636151582003 -0.00344871124253 0.0112777063623 0.0544707849622 0.00255479058251 0.0128589896485 0.0458973273635 -0.000604764616583 0.0111892325804 -0.0911771580577 0.0422582998872 0.0333826653659 -0.0878712534904 0.0469209626317 0.0273304414004 -0.0873394012451 0.0418514795601 0.0236989371479 -0.00612018536776 -0.0598083510995 -0.0299312565476 -0.0095995971933 -0.0609665364027 -0.0261474959552 -0.0120546650141 -0.0671373382211 -0.028148625046 -0.0149490861222 -0.0439244024456 -0.0251441132277 -0.01405338943 -0.0357937663794 -0.0235176254064 -0.0171560104936 -0.0368272177875 -0.0223063975573 -0.0785785987973 0.0702539384365 0.013914372772 -0.0729605704546 0.0639232620597 0.0233493559062 -0.0723753124475 0.0711080506444 0.0155806122348 0.0211553759873 0.0312036816031 -0.0245885774493 0.0348762497306 0.0356907360256 -0.0298048574477 0.0339798666537 0.0333764031529 -0.0331196337938 0.0600861534476 -0.00594944367185 -0.0371469557285 0.0675886794925 0.00517250876874 -0.0339571870863 0.0602696500719 -0.00843365583569 -0.0316083207726 0.0665407851338 0.00159848632757 -0.00484928302467 0.0666749030352 -0.00735376635566 -0.00298768025823 0.0624106451869 -0.00390694057569 -0.00189818732906 0.048808876425 0.0252794381231 0.00892338063568 0.0447547212243 0.0325945727527 0.00325110368431 0.0374021679163 0.0269114822149 0.00774644361809 0.0292802155018 -0.012616426684 -0.0106529798359 0.0395265072584 -0.00346563779749 -0.0100429235026 0.0309519954026 -0.00872462801635 -0.00288755609654 0.063760265708 -0.0543169416487 -0.0341284386814 0.0627965405583 -0.0644452646375 -0.0340751446784 0.0649472996593 -0.0564471706748 -0.0301647242159 0.0356194712222 0.0371010899544 -0.0225756745785 0.0447824895382 0.0373931154609 -0.0133784590289 0.0438260957599 0.0375089608133 -0.0233849193901 -0.0306212659925 0.0172087047249 0.0100856469944 -0.030961079523 0.00549497501925 0.0111165465787 -0.0233208872378 0.0171473640949 0.00885204598308 -0.0729605704546 0.0639232620597 0.0233493559062 -0.068583458662 0.0648453906178 0.0195779465139 -0.0723753124475 0.0711080506444 0.0155806122348 0.0656110197306 -0.0270528197289 -0.0288560781628 0.0669340640306 -0.0361999757588 -0.031447045505 0.0662746876478 -0.0250612292439 -0.0328748524189 0.0530218444765 0.0132510503754 0.0114024560899 0.0422127395868 0.0228577423841 0.00999154523015 0.0477037541568 0.0117063503712 0.0115124462172 -0.0379135832191 -0.00708554778248 0.00403723120689 -0.0361147299409 -0.0120508149266 0.00691540632397 -0.0364621728659 -4.06305734941e-05 0.00923090986907 0.0784853920341 -0.0390462353826 -2.51326264333e-06 0.0697775110602 -0.0257171951234 0.000200476919417 0.075530923903 -0.0326715037227 -0.00224941363558 0.0713142603636 0.0189655330032 -0.0151191568002 0.0696674659848 0.0262376572937 -0.0128174172714 0.0736648738384 0.0202186964452 -0.00637979293242 -0.0631757378578 0.0389323234558 -0.00277568120509 -0.0616058968008 0.0379795208573 -0.00707599101588 -0.0520143918693 0.0224781483412 -0.0120825413615 -0.00599885499105 -0.0184409264475 -0.015290110372 -0.0216245427728 -0.0176681727171 -0.0117809949443 -0.0251056849957 -0.0178923550993 -0.0185515768826 0.0680408999324 0.0129547277465 0.00671541178599 0.0655900388956 0.0184390544891 0.00736436760053 0.0643844082952 0.0092457216233 0.0105861956254 -0.0646032914519 0.0391447842121 0.00296345702372 -0.0615916401148 0.041047938168 0.00514492439106 -0.0680031627417 0.0388116128743 0.0118984086439 -0.0337675176561 -0.0367394164205 -0.00267041777261 -0.0356382876635 -0.0533351935446 0.00276124686934 -0.0353548154235 -0.0399240851402 0.000486829754664 -0.0089406510815 0.0311568155885 -0.0189374648035 -0.0210811626166 0.0406454503536 -0.0149188544601 -0.00303131062537 0.0293679926544 -0.0160501208156 0.0600458458066 -0.00507800886407 -0.0249140188098 0.0512403547764 -0.00441910745576 -0.0253982823342 0.0570097789168 -0.0103748161346 -0.028088811785 0.0711485892534 0.0247458182275 -0.00319633609615 0.065167747438 0.0291529707611 -0.00138657167554 0.0627590343356 0.0254758372903 0.00493016792461 -0.0812487527728 0.04326537624 0.0373680815101 -0.0781400799751 0.0399436429143 0.0323086194694 -0.0739856958389 0.0511276461184 0.0285730604082 -0.0699571371078 0.0646914690733 -0.00456732325256 -0.0634358376265 0.0546735934913 -0.0118903163821 -0.0721085593104 0.0587044768035 -0.00398772349581 0.0891577154398 -0.0754422545433 0.00495696906 0.0842919945717 -0.076128102839 0.00855887308717 0.0858451649547 -0.0757835879922 -0.00102130440064 0.0126880630851 0.0209196154028 0.00636390130967 0.023449998349 0.0172611102462 0.00965423230082 0.0262714382261 0.024643054232 0.00333152944222 -0.0321104675531 -0.00197183503769 -0.03518948704 -0.0333822481334 -0.00652450276539 -0.0326015427709 -0.0366347320378 0.0017704871716 -0.0322802253067 0.0627965405583 -0.0644452646375 -0.0340751446784 0.063760265708 -0.0543169416487 -0.0341284386814 0.0644002556801 -0.0638302341104 -0.0359592176974 -0.0804472342134 0.0510345138609 0.0322105102241 -0.0845351070166 0.0501509457827 0.0333207882941 -0.0877385362983 0.0418181084096 0.0414660908282 -0.068583458662 0.0648453906178 0.0195779465139 -0.0660762935877 0.0578988678753 0.0189452208579 -0.0649350732565 0.0619430281222 0.0139570971951 -0.0132942395285 -0.0358155034482 -0.0281592346728 -0.0151859046891 -0.0280584804714 -0.02708007209 -0.01405338943 -0.0357937663794 -0.0235176254064 -0.0680031627417 0.0388116128743 0.0118984086439 -0.0709582194686 0.0410792566836 0.0236960314214 -0.0745909139514 0.0392232574522 0.0197847299278 -0.0444579236209 0.00304091628641 -0.0277560744435 -0.0381523445249 -0.00304430956021 -0.029428480193 -0.0414947420359 -0.00295710819773 -0.0225406978279 0.0688916966319 0.00488886702806 0.00309444754384 0.0665930733085 -0.00253646099009 0.00908811856061 0.0738353580236 0.00327896140516 0.000486694596475 0.0604395531118 -0.014644660987 -0.0287422351539 0.0656110197306 -0.0270528197289 -0.0288560781628 0.0662746876478 -0.0250612292439 -0.0328748524189 -0.0542124435306 0.0460482388735 0.00364887295291 -0.0614165142179 0.0504314303398 0.0064328792505 -0.0572749041021 0.046243134886 0.00337952957489 -0.0209524612874 -0.0299015939236 -0.0216957069933 -0.0171560104936 -0.0368272177875 -0.0223063975573 -0.0167125929147 -0.0282691419125 -0.0229621194303 0.0585702136159 -0.00812319666147 0.00419311970472 0.0666749030352 -0.00735376635566 -0.00298768025823 0.0613831505179 -0.0133591145277 0.00220111338422 0.0249150972813 0.0107660228387 -0.0397206582129 0.0182611495256 -0.000761016330216 -0.0385480634868 0.0173371694982 0.00796426646411 -0.0401615314186 -0.0622629895806 0.0477367714047 0.00780300609767 -0.0614165142179 0.0504314303398 0.0064328792505 -0.0635816827416 0.0559310466051 0.0108306938782 -0.0136914439499 -0.0546173118055 -0.0263203363866 -0.0174274221063 -0.0494892522693 -0.0261709243059 -0.0150378849357 -0.0664253011346 -0.026939317584 0.0644002556801 -0.0638302341104 -0.0359592176974 0.058990996331 -0.0744717493653 -0.0386376976967 0.0627965405583 -0.0644452646375 -0.0340751446784 0.0700432732701 0.0106197241694 -0.0254250112921 0.0675277709961 0.00351993273944 -0.0305571258068 0.0710225701332 0.0116371689364 -0.0303473770618 -0.016403356567 -0.00939153227955 -0.00214911578223 -0.00657241046429 -0.0163257047534 -0.00350670074113 -0.012282749638 -0.00724740931764 -2.84041252598e-05 -0.00435515260324 0.025612950325 -0.0247763358057 -0.0111597320065 0.0221810676157 -0.0287142917514 -0.012165626511 0.0265315938741 -0.0258189272135 -0.0295234173536 0.0311564076692 0.00292094191536 -0.0278973150998 0.0252974294126 0.00663841702044 -0.0218933653086 0.0331122018397 -0.000901366525795 0.0586783364415 -0.0037231084425 -0.00361427664757 0.0665407851338 0.00159848632757 -0.00484928302467 0.0624106451869 -0.00390694057569 -0.00189818732906 0.0683374479413 -0.0672494471073 -0.0335944145918 0.072433590889 -0.0604861862957 -0.0301266685128 0.0650694146752 -0.0661949291825 -0.0290601216257 -0.0846682190895 0.0387630797923 0.0249170754105 -0.0873394012451 0.0418514795601 0.0236989371479 -0.0871415585279 0.0463454276323 0.0139984423295 0.0704541727901 -0.00372631778009 0.00695726461709 0.0719613134861 -0.0083319125697 0.00354015408084 0.0738353580236 0.00327896140516 0.000486694596475 -0.0838714838028 0.0507906712592 0.00711987819523 -0.0789092034101 0.0428538136184 0.00382694439031 -0.0825153067708 0.041629076004 0.009376286529 0.0197910871357 -0.00383032113314 0.00980013236403 0.014073533006 -0.000268704665359 0.0117724873126 0.0129595380276 -0.00801198463887 0.00812369771302 -0.0212532021105 -0.0387861654162 -0.0227633845061 -0.0243194364011 -0.0365216545761 -0.0246309749782 -0.0174274221063 -0.0494892522693 -0.0261709243059 -0.0893638506532 0.0394080318511 0.0356427282095 -0.0911771580577 0.0422582998872 0.0333826653659 -0.0914800092578 0.0378939323127 0.0321716368198 -0.0859415605664 0.0709468647838 0.00737344101071 -0.0836498662829 0.0665282458067 0.00920514483005 -0.0800869390368 0.0719245970249 0.00986813567579 0.0144752385095 -0.0070848762989 -0.034492328763 0.0182611495256 -0.000761016330216 -0.0385480634868 0.0240502078086 -0.00868919305503 -0.0290734339505 -0.0631757378578 0.0389323234558 -0.00277568120509 -0.0520143918693 0.0224781483412 -0.0120825413615 -0.051504354924 0.0203009396791 -0.0043327328749 0.0623909682035 -0.0175557024777 -4.21606127929e-05 0.0697775110602 -0.0257171951234 0.000200476919417 0.0622591376305 -0.020616741851 0.00515010254458 0.07365013659 0.0133140301332 -0.000675909686834 0.0747518464923 0.0119193941355 -0.00421227002516 0.0736648738384 0.0202186964452 -0.00637979293242 0.0144752385095 -0.0070848762989 -0.034492328763 0.00936001725495 -0.00345246354118 -0.0368482433259 0.0182611495256 -0.000761016330216 -0.0385480634868 0.0604619793594 -0.0071836868301 0.00888195540756 0.0682200118899 -0.00788477621973 0.00767027726397 0.0665930733085 -0.00253646099009 0.00908811856061 0.0891577154398 -0.0754422545433 0.00495696906 0.0852193087339 -0.0724491924047 0.00872989185154 0.0842919945717 -0.076128102839 0.00855887308717 -0.0737345814705 0.038439001888 0.0121840443462 -0.0825153067708 0.041629076004 0.009376286529 -0.0789092034101 0.0428538136184 0.00382694439031 0.036672718823 0.0113793108612 -0.0394277796149 0.0441741943359 0.0165656842291 -0.0412995144725 0.0384086482227 0.00514970067888 -0.0410340279341 0.0710225701332 0.0116371689364 -0.0303473770618 0.0694160535932 0.0198294073343 -0.0330271050334 0.0717578157783 0.015957320109 -0.0263392049819 -0.0342719815671 -0.0143612474203 -0.0263750851154 -0.0383309051394 -0.00495197204873 -0.0253514498472 -0.0351685248315 -0.0100314561278 -0.031182685867 0.0852193087339 -0.0724491924047 0.00872989185154 0.0782531201839 -0.0722363963723 0.00564285600558 0.0767389982939 -0.0762002691627 0.00715722655877 0.0555628426373 -0.0745320767164 -0.0335095077753 0.0609687641263 -0.0750585868955 -0.0276746544987 0.0574748292565 -0.0728332474828 -0.0297978837043 0.0816290304065 -0.0504989773035 0.00101880810689 0.0809005796909 -0.0487979911268 0.00395165523514 0.0769165381789 -0.0372788608074 0.004157373216 -0.0272913184017 0.00181260507088 0.0117624523118 -0.0207055173814 0.00392329553142 0.00909946020693 -0.0164575334638 0.0125575726852 0.00686521595344 0.0842658951879 -0.0518600940704 0.00558784976602 0.0855906233191 -0.056857522577 0.00729697011411 0.0917203947902 -0.059632204473 0.00453671533614 -0.0322041623294 0.0489787906408 -0.00726023875177 -0.0214076433331 0.0391493700445 -0.00776851270348 -0.0386477708817 0.0575842261314 -0.0097325630486 0.0470996797085 -0.0131811331958 -0.028420528397 0.0512403547764 -0.00441910745576 -0.0253982823342 0.0432890281081 -0.00950278807431 -0.0291274245828 -0.0337675176561 -0.0367394164205 -0.00267041777261 -0.0345421843231 -0.0537865906954 -3.66152089555e-05 -0.0356382876635 -0.0533351935446 0.00276124686934 -0.084884122014 0.054450109601 0.0266954693943 -0.0884201526642 0.046169500798 0.0338194072247 -0.0845351070166 0.0501509457827 0.0333207882941 -0.0143243018538 -0.0176195204258 -0.00678763026372 -0.0268499869853 -0.016901101917 -0.00799196213484 -0.0216245427728 -0.0176681727171 -0.0117809949443 -0.0292823426425 -0.016650011763 -0.0205444768071 -0.0325671248138 -0.0150363976136 -0.00584217207506 -0.0342719815671 -0.0143612474203 -0.0263750851154 0.0350222811103 0.011573231779 0.00757634406909 0.0385896004736 0.000291874835966 0.00444744434208 0.0378097891808 0.00806960370392 0.00718982517719 -0.0473023541272 0.0122802108526 -0.0124696297571 -0.0520143918693 0.0224781483412 -0.0120825413615 -0.0483878850937 0.0177123211324 -0.0199927464128 -0.0509763434529 0.0460034161806 -0.0177591200918 -0.0412645675242 0.0296845398843 -0.0242789257318 -0.0521116182208 0.0417004041374 -0.0192602630705 0.0442871451378 -0.00394489476457 -0.0271087102592 0.0400369912386 -0.00509023061022 -0.0284015871584 0.0432890281081 -0.00950278807431 -0.0291274245828 -0.0317054688931 -0.00275706243701 0.0117793781683 -0.0272913184017 0.00181260507088 0.0117624523118 -0.030961079523 0.00549497501925 0.0111165465787 0.0769819021225 -0.0300572067499 -0.00352066406049 0.0721606612206 -0.022786539048 -0.00311841559596 0.0825529024005 -0.0241261273623 -0.00263262726367 -0.0637906864285 0.0637098252773 -0.00896131899208 -0.0634358376265 0.0546735934913 -0.0118903163821 -0.0699571371078 0.0646914690733 -0.00456732325256 -0.0631757378578 0.0389323234558 -0.00277568120509 -0.0604898072779 0.0358108095825 -0.000405532977311 -0.0646032914519 0.0391447842121 0.00296345702372 -0.0325671248138 -0.0150363976136 -0.00584217207506 -0.0368985123932 -0.0142811005935 0.00302317948081 -0.0363425649703 -0.0124280676246 -0.00762832257897 -0.015117011033 -0.0188090614974 -0.0260683037341 -0.00599885499105 -0.0184409264475 -0.015290110372 -0.0173668675125 -0.0188725441694 -0.0200483836234 0.0384086482227 0.00514970067888 -0.0410340279341 0.0380213819444 -0.000848870549817 -0.0383270718157 0.0312596708536 0.00204978929833 -0.0341790989041 0.0418007522821 -0.00242996658199 -0.00372027652338 0.0385896004736 0.000291874835966 0.00444744434208 0.0366409905255 -0.00356341060251 -0.00233652559109 -0.0918235704303 0.0354779586196 0.0378490537405 -0.0916560813785 0.0415009707212 0.0378023125231 -0.0893638506532 0.0394080318511 0.0356427282095 0.0672424212098 0.0293734762818 -0.00736515130848 0.0711485892534 0.0247458182275 -0.00319633609615 0.0679226443172 0.029033575207 -0.0159582849592 -0.0647223517299 0.066875346005 0.0136522306129 -0.0680218562484 0.0746396705508 0.0158250704408 -0.0661604255438 0.0740167275071 0.0206666216254 -0.0473023541272 0.0122802108526 -0.0124696297571 -0.0434817001224 0.000976005452685 -0.00737236114219 -0.0480200089514 0.0139896376058 -0.00342765916139 0.0574302785099 -0.0164519175887 -0.0373466834426 0.0605014450848 -0.0130754690617 -0.0308906380087 0.0605648122728 -0.0226184297353 -0.0335885249078 0.0577097833157 -0.0276579707861 -0.0262848697603 0.0631862655282 -0.0409915708005 -0.0296012889594 0.0656110197306 -0.0270528197289 -0.0288560781628 0.0583206899464 -0.00380795542151 -0.00911643542349 0.0565774738789 -0.00623939558864 -0.00480305217206 0.0539004430175 -0.00540307629853 -0.00894699897617 0.0326762907207 -0.00954702217132 -0.0169461015612 0.0395265072584 -0.00346563779749 -0.0100429235026 0.0292802155018 -0.012616426684 -0.0106529798359 0.0200015287846 -0.0166918933392 -0.0100967604667 0.0292802155018 -0.012616426684 -0.0106529798359 0.0233577005565 -0.0130151677877 -0.00125345273409 0.0197910871357 -0.00383032113314 0.00980013236403 0.0129595380276 -0.00801198463887 0.00812369771302 0.0222891252488 -0.0072523932904 0.00697265705094 0.0754076316953 -0.0163155626506 0.0023912510369 0.0725204646587 -0.00817401800305 -0.000920072081499 0.0719613134861 -0.0083319125697 0.00354015408084 -0.017013894394 -0.0347188636661 -0.030624659732 -0.0151859046891 -0.0280584804714 -0.02708007209 -0.0132942395285 -0.0358155034482 -0.0281592346728 -0.0234806258231 0.0277767758816 -0.028870517388 -0.0121780969203 0.0159933697432 -0.0321105457842 -0.0276381317526 0.0215675216168 -0.0321951173246 -0.059178583324 0.0700522735715 -0.00168476975523 -0.0707638338208 0.0720755532384 0.00634763808921 -0.0606118962169 0.0685695037246 0.00690156780183 0.048808876425 0.0252794381231 0.00892338063568 0.0511791184545 0.0201472640038 0.0112951202318 0.0585609525442 0.0207895357162 0.00992077030241 -0.0272648502141 0.0430674515665 -0.0166629143059 -0.0379568003118 0.0557544454932 -0.013513436541 -0.0210811626166 0.0406454503536 -0.0149188544601 -0.0352949239314 0.0277008544654 0.00385763379745 -0.0395993106067 0.0181454904377 0.00810672808439 -0.0355813615024 0.0215603802353 0.00780190061778 0.0609687641263 -0.0750585868955 -0.0276746544987 0.0672754794359 -0.0748537257314 -0.028308160603 0.0650694146752 -0.0661949291825 -0.0290601216257 0.0248502586037 0.00308163510635 0.0112843178213 0.0197910871357 -0.00383032113314 0.00980013236403 0.0290143638849 -0.000971557688899 0.00837186072022 -0.0299632400274 -0.0203246790916 -0.026556706056 -0.0243194364011 -0.0365216545761 -0.0246309749782 -0.0292823426425 -0.016650011763 -0.0205444768071 -0.074530698359 0.0564152859151 0.000770429789554 -0.0838714838028 0.0507906712592 0.00711987819523 -0.0803875476122 0.0608413852751 0.00552876945585 -0.0322233475745 -0.0618353933096 -0.00231006834656 -0.0300818383694 -0.0526309125125 -0.000229920042329 -0.0297503750771 -0.0631617978215 0.000223000184633 -0.0190910883248 0.0310631301254 -0.0254277605563 -0.0111597320065 0.0221810676157 -0.0287142917514 -0.0234806258231 0.0277767758816 -0.028870517388 -0.0297503750771 -0.0631617978215 0.000223000184633 -0.033697348088 -0.0659902095795 0.000380541518098 -0.0322233475745 -0.0618353933096 -0.00231006834656 -0.0686424598098 0.0706430822611 0.0113607710227 -0.0606118962169 0.0685695037246 0.00690156780183 -0.0707638338208 0.0720755532384 0.00634763808921 0.0355566330254 -0.0077797062695 -0.0261449292302 0.0403502397239 -0.00280293240212 -0.0176492966712 0.0326762907207 -0.00954702217132 -0.0169461015612 0.0600458458066 -0.00507800886407 -0.0249140188098 0.0622391216457 0.00130491273012 -0.0162859521806 0.0556262694299 -0.00370851811022 -0.0167932603508 -0.0584039799869 0.0388491488993 0.00238339230418 -0.0646032914519 0.0391447842121 0.00296345702372 -0.0604898072779 0.0358108095825 -0.000405532977311 -0.0454866327345 0.0472589023411 0.000885553658009 -0.0400194935501 0.035411670804 -0.00117507681716 -0.0314232259989 0.0354066677392 -0.00278120324947 -0.035688880831 0.0322657860816 -0.00136898260098 -0.0352949239314 0.0277008544654 0.00385763379745 -0.0295234173536 0.0311564076692 0.00292094191536 -0.0497838333249 0.0559922680259 0.00280405301601 -0.0384465046227 0.0464623495936 -0.00204830104485 -0.0444217473269 0.0602424219251 -0.00442242622375 0.0636151582003 -0.00344871124253 0.0112777063623 0.0604619793594 -0.0071836868301 0.00888195540756 0.0665930733085 -0.00253646099009 0.00908811856061 -0.0594911910594 0.0402323678136 -0.0138589777052 -0.0473444573581 0.0222850441933 -0.0223869681358 -0.0529617629945 0.027991278097 -0.0167360249907 0.0605431683362 0.0167442467064 -0.0403055250645 0.0554471611977 0.0115022500977 -0.042177259922 0.051399409771 0.0217050053179 -0.0416473895311 -0.013514730148 0.0359381325543 -0.00797349400818 -0.00179908366408 0.0287952888757 -0.0084942439571 -0.00303131062537 0.0293679926544 -0.0160501208156 -0.0234806258231 0.0277767758816 -0.028870517388 -0.0276381317526 0.0215675216168 -0.0321951173246 -0.0281191021204 0.0276504568756 -0.0284133274108 0.0366409905255 -0.00356341060251 -0.00233652559109 0.0385896004736 0.000291874835966 0.00444744434208 0.0354121625423 -0.000109013933979 0.00450044637546 0.0649472996593 -0.0564471706748 -0.0301647242159 0.0610778927803 -0.0446138717234 -0.033351752907 0.063760265708 -0.0543169416487 -0.0341284386814 0.0665984004736 -0.0429504625499 -0.0306054838002 0.072433590889 -0.0604861862957 -0.0301266685128 0.0714298635721 -0.0534965991974 -0.0324478931725 0.0706675350666 0.0171718429774 0.00147831998765 0.0738353580236 0.00327896140516 0.000486694596475 0.07365013659 0.0133140301332 -0.000675909686834 0.0924971923232 -0.0599343143404 0.00196787621826 0.0855331420898 -0.0662355422974 -0.000724932178855 0.0889291241765 -0.0584181435406 -0.00213292124681 -0.0268499869853 -0.016901101917 -0.00799196213484 -0.0262299366295 -0.017805961892 -0.00452885916457 -0.0325671248138 -0.0150363976136 -0.00584217207506 -0.0709582194686 0.0410792566836 0.0236960314214 -0.0643574744463 0.0411904193461 0.0170047152787 -0.0712874382734 0.0450919307768 0.0275897365063 0.0662746876478 -0.0250612292439 -0.0328748524189 0.068005733192 -0.0399323999882 -0.0337682925165 0.0629565417767 -0.0314845256507 -0.0355579890311 0.0585609525442 0.0207895357162 0.00992077030241 0.0643844082952 0.0092457216233 0.0105861956254 0.0655900388956 0.0184390544891 0.00736436760053 0.0564444027841 -0.00904699414968 -0.0371534638107 0.052886724472 -0.00321291876025 -0.0398595966399 0.0600861534476 -0.00594944367185 -0.0371469557285 0.0447547212243 0.0325945727527 0.00325110368431 0.0347373187542 0.0342971347272 -0.00132000620943 0.0336626619101 0.0301720779389 0.00381930125877 -0.0647223517299 0.066875346005 0.0136522306129 -0.0661604255438 0.0740167275071 0.0206666216254 -0.0652576088905 0.0690394937992 0.0202288385481 0.0102474931628 0.0033322009258 -0.0393172241747 0.0173371694982 0.00796426646411 -0.0401615314186 0.0182611495256 -0.000761016330216 -0.0385480634868 -0.000636835582554 -0.00549022806808 -0.0329833440483 -0.00354365818202 -0.0125812022015 -0.0245189536363 -0.0137986438349 -0.001824338804 -0.028773734346 0.0520869679749 0.0365738160908 -0.0220487043262 0.0469395145774 0.0357567593455 -0.0306289978325 0.0438260957599 0.0375089608133 -0.0233849193901 -0.0306760501117 -0.0296483207494 0.00481163570657 -0.0342002138495 -0.0296476446092 0.0025749206543 -0.0329865068197 -0.039376296103 0.00508400192484 -0.0836498662829 0.0665282458067 0.00920514483005 -0.0847244933248 0.0610410422087 0.010350859724 -0.0844842940569 0.0636154636741 0.0146970963106 0.0684423595667 0.00943436846137 -0.0206362344325 0.0713142603636 0.0189655330032 -0.0151191568002 0.0687282457948 0.0103019243106 -0.0152066750452 -0.0847748368979 0.0726360008121 0.00620254734531 -0.0851302146912 0.0776748657227 0.00939944665879 -0.0771700739861 0.0674374252558 0.00493047386408 0.0874174982309 -0.0661162883043 0.0035401773639 0.0852193087339 -0.0724491924047 0.00872989185154 0.0891577154398 -0.0754422545433 0.00495696906 0.0126880630851 0.0209196154028 0.00636390130967 0.00612076325342 0.0238218400627 0.00119427102618 -0.00275399489328 0.0223833154887 0.00140674877912 0.0312596708536 0.00204978929833 -0.0341790989041 0.032403241843 0.0125592537224 -0.0369361899793 0.036672718823 0.0113793108612 -0.0394277796149 0.0839467793703 -0.0338173881173 -0.0013689303305 0.089011117816 -0.049574509263 0.0023703314364 0.0870006456971 -0.046893555671 0.000147852333612 -0.0764897316694 0.0619541816413 0.00187294220086 -0.0721085593104 0.0587044768035 -0.00398772349581 -0.074530698359 0.0564152859151 0.000770429789554 0.00984027050436 0.0123339463025 -0.039398021996 0.00251939380541 0.00667322427034 -0.0372835509479 -0.00667092064396 0.0126474965364 -0.0336418747902 -0.0252886787057 0.0373778603971 -0.00468408223242 -0.0384465046227 0.0464623495936 -0.00204830104485 -0.0314232259989 0.0354066677392 -0.00278120324947 0.0662746876478 -0.0250612292439 -0.0328748524189 0.0669340640306 -0.0361999757588 -0.031447045505 0.068005733192 -0.0399323999882 -0.0337682925165 0.0605648122728 -0.0226184297353 -0.0335885249078 0.0605014450848 -0.0130754690617 -0.0308906380087 0.0662746876478 -0.0250612292439 -0.0328748524189 0.0020479504019 0.0282315947115 -0.00889560487121 0.00612076325342 0.0238218400627 0.00119427102618 0.0074149183929 0.0266806911677 -0.00382403749973 0.0592680051923 -0.00785803515464 -0.0265315212309 0.0577224753797 -0.020547606051 -0.0277862790972 0.0604395531118 -0.014644660987 -0.0287422351539 0.0720914751291 0.00546024832875 -0.010403117165 0.0735007673502 0.0154050234705 -0.0108875501901 0.0743348672986 0.00406845519319 -0.00470197200775 -0.0383309051394 -0.00495197204873 -0.0253514498472 -0.0381523445249 -0.00304430956021 -0.029428480193 -0.0351685248315 -0.0100314561278 -0.031182685867 0.066437445581 -0.0657137334347 -0.0362850166857 0.0644002556801 -0.0638302341104 -0.0359592176974 0.0703099220991 -0.0588180907071 -0.0379472970963 0.0226106531918 0.0281752999872 -0.00177787418943 0.0126880630851 0.0209196154028 0.00636390130967 0.0262714382261 0.024643054232 0.00333152944222 0.0874174982309 -0.0661162883043 0.0035401773639 0.0870789960027 -0.071105375886 -0.00195318716578 0.0855331420898 -0.0662355422974 -0.000724932178855 -0.0356382876635 -0.0533351935446 0.00276124686934 -0.0354872792959 -0.0626792758703 0.00653606234118 -0.033011417836 -0.0568285062909 0.00476831337437 -0.0859415605664 0.0709468647838 0.00737344101071 -0.0800869390368 0.0719245970249 0.00986813567579 -0.0851302146912 0.0776748657227 0.00939944665879 0.0665930733085 -0.00253646099009 0.00908811856061 0.0704541727901 -0.00372631778009 0.00695726461709 0.0738353580236 0.00327896140516 0.000486694596475 -0.0664762184024 0.0484478063881 0.0224707052112 -0.0660762935877 0.0578988678753 0.0189452208579 -0.0690960809588 0.0526289157569 0.0247765686363 0.0261729359627 0.0250671524554 -0.0349872037768 0.0116761699319 0.0192524939775 -0.0374199561775 0.010564208962 0.0239719748497 -0.0332786925137 -0.0149490861222 -0.0439244024456 -0.0251441132277 -0.0136914439499 -0.0546173118055 -0.0263203363866 -0.0128323584795 -0.0432547368109 -0.0266204569489 0.0696674659848 0.0262376572937 -0.0128174172714 0.0709100887179 0.0228983853012 -0.0246697273105 0.0679226443172 0.029033575207 -0.0159582849592 0.0738353580236 0.00327896140516 0.000486694596475 0.0719613134861 -0.0083319125697 0.00354015408084 0.0725204646587 -0.00817401800305 -0.000920072081499 -0.042618881911 -0.00202813232318 0.0045546782203 -0.0448838733137 0.00699473544955 0.00491576688364 -0.0451306067407 0.00219421950169 -0.00086157215992 0.0604619793594 -0.0071836868301 0.00888195540756 0.0636151582003 -0.00344871124253 0.0112777063623 0.054339889437 -0.00603239936754 0.00829239841551 0.0400369912386 -0.00509023061022 -0.0284015871584 0.0442871451378 -0.00394489476457 -0.0271087102592 0.0403502397239 -0.00280293240212 -0.0176492966712 0.0100417975336 0.0276991538703 -0.0274829212576 0.0226895809174 0.0281979069114 -0.0306504108012 0.010564208962 0.0239719748497 -0.0332786925137 0.0504876486957 0.00247842236422 -0.0409079678357 0.0504528395832 0.0169036872685 -0.0417337194085 0.0554471611977 0.0115022500977 -0.042177259922 -0.0877385362983 0.0418181084096 0.0414660908282 -0.0845351070166 0.0501509457827 0.0333207882941 -0.0916560813785 0.0415009707212 0.0378023125231 0.0627965405583 -0.0644452646375 -0.0340751446784 0.058990996331 -0.0744717493653 -0.0386376976967 0.0555628426373 -0.0745320767164 -0.0335095077753 0.0604395531118 -0.014644660987 -0.0287422351539 0.0662746876478 -0.0250612292439 -0.0328748524189 0.0605014450848 -0.0130754690617 -0.0308906380087 0.0117476321757 -0.018792707473 -0.00953142251819 0.00139058486093 -0.0184520073235 -0.00464215269312 -0.00245248852298 -0.0189631339163 -0.0111456979066 -0.0181399043649 0.0110053224489 -0.0338392443955 -0.0171094201505 0.00397073151544 -0.0321909785271 -0.0245153158903 0.0113811995834 -0.0345406457782 -0.0368985123932 -0.0142811005935 0.00302317948081 -0.0398653969169 -0.00837041065097 -0.00824570097029 -0.0363425649703 -0.0124280676246 -0.00762832257897 -0.00354365818202 -0.0125812022015 -0.0245189536363 -0.00552152097225 -0.0164588801563 -0.0201352518052 -0.0097112422809 -0.0125950593501 -0.0233362093568 0.0422127395868 0.0228577423841 0.00999154523015 0.0317819230258 0.0191275794059 0.00692741526291 0.0405975058675 0.0136088356376 0.009625967592 -0.0262299366295 -0.017805961892 -0.00452885916457 -0.0281935930252 -0.027320895344 -0.00371586484835 -0.0295641105622 -0.0183343663812 -0.00392657378688 0.0294583402574 -0.00562923774123 0.00414758501574 0.0309519954026 -0.00872462801635 -0.00288755609654 0.0366409905255 -0.00356341060251 -0.00233652559109 -0.0572749041021 0.046243134886 0.00337952957489 -0.0440319478512 0.0313652604818 0.0016547862906 -0.0542124435306 0.0460482388735 0.00364887295291 -0.0477432273328 0.0646687820554 -0.00769540853798 -0.0379568003118 0.0557544454932 -0.013513436541 -0.0493567548692 0.0630387067795 -0.0120902014896 -0.0390509627759 -0.0665916949511 0.00520234834403 -0.0356382876635 -0.0533351935446 0.00276124686934 -0.0391314774752 -0.0653252974153 0.00145680934656 -0.0529348887503 0.0521784499288 0.0046043144539 -0.0649350732565 0.0619430281222 0.0139570971951 -0.0635816827416 0.0559310466051 0.0108306938782 0.0261729359627 0.0250671524554 -0.0349872037768 0.0347270555794 0.027764454484 -0.0385278463364 0.035920906812 0.0201838165522 -0.0398071408272 -0.020558193326 -0.0756064131856 -0.0252870116383 -0.0106726381928 -0.0760597363114 -0.026934184134 -0.0149209909141 -0.0729868486524 -0.0253179427236 -0.0812487527728 0.04326537624 0.0373680815101 -0.0804472342134 0.0510345138609 0.0322105102241 -0.0824304521084 0.0437800884247 0.0388801582158 0.0393960885704 -0.00669425539672 -0.0340571328998 0.0380213819444 -0.000848870549817 -0.0383270718157 0.0420701652765 -0.00905739609152 -0.0347143113613 -0.0281935930252 -0.027320895344 -0.00371586484835 -0.0222899951041 -0.0185299292207 -0.00168474740349 -0.022982083261 -0.0271414145827 -3.11253916152e-06 -0.020558193326 -0.0756064131856 -0.0252870116383 -0.0228810738772 -0.0747795701027 -0.0308283958584 -0.020613707602 -0.0750931277871 -0.0343296974897 0.0193834770471 -0.0151846110821 -0.0219733603299 0.0200015287846 -0.0166918933392 -0.0100967604667 0.0117476321757 -0.018792707473 -0.00953142251819 0.0512403547764 -0.00441910745576 -0.0253982823342 0.0470996797085 -0.0131811331958 -0.028420528397 0.0570097789168 -0.0103748161346 -0.028088811785 0.050373673439 -0.00361777818762 -0.0148283494636 0.0438971333206 -0.00236639939249 -0.0157472789288 0.0512403547764 -0.00441910745576 -0.0253982823342 0.0262714382261 0.024643054232 0.00333152944222 0.023449998349 0.0172611102462 0.00965423230082 0.0317819230258 0.0191275794059 0.00692741526291 -0.0181487128139 0.0191756095737 0.00725237466395 -0.0272913184017 0.00181260507088 0.0117624523118 -0.0164575334638 0.0125575726852 0.00686521595344 -0.0418213754892 -0.00742403836921 -0.0206348095089 -0.0414947420359 -0.00295710819773 -0.0225406978279 -0.0393666550517 -0.0106545481831 -0.0230054780841 0.0604619793594 -0.0071836868301 0.00888195540756 0.0672499090433 -0.0154510131106 0.00984042230994 0.0682200118899 -0.00788477621973 0.00767027726397 -0.0398653969169 -0.00837041065097 -0.00824570097029 -0.0393666550517 -0.0106545481831 -0.0230054780841 -0.0363425649703 -0.0124280676246 -0.00762832257897 -0.0779805928469 0.0700702145696 0.00802535843104 -0.0699571371078 0.0646914690733 -0.00456732325256 -0.0771700739861 0.0674374252558 0.00493047386408 0.064685754478 -0.0747605413198 -0.0385285392404 0.0696968138218 -0.0747989788651 -0.0332351587713 0.058990996331 -0.0744717493653 -0.0386376976967 0.0818465650082 -0.0646916031837 0.004601073917 0.0855906233191 -0.056857522577 0.00729697011411 0.0809005796909 -0.0487979911268 0.00395165523514 -0.00354365818202 -0.0125812022015 -0.0245189536363 0.00320344255306 -0.0166476164013 -0.0215023886412 -0.00552152097225 -0.0164588801563 -0.0201352518052 0.0226106531918 0.0281752999872 -0.00177787418943 0.00612076325342 0.0238218400627 0.00119427102618 0.0126880630851 0.0209196154028 0.00636390130967 0.0622391216457 0.00130491273012 -0.0162859521806 0.0687282457948 0.0103019243106 -0.0152066750452 0.0656465515494 0.00329640181735 -0.0110076479614 0.00896836072206 -0.0178859829903 -0.0177515987307 0.0117476321757 -0.018792707473 -0.00953142251819 -0.00245248852298 -0.0189631339163 -0.0111456979066 0.0662746876478 -0.0250612292439 -0.0328748524189 0.0629565417767 -0.0314845256507 -0.0355579890311 0.0605648122728 -0.0226184297353 -0.0335885249078 -0.0193056520075 -0.00640912586823 0.00741031300277 -0.0160121638328 -0.00505490787327 0.00223769713193 -0.0152684766799 0.00252701970749 0.00554668903351 0.0602696500719 -0.00843365583569 -0.0316083207726 0.0605014450848 -0.0130754690617 -0.0308906380087 0.0564444027841 -0.00904699414968 -0.0371534638107 -0.0381806567311 0.0407792925835 -0.019973538816 -0.0272648502141 0.0430674515665 -0.0166629143059 -0.0321282558143 0.0364747196436 -0.0215411484241 -0.013514730148 0.0359381325543 -0.00797349400818 -0.0214076433331 0.0391493700445 -0.00776851270348 -0.0218933653086 0.0331122018397 -0.000901366525795 0.0317371711135 -0.00692039029673 -0.0261175371706 0.0193834770471 -0.0151846110821 -0.0219733603299 0.0240502078086 -0.00868919305503 -0.0290734339505 -0.0329524688423 0.00574303558096 -0.0349177606404 -0.0321104675531 -0.00197183503769 -0.03518948704 -0.0366347320378 0.0017704871716 -0.0322802253067 0.0240502078086 -0.00868919305503 -0.0290734339505 0.0147100556642 -0.0147073278204 -0.0253510158509 0.0144752385095 -0.0070848762989 -0.034492328763 0.0385896004736 0.000291874835966 0.00444744434208 0.0350222811103 0.011573231779 0.00757634406909 0.0354121625423 -0.000109013933979 0.00450044637546 0.0493391752243 -0.0192066207528 -0.0282236821949 0.0577224753797 -0.020547606051 -0.0277862790972 0.0564465895295 -0.0182245336473 -0.0300305616111 -0.0791537687182 0.0380757376552 0.0263591650873 -0.0709582194686 0.0410792566836 0.0236960314214 -0.0781400799751 0.0399436429143 0.0323086194694 0.00320344255306 -0.0166476164013 -0.0215023886412 0.00896836072206 -0.0178859829903 -0.0177515987307 -0.00599885499105 -0.0184409264475 -0.015290110372 -0.0737345814705 0.038439001888 0.0121840443462 -0.0741797760129 0.0406901091337 0.00791172217578 -0.0690660402179 0.0400546975434 0.00533317448571 0.0100417975336 0.0276991538703 -0.0274829212576 0.0106190349907 0.02950688079 -0.021063869819 0.0211553759873 0.0312036816031 -0.0245885774493 0.032403241843 0.0125592537224 -0.0369361899793 0.0312596708536 0.00204978929833 -0.0341790989041 0.0249150972813 0.0107660228387 -0.0397206582129 0.0565774738789 -0.00623939558864 -0.00480305217206 0.0561687573791 -0.00828116200864 -0.00418273126706 0.0539004430175 -0.00540307629853 -0.00894699897617 0.0665930733085 -0.00253646099009 0.00908811856061 0.0643844082952 0.0092457216233 0.0105861956254 0.0636151582003 -0.00344871124253 0.0112777063623 -0.0364621728659 -4.06305734941e-05 0.00923090986907 -0.0361147299409 -0.0120508149266 0.00691540632397 -0.0344442464411 -0.00569979753345 0.00958135258406 0.0706675350666 0.0171718429774 0.00147831998765 0.07365013659 0.0133140301332 -0.000675909686834 0.0711485892534 0.0247458182275 -0.00319633609615 -0.0446118675172 0.0268158894032 -0.0240276791155 -0.0412645675242 0.0296845398843 -0.0242789257318 -0.0394592247903 0.021167581901 -0.0313589386642 0.0554471611977 0.0115022500977 -0.042177259922 0.0605431683362 0.0167442467064 -0.0403055250645 0.0601614937186 0.00885820761323 -0.0400737561285 -0.0739856958389 0.0511276461184 0.0285730604082 -0.0754706263542 0.057246144861 0.0283697806299 -0.0804472342134 0.0510345138609 0.0322105102241 0.00980327930301 0.028790736571 -0.00937963183969 0.0074149183929 0.0266806911677 -0.00382403749973 0.0210444331169 0.030273001641 -0.00706696789712 -0.0616058968008 0.0379795208573 -0.00707599101588 -0.0652352571487 0.0470017306507 -0.00540793221444 -0.0642814710736 0.0499867610633 -0.00973032042384 -0.0444244034588 0.0199280641973 0.00183702539653 -0.0484882481396 0.0242904219776 -0.00165670679417 -0.0480200089514 0.0139896376058 -0.00342765916139 0.0447547212243 0.0325945727527 0.00325110368431 0.048808876425 0.0252794381231 0.00892338063568 0.0555975027382 0.0286405310035 0.00528475176543 -0.00245248852298 -0.0189631339163 -0.0111456979066 -0.0216245427728 -0.0176681727171 -0.0117809949443 -0.00599885499105 -0.0184409264475 -0.015290110372 0.0319815389812 0.0352998748422 -0.0105315605178 0.0347373187542 0.0342971347272 -0.00132000620943 0.0380271226168 0.0364866815507 -0.00783421378583 -0.0221735388041 0.0248348526657 0.00527667580172 -0.0278973150998 0.0252974294126 0.00663841702044 -0.0233208872378 0.0171473640949 0.00885204598308 -0.0151771679521 -0.0751925632358 -0.0358218029141 -0.020558193326 -0.0756064131856 -0.0252870116383 -0.020613707602 -0.0750931277871 -0.0343296974897 -0.0128323584795 -0.0432547368109 -0.0266204569489 -0.00944765098393 -0.0544546283782 -0.0279082152992 -0.0123113365844 -0.0451743938029 -0.0291688665748 0.0720914751291 0.00546024832875 -0.010403117165 0.0708144679666 0.000992352026515 -0.00854138284922 0.0689210742712 0.00494329864159 -0.0121464785188 -0.0582275763154 0.0486685633659 -0.0147868087515 -0.0642814710736 0.0499867610633 -0.00973032042384 -0.0634358376265 0.0546735934913 -0.0118903163821 -0.0635816827416 0.0559310466051 0.0108306938782 -0.0649350732565 0.0619430281222 0.0139570971951 -0.0660762935877 0.0578988678753 0.0189452208579 0.0393960885704 -0.00669425539672 -0.0340571328998 0.0355566330254 -0.0077797062695 -0.0261449292302 0.0317371711135 -0.00692039029673 -0.0261175371706 0.0643844082952 0.0092457216233 0.0105861956254 0.0585609525442 0.0207895357162 0.00992077030241 0.0603620111942 0.0056228893809 0.0125811565667 0.050373673439 -0.00361777818762 -0.0148283494636 0.0539004430175 -0.00540307629853 -0.00894699897617 0.0418007522821 -0.00242996658199 -0.00372027652338 -0.0804472342134 0.0510345138609 0.0322105102241 -0.0812487527728 0.04326537624 0.0373680815101 -0.0739856958389 0.0511276461184 0.0285730604082 -0.0289716441184 -0.0219964478165 0.00619356799871 -0.0324501506984 -0.014356858097 0.00908888690174 -0.0336843542755 -0.0225331783295 0.00486868014559 -0.0106726381928 -0.0760597363114 -0.026934184134 -0.0151771679521 -0.0751925632358 -0.0358218029141 -0.0101257953793 -0.0729843974113 -0.0340920165181 0.0182611495256 -0.000761016330216 -0.0385480634868 0.023355057463 0.00308697414584 -0.0384194366634 0.0312596708536 0.00204978929833 -0.0341790989041 0.0147100556642 -0.0147073278204 -0.0253510158509 0.00896836072206 -0.0178859829903 -0.0177515987307 0.00320344255306 -0.0166476164013 -0.0215023886412 -0.0386477708817 0.0575842261314 -0.0097325630486 -0.0477432273328 0.0646687820554 -0.00769540853798 -0.0444217473269 0.0602424219251 -0.00442242622375 0.0036262657959 -0.00963021907955 0.00580563070253 -0.00387709727511 -0.00124160922132 0.00781158497557 -0.012282749638 -0.00724740931764 -2.84041252598e-05 -0.0796836540103 0.0613558962941 0.0230027772486 -0.0785785987973 0.0702539384365 0.013914372772 -0.0867733284831 0.0609648488462 0.0183455236256 0.0565774738789 -0.00623939558864 -0.00480305217206 0.0583206899464 -0.00380795542151 -0.00911643542349 0.0586783364415 -0.0037231084425 -0.00361427664757 -0.0847244933248 0.0610410422087 0.010350859724 -0.0836498662829 0.0665282458067 0.00920514483005 -0.0803875476122 0.0608413852751 0.00552876945585 0.0852778479457 -0.0646592378616 0.00590485380962 0.0874174982309 -0.0661162883043 0.0035401773639 0.0917203947902 -0.059632204473 0.00453671533614 -0.00303131062537 0.0293679926544 -0.0160501208156 0.0020479504019 0.0282315947115 -0.00889560487121 0.00260267360136 0.0286370813847 -0.0185407903045 -0.0652352571487 0.0470017306507 -0.00540793221444 -0.0616058968008 0.0379795208573 -0.00707599101588 -0.0684591978788 0.0459762923419 -0.00223574182019 0.052886724472 -0.00321291876025 -0.0398595966399 0.0566076412797 0.00240896618925 -0.0403126217425 0.0600861534476 -0.00594944367185 -0.0371469557285 0.0477273277938 -0.00715120695531 -0.0378671810031 0.0447440594435 -0.0021612313576 -0.0399010665715 0.052886724472 -0.00321291876025 -0.0398595966399 -0.0454866327345 0.0472589023411 0.000885553658009 -0.0497838333249 0.0559922680259 0.00280405301601 -0.0529348887503 0.0521784499288 0.0046043144539 -0.0228501725942 -0.016540652141 0.00465866690502 -0.0183539558202 -0.0179969426244 0.000481826165924 -0.0160560552031 -0.0138077298179 -0.00380461011082 0.072433590889 -0.0604861862957 -0.0301266685128 0.0664181634784 -0.0537509098649 -0.0296265520155 0.068222001195 -0.0600504949689 -0.0280908290297 -0.0299632400274 -0.0203246790916 -0.026556706056 -0.025938231498 -0.03466161713 -0.0289873387665 -0.0243194364011 -0.0365216545761 -0.0246309749782 0.0477273277938 -0.00715120695531 -0.0378671810031 0.052886724472 -0.00321291876025 -0.0398595966399 0.0564444027841 -0.00904699414968 -0.0371534638107 -0.0141136925668 -0.0657514110208 -0.0340646244586 -0.0166946779937 -0.0649743825197 -0.0305308811367 -0.0177184212953 -0.0485680811107 -0.0308595653623 -0.0136914439499 -0.0546173118055 -0.0263203363866 -0.0149490861222 -0.0439244024456 -0.0251441132277 -0.0174274221063 -0.0494892522693 -0.0261709243059 -0.00733371544629 0.0176978223026 0.00535471225157 -0.00962803792208 0.0256944280118 1.13362448246e-05 -0.0131820514798 0.0203566141427 0.00420704483986 -0.0242858082056 -0.0106024397537 -0.0329431965947 -0.0208534952253 -0.0113473143429 -0.0300426613539 -0.0236518606544 -0.0245031807572 -0.0287500377744 -0.0884201526642 0.046169500798 0.0338194072247 -0.0916560813785 0.0415009707212 0.0378023125231 -0.0845351070166 0.0501509457827 0.0333207882941 0.0769165381789 -0.0372788608074 0.004157373216 0.0809005796909 -0.0487979911268 0.00395165523514 0.0819737017155 -0.0426090881228 0.00562430731952 0.0159195382148 0.00791420787573 0.0129859792069 0.0122077101842 0.0155965732411 0.0104404864833 0.00324054062366 0.0132272355258 0.00995828956366 0.0326762907207 -0.00954702217132 -0.0169461015612 0.0292802155018 -0.012616426684 -0.0106529798359 0.0193834770471 -0.0151846110821 -0.0219733603299 -0.00179908366408 0.0287952888757 -0.0084942439571 0.00612076325342 0.0238218400627 0.00119427102618 0.0020479504019 0.0282315947115 -0.00889560487121 0.0669340640306 -0.0361999757588 -0.031447045505 0.0656110197306 -0.0270528197289 -0.0288560781628 0.0665984004736 -0.0429504625499 -0.0306054838002 -0.0314821787179 0.0163094382733 -0.0335103832185 -0.0391113087535 0.0111347045749 -0.0334663800895 -0.0276381317526 0.0215675216168 -0.0321951173246 0.0624106451869 -0.00390694057569 -0.00189818732906 0.0585702136159 -0.00812319666147 0.00419311970472 0.0598698109388 -0.00478675868362 0.00212140567601 -0.0390509627759 -0.0665916949511 0.00520234834403 -0.0349772572517 -0.0686603486538 0.00445972103626 -0.0354872792959 -0.0626792758703 0.00653606234118 -0.0838714838028 0.0507906712592 0.00711987819523 -0.074530698359 0.0564152859151 0.000770429789554 -0.0789092034101 0.0428538136184 0.00382694439031 -0.0270780660212 -0.0214741248637 0.00396085856482 -0.0257243346423 -0.0339680574834 0.00464337132871 -0.0228501725942 -0.016540652141 0.00465866690502 0.0106190349907 0.02950688079 -0.021063869819 0.0197386574 0.031497310847 -0.0197209063917 0.0211553759873 0.0312036816031 -0.0245885774493 0.0650048032403 -0.0514324046671 -0.0363247282803 0.068005733192 -0.0399323999882 -0.0337682925165 0.070414699614 -0.052166569978 -0.0348446667194 -0.0353548154235 -0.0399240851402 0.000486829754664 -0.0342002138495 -0.0296476446092 0.0025749206543 -0.0334986448288 -0.0282570403069 -0.00100804551039 -0.0289716441184 -0.0219964478165 0.00619356799871 -0.0306760501117 -0.0296483207494 0.00481163570657 -0.0270780660212 -0.0214741248637 0.00396085856482 0.0623909682035 -0.0175557024777 -4.21606127929e-05 0.0561687573791 -0.00828116200864 -0.00418273126706 0.0565774738789 -0.00623939558864 -0.00480305217206 -0.0190910883248 0.0310631301254 -0.0254277605563 -0.0234806258231 0.0277767758816 -0.028870517388 -0.0200255811214 0.0362959876657 -0.0195930022746 0.0395265072584 -0.00346563779749 -0.0100429235026 0.0418007522821 -0.00242996658199 -0.00372027652338 0.0366409905255 -0.00356341060251 -0.00233652559109 0.00320344255306 -0.0166476164013 -0.0215023886412 -0.00599885499105 -0.0184409264475 -0.015290110372 -0.00552152097225 -0.0164588801563 -0.0201352518052 0.068005733192 -0.0399323999882 -0.0337682925165 0.062893807888 -0.0445155724883 -0.0355053693056 0.0629565417767 -0.0314845256507 -0.0355579890311 -0.0379568003118 0.0557544454932 -0.013513436541 -0.0412535518408 0.055965680629 -0.0148494942114 -0.0493567548692 0.0630387067795 -0.0120902014896 -0.0434817001224 0.000976005452685 -0.00737236114219 -0.0398653969169 -0.00837041065097 -0.00824570097029 -0.0451306067407 0.00219421950169 -0.00086157215992 -0.0453874990344 0.0272053200752 0.00166924321093 -0.0444244034588 0.0199280641973 0.00183702539653 -0.0409485846758 0.0245515368879 0.00264321570285 0.075042180717 -0.0225737150759 0.0023139808327 0.0787196084857 -0.0281961280853 0.00396182155237 0.0813561230898 -0.0232167206705 0.00461526820436 0.0465668439865 -0.0175501257181 -0.036243584007 0.0514739230275 -0.0248519349843 -0.0344557315111 0.0461902655661 -0.0180508662015 -0.0307826343924 -0.04279313609 -0.0713429301977 0.00690987613052 -0.0391314774752 -0.0653252974153 0.00145680934656 -0.0468597859144 -0.0764623731375 0.00214877328835 0.0769165381789 -0.0372788608074 0.004157373216 0.0819737017155 -0.0426090881228 0.00562430731952 0.0795691013336 -0.0337514281273 0.00747773703188 -0.0412535518408 0.055965680629 -0.0148494942114 -0.0381806567311 0.0407792925835 -0.019973538816 -0.0503109395504 0.0511616170406 -0.0167050715536 0.0754076316953 -0.0163155626506 0.0023912510369 0.0719613134861 -0.0083319125697 0.00354015408084 0.0705670714378 -0.0142336087301 0.0080666737631 0.0717578157783 0.015957320109 -0.0263392049819 0.0709100887179 0.0228983853012 -0.0246697273105 0.0713142603636 0.0189655330032 -0.0151191568002 0.0839467793703 -0.0338173881173 -0.0013689303305 0.0839628651738 -0.0313502401114 0.00374555471353 0.089011117816 -0.049574509263 0.0023703314364 0.0456950850785 0.0270799733698 -0.0398734845221 0.0434862487018 0.032226357609 -0.036325853318 0.0529840812087 0.0303148236126 -0.0373462289572 -0.00338091235608 0.0199865065515 -0.0312582217157 -0.00667092064396 0.0126474965364 -0.0336418747902 -0.0121780969203 0.0159933697432 -0.0321105457842 -0.0212301537395 -0.0123340161517 0.00449436390772 -0.0228501725942 -0.016540652141 0.00465866690502 -0.016403356567 -0.00939153227955 -0.00214911578223 0.0769819021225 -0.0300572067499 -0.00352066406049 0.0839467793703 -0.0338173881173 -0.0013689303305 0.0870006456971 -0.046893555671 0.000147852333612 -0.0473444573581 0.0222850441933 -0.0223869681358 -0.0446118675172 0.0268158894032 -0.0240276791155 -0.043179307133 0.0188505705446 -0.0299622882158 -0.04279313609 -0.0713429301977 0.00690987613052 -0.0390509627759 -0.0665916949511 0.00520234834403 -0.0391314774752 -0.0653252974153 0.00145680934656 0.0838564783335 -0.0586024485528 -0.00192842667457 0.0855331420898 -0.0662355422974 -0.000724932178855 0.0821102634072 -0.0652388483286 0.00123241753317 0.068005733192 -0.0399323999882 -0.0337682925165 0.0669340640306 -0.0361999757588 -0.031447045505 0.0714298635721 -0.0534965991974 -0.0324478931725 -0.0247759241611 -0.0095595670864 0.010169220157 -0.0207055173814 0.00392329553142 0.00909946020693 -0.0272913184017 0.00181260507088 0.0117624523118 0.0226895809174 0.0281979069114 -0.0306504108012 0.0339798666537 0.0333764031529 -0.0331196337938 0.0347270555794 0.027764454484 -0.0385278463364 0.0355566330254 -0.0077797062695 -0.0261449292302 0.0400369912386 -0.00509023061022 -0.0284015871584 0.0403502397239 -0.00280293240212 -0.0176492966712 0.0711485892534 0.0247458182275 -0.00319633609615 0.0696674659848 0.0262376572937 -0.0128174172714 0.0679226443172 0.029033575207 -0.0159582849592 0.0574748292565 -0.0728332474828 -0.0297978837043 0.0609687641263 -0.0750585868955 -0.0276746544987 0.0650694146752 -0.0661949291825 -0.0290601216257 -0.0649350732565 0.0619430281222 0.0139570971951 -0.0529348887503 0.0521784499288 0.0046043144539 -0.0497838333249 0.0559922680259 0.00280405301601 0.0491875857115 0.00344011140987 0.0133795123547 0.0424964986742 0.00406499719247 0.0110576944426 0.0458973273635 -0.000604764616583 0.0111892325804 -0.012700362131 0.00108930072747 0.00499837147072 -0.0102530596778 0.0101205231622 0.0068081477657 -0.0152684766799 0.00252701970749 0.00554668903351 -0.00944765098393 -0.0544546283782 -0.0279082152992 -0.0095995971933 -0.0609665364027 -0.0261474959552 -0.00612018536776 -0.0598083510995 -0.0299312565476 0.00573587883264 -0.00811646040529 -0.0328484363854 -0.000636835582554 -0.00549022806808 -0.0329833440483 0.00936001725495 -0.00345246354118 -0.0368482433259 -0.017013894394 -0.0347188636661 -0.030624659732 -0.0163839384913 -0.0424141436815 -0.0313666537404 -0.0201097112149 -0.0344850867987 -0.0324018150568 -0.0690960809588 0.0526289157569 0.0247765686363 -0.0671717971563 0.0575826056302 0.0252393428236 -0.0739856958389 0.0511276461184 0.0285730604082 -0.0329524688423 0.00574303558096 -0.0349177606404 -0.0245153158903 0.0113811995834 -0.0345406457782 -0.0262746438384 0.00359826651402 -0.0357828103006 -0.0642814710736 0.0499867610633 -0.00973032042384 -0.0721085593104 0.0587044768035 -0.00398772349581 -0.0634358376265 0.0546735934913 -0.0118903163821 -0.0141136925668 -0.0657514110208 -0.0340646244586 -0.0101257953793 -0.0729843974113 -0.0340920165181 -0.0162332504988 -0.0693076774478 -0.0359947793186 -0.084793664515 0.0397632867098 0.0391925126314 -0.0876308903098 0.0384379811585 0.0428250767291 -0.0822595283389 0.0367101840675 0.0404899641871 0.0627965405583 -0.0644452646375 -0.0340751446784 0.0555628426373 -0.0745320767164 -0.0335095077753 0.0574748292565 -0.0728332474828 -0.0297978837043 -0.0785785987973 0.0702539384365 0.013914372772 -0.0796836540103 0.0613558962941 0.0230027772486 -0.0729605704546 0.0639232620597 0.0233493559062 0.0795691013336 -0.0337514281273 0.00747773703188 0.089011117816 -0.049574509263 0.0023703314364 0.0843727812171 -0.0348832271993 0.00490371277556 -0.0317054688931 -0.00275706243701 0.0117793781683 -0.0364621728659 -4.06305734941e-05 0.00923090986907 -0.0344442464411 -0.00569979753345 0.00958135258406 -0.0262746438384 0.00359826651402 -0.0357828103006 -0.0253939889371 -0.00600079027936 -0.0348485857248 -0.0321104675531 -0.00197183503769 -0.03518948704 0.0447440594435 -0.0021612313576 -0.0399010665715 0.0504876486957 0.00247842236422 -0.0409079678357 0.0566076412797 0.00240896618925 -0.0403126217425 -0.0201097112149 -0.0344850867987 -0.0324018150568 -0.0236518606544 -0.0245031807572 -0.0287500377744 -0.017013894394 -0.0347188636661 -0.030624659732 -0.0306760501117 -0.0296483207494 0.00481163570657 -0.0336843542755 -0.0225331783295 0.00486868014559 -0.0342002138495 -0.0296476446092 0.0025749206543 0.051399409771 0.0217050053179 -0.0416473895311 0.0529840812087 0.0303148236126 -0.0373462289572 0.060431715101 0.0229300074279 -0.0391151756048 -0.0914800092578 0.0378939323127 0.0321716368198 -0.0911771580577 0.0422582998872 0.0333826653659 -0.0873394012451 0.0418514795601 0.0236989371479 -0.0384465046227 0.0464623495936 -0.00204830104485 -0.0252886787057 0.0373778603971 -0.00468408223242 -0.0322041623294 0.0489787906408 -0.00726023875177 0.068005733192 -0.0399323999882 -0.0337682925165 0.0650048032403 -0.0514324046671 -0.0363247282803 0.062893807888 -0.0445155724883 -0.0355053693056 0.0294583402574 -0.00562923774123 0.00414758501574 0.0290143638849 -0.000971557688899 0.00837186072022 0.0222891252488 -0.0072523932904 0.00697265705094 -0.0242858082056 -0.0106024397537 -0.0329431965947 -0.0207476206124 -0.00821672473103 -0.0326586216688 -0.0208534952253 -0.0113473143429 -0.0300426613539 -0.016403356567 -0.00939153227955 -0.00214911578223 -0.0160121638328 -0.00505490787327 0.00223769713193 -0.0212301537395 -0.0123340161517 0.00449436390772 -0.0137986438349 -0.001824338804 -0.028773734346 -0.0133467214182 0.00200212979689 -0.0306190121919 -0.000636835582554 -0.00549022806808 -0.0329833440483 -0.0412535518408 0.055965680629 -0.0148494942114 -0.0379568003118 0.0557544454932 -0.013513436541 -0.0272648502141 0.0430674515665 -0.0166629143059 -0.0137986438349 -0.001824338804 -0.028773734346 -0.0207476206124 -0.00821672473103 -0.0326586216688 -0.0196293834597 -0.0012800822733 -0.0339007712901 0.0680408999324 0.0129547277465 0.00671541178599 0.0643844082952 0.0092457216233 0.0105861956254 0.0665930733085 -0.00253646099009 0.00908811856061 -0.0395993106067 0.0181454904377 0.00810672808439 -0.0409485846758 0.0245515368879 0.00264321570285 -0.0444244034588 0.0199280641973 0.00183702539653 0.0598698109388 -0.00478675868362 0.00212140567601 0.0586783364415 -0.0037231084425 -0.00361427664757 0.0624106451869 -0.00390694057569 -0.00189818732906 0.0798162445426 -0.0199222583324 -0.0021768680308 0.0721606612206 -0.022786539048 -0.00311841559596 0.0690664798021 -0.0187059957534 -0.00380081683397 0.07365013659 0.0133140301332 -0.000675909686834 0.0738353580236 0.00327896140516 0.000486694596475 0.0747518464923 0.0119193941355 -0.00421227002516 -0.0133467214182 0.00200212979689 -0.0306190121919 -0.0122720496729 0.00743707921356 -0.0320300944149 0.00347416335717 0.00105483434163 -0.0370849557221 0.0665407851338 0.00159848632757 -0.00484928302467 0.0689210742712 0.00494329864159 -0.0121464785188 0.0708144679666 0.000992352026515 -0.00854138284922 0.0441741943359 0.0165656842291 -0.0412995144725 0.0504528395832 0.0169036872685 -0.0417337194085 0.0458527319133 0.0086536090821 -0.0422105453908 0.0602696500719 -0.00843365583569 -0.0316083207726 0.0675886794925 0.00517250876874 -0.0339571870863 0.0675277709961 0.00351993273944 -0.0305571258068 -0.0785785987973 0.0702539384365 0.013914372772 -0.0779805928469 0.0700702145696 0.00802535843104 -0.0800869390368 0.0719245970249 0.00986813567579 0.0858451649547 -0.0757835879922 -0.00102130440064 0.0842919945717 -0.076128102839 0.00855887308717 0.0805870443583 -0.075636588037 -0.00136879249476 0.0825529024005 -0.0241261273623 -0.00263262726367 0.0798162445426 -0.0199222583324 -0.0021768680308 0.0831556394696 -0.0222345832735 0.00146252510604 0.0561687573791 -0.00828116200864 -0.00418273126706 0.0622591376305 -0.020616741851 0.00515010254458 0.0533758401871 -0.00816207565367 0.00332604441792 -0.0347196757793 0.00916986633092 0.0111047113314 -0.0395993106067 0.0181454904377 0.00810672808439 -0.0409355126321 0.00761891668662 0.00908278673887 0.0206307601184 0.0312986150384 -0.0104876076803 0.0210444331169 0.030273001641 -0.00706696789712 0.0319815389812 0.0352998748422 -0.0105315605178 -0.0796836540103 0.0613558962941 0.0230027772486 -0.0845351070166 0.0501509457827 0.0333207882941 -0.0754706263542 0.057246144861 0.0283697806299 0.065537057817 0.0151417842135 -0.0360496491194 0.0694160535932 0.0198294073343 -0.0330271050334 0.0688897669315 0.0143098514527 -0.034747581929 0.0627965405583 -0.0644452646375 -0.0340751446784 0.0574748292565 -0.0728332474828 -0.0297978837043 0.0635530352592 -0.0652642175555 -0.0305935442448 -0.012165626511 0.0265315938741 -0.0258189272135 -0.0190910883248 0.0310631301254 -0.0254277605563 -0.0089406510815 0.0311568155885 -0.0189374648035 0.0688916966319 0.00488886702806 0.00309444754384 0.0706675350666 0.0171718429774 0.00147831998765 0.0665930733085 -0.00253646099009 0.00908811856061 -0.0141136925668 -0.0657514110208 -0.0340646244586 -0.0177184212953 -0.0485680811107 -0.0308595653623 -0.0138331912458 -0.0603952333331 -0.0342085324228 0.0700432732701 0.0106197241694 -0.0254250112921 0.0713142603636 0.0189655330032 -0.0151191568002 0.0684423595667 0.00943436846137 -0.0206362344325 -0.0233208872378 0.0171473640949 0.00885204598308 -0.0272913184017 0.00181260507088 0.0117624523118 -0.0181487128139 0.0191756095737 0.00725237466395 0.072433590889 -0.0604861862957 -0.0301266685128 0.068222001195 -0.0600504949689 -0.0280908290297 0.0650694146752 -0.0661949291825 -0.0290601216257 -0.0228501725942 -0.016540652141 0.00465866690502 -0.0257243346423 -0.0339680574834 0.00464337132871 -0.0230524111539 -0.0281649492681 0.00343656959012 -0.0251056849957 -0.0178923550993 -0.0185515768826 -0.0292823426425 -0.016650011763 -0.0205444768071 -0.0252824239433 -0.0285316351801 -0.0223765242845 0.0784853920341 -0.0390462353826 -2.51326264333e-06 0.0816290304065 -0.0504989773035 0.00101880810689 0.0769165381789 -0.0372788608074 0.004157373216 0.023449998349 0.0172611102462 0.00965423230082 0.0122077101842 0.0155965732411 0.0104404864833 0.0159195382148 0.00791420787573 0.0129859792069 0.0711485892534 0.0247458182275 -0.00319633609615 0.07365013659 0.0133140301332 -0.000675909686834 0.0736648738384 0.0202186964452 -0.00637979293242 0.065167747438 0.0291529707611 -0.00138657167554 0.0711485892534 0.0247458182275 -0.00319633609615 0.0672424212098 0.0293734762818 -0.00736515130848 -0.0324501506984 -0.014356858097 0.00908888690174 -0.0282046999782 -0.011605437845 0.00933898519725 -0.0344442464411 -0.00569979753345 0.00958135258406 -0.0480200089514 0.0139896376058 -0.00342765916139 -0.0520143918693 0.0224781483412 -0.0120825413615 -0.0473023541272 0.0122802108526 -0.0124696297571 -0.0616058968008 0.0379795208573 -0.00707599101588 -0.0642814710736 0.0499867610633 -0.00973032042384 -0.0594911910594 0.0402323678136 -0.0138589777052 -0.0664762184024 0.0484478063881 0.0224707052112 -0.0617349892855 0.0479026585817 0.0140177402645 -0.0660762935877 0.0578988678753 0.0189452208579 0.00798164401203 -0.0143224736676 0.00208706385456 0.0036262657959 -0.00963021907955 0.00580563070253 -0.00657241046429 -0.0163257047534 -0.00350670074113 -0.0212532021105 -0.0387861654162 -0.0227633845061 -0.0209524612874 -0.0299015939236 -0.0216957069933 -0.0252824239433 -0.0285316351801 -0.0223765242845 0.0665930733085 -0.00253646099009 0.00908811856061 0.0682200118899 -0.00788477621973 0.00767027726397 0.0704541727901 -0.00372631778009 0.00695726461709 0.065537057817 0.0151417842135 -0.0360496491194 0.0601614937186 0.00885820761323 -0.0400737561285 0.0605431683362 0.0167442467064 -0.0403055250645 -0.0660762935877 0.0578988678753 0.0189452208579 -0.0671717971563 0.0575826056302 0.0252393428236 -0.0690960809588 0.0526289157569 0.0247765686363 -0.0182791948318 -0.019947366789 -0.0281752515584 -0.015117011033 -0.0188090614974 -0.0260683037341 -0.0151859046891 -0.0280584804714 -0.02708007209 -0.0680218562484 0.0746396705508 0.0158250704408 -0.0686424598098 0.0706430822611 0.0113607710227 -0.0731192678213 0.071150586009 0.0126371867955 -0.0368985123932 -0.0142811005935 0.00302317948081 -0.0363080054522 -0.0172072835267 0.00185445009265 -0.0361147299409 -0.0120508149266 0.00691540632397 -0.0453874990344 0.0272053200752 0.00166924321093 -0.0409485846758 0.0245515368879 0.00264321570285 -0.0440319478512 0.0313652604818 0.0016547862906 0.0725733786821 -0.0235378220677 0.00565016036853 0.0787196084857 -0.0281961280853 0.00396182155237 0.075042180717 -0.0225737150759 0.0023139808327 -0.0891168415546 0.0341397710145 0.0321871414781 -0.0834547281265 0.0319089256227 0.0349037349224 -0.0878021046519 0.033477794379 0.0395364612341 0.0563069395721 -0.0334836132824 -0.0334919802845 0.0610778927803 -0.0446138717234 -0.033351752907 0.0578673295677 -0.03540468961 -0.0308890789747 -0.0294777080417 -0.0637631043792 0.00494760088623 -0.033697348088 -0.0659902095795 0.000380541518098 -0.0297503750771 -0.0631617978215 0.000223000184633 0.0403502397239 -0.00280293240212 -0.0176492966712 0.0438971333206 -0.00236639939249 -0.0157472789288 0.0395265072584 -0.00346563779749 -0.0100429235026 -0.0838714838028 0.0507906712592 0.00711987819523 -0.0847244933248 0.0610410422087 0.010350859724 -0.0803875476122 0.0608413852751 0.00552876945585 0.0688897669315 0.0143098514527 -0.034747581929 0.0675886794925 0.00517250876874 -0.0339571870863 0.065537057817 0.0151417842135 -0.0360496491194 0.0668106079102 -0.070455878973 -0.0377877689898 0.0696968138218 -0.0747989788651 -0.0332351587713 0.064685754478 -0.0747605413198 -0.0385285392404 0.058284394443 -0.0302759688348 -0.0378637500107 0.0575529150665 -0.022210219875 -0.0367079302669 0.0629565417767 -0.0314845256507 -0.0355579890311 -0.0207055173814 0.00392329553142 0.00909946020693 -0.0193056520075 -0.00640912586823 0.00741031300277 -0.0152684766799 0.00252701970749 0.00554668903351 -0.0329524688423 0.00574303558096 -0.0349177606404 -0.0366347320378 0.0017704871716 -0.0322802253067 -0.0409325622022 0.00616488046944 -0.0320984274149 0.0622591376305 -0.020616741851 0.00515010254458 0.0561687573791 -0.00828116200864 -0.00418273126706 0.0623909682035 -0.0175557024777 -4.21606127929e-05 0.0226895809174 0.0281979069114 -0.0306504108012 0.0261729359627 0.0250671524554 -0.0349872037768 0.010564208962 0.0239719748497 -0.0332786925137 -0.0306212659925 0.0172087047249 0.0100856469944 -0.0278973150998 0.0252974294126 0.00663841702044 -0.0355813615024 0.0215603802353 0.00780190061778 0.0683374479413 -0.0672494471073 -0.0335944145918 0.0668106079102 -0.070455878973 -0.0377877689898 0.066437445581 -0.0657137334347 -0.0362850166857 0.0543802753091 0.0360043048859 -0.0105734793469 0.0609902478755 0.0335634611547 -0.0094895362854 0.0616061538458 0.0335902385414 -0.0206316392869 -0.0394592247903 0.021167581901 -0.0313589386642 -0.0347637310624 0.0273377392441 -0.0288795996457 -0.0276381317526 0.0215675216168 -0.0321951173246 -0.0394592247903 0.021167581901 -0.0313589386642 -0.0391113087535 0.0111347045749 -0.0334663800895 -0.043179307133 0.0188505705446 -0.0299622882158 -0.0684591978788 0.0459762923419 -0.00223574182019 -0.0631757378578 0.0389323234558 -0.00277568120509 -0.0690660402179 0.0400546975434 0.00533317448571 0.0690664798021 -0.0187059957534 -0.00380081683397 0.0623909682035 -0.0175557024777 -4.21606127929e-05 0.0643664449453 -0.0178353134543 0.000139734620461 -0.00962803792208 0.0256944280118 1.13362448246e-05 -0.00179908366408 0.0287952888757 -0.0084942439571 -0.0180822927505 0.0280927624553 0.00312319723889 -0.0649350732565 0.0619430281222 0.0139570971951 -0.0647223517299 0.066875346005 0.0136522306129 -0.0652576088905 0.0690394937992 0.0202288385481 0.0604619793594 -0.0071836868301 0.00888195540756 0.0648333206773 -0.0193411242217 0.00892408099025 0.0672499090433 -0.0154510131106 0.00984042230994 -0.0352949239314 0.0277008544654 0.00385763379745 -0.0355813615024 0.0215603802353 0.00780190061778 -0.0295234173536 0.0311564076692 0.00292094191536 0.0116761699319 0.0192524939775 -0.0374199561775 -0.00667092064396 0.0126474965364 -0.0336418747902 -0.00338091235608 0.0199865065515 -0.0312582217157 -0.0390509627759 -0.0665916949511 0.00520234834403 -0.0354872792959 -0.0626792758703 0.00653606234118 -0.0356382876635 -0.0533351935446 0.00276124686934 0.0711485892534 0.0247458182275 -0.00319633609615 0.0627590343356 0.0254758372903 0.00493016792461 0.0655900388956 0.0184390544891 0.00736436760053 -0.0218933653086 0.0331122018397 -0.000901366525795 -0.0252886787057 0.0373778603971 -0.00468408223242 -0.0295234173536 0.0311564076692 0.00292094191536 0.0767389982939 -0.0762002691627 0.00715722655877 0.0805870443583 -0.075636588037 -0.00136879249476 0.0842919945717 -0.076128102839 0.00855887308717 0.0577097833157 -0.0276579707861 -0.0262848697603 0.0577224753797 -0.020547606051 -0.0277862790972 0.0493391752243 -0.0192066207528 -0.0282236821949 -0.0281191021204 0.0276504568756 -0.0284133274108 -0.0272648502141 0.0430674515665 -0.0166629143059 -0.0200255811214 0.0362959876657 -0.0195930022746 -0.074530698359 0.0564152859151 0.000770429789554 -0.0736100152135 0.0473809987307 0.00139974115882 -0.0789092034101 0.0428538136184 0.00382694439031 0.0233577005565 -0.0130151677877 -0.00125345273409 0.0294583402574 -0.00562923774123 0.00414758501574 0.0222891252488 -0.0072523932904 0.00697265705094 0.0504257716238 -0.016582140699 -0.0392883382738 0.0564444027841 -0.00904699414968 -0.0371534638107 0.0574302785099 -0.0164519175887 -0.0373466834426 0.0818465650082 -0.0646916031837 0.004601073917 0.0852778479457 -0.0646592378616 0.00590485380962 0.0855906233191 -0.056857522577 0.00729697011411 0.0366409905255 -0.00356341060251 -0.00233652559109 0.0354121625423 -0.000109013933979 0.00450044637546 0.0294583402574 -0.00562923774123 0.00414758501574 -0.0529617629945 0.027991278097 -0.0167360249907 -0.0483878850937 0.0177123211324 -0.0199927464128 -0.0520143918693 0.0224781483412 -0.0120825413615 -0.0434817001224 0.000976005452685 -0.00737236114219 -0.0473023541272 0.0122802108526 -0.0124696297571 -0.0455527119339 0.00271498505026 -0.0177962481976 -0.0287229511887 -0.0406478978693 0.00720230536535 -0.0306760501117 -0.0296483207494 0.00481163570657 -0.0329865068197 -0.039376296103 0.00508400192484 -0.0123113365844 -0.0451743938029 -0.0291688665748 -0.0132942395285 -0.0358155034482 -0.0281592346728 -0.0128323584795 -0.0432547368109 -0.0266204569489 0.0312596708536 0.00204978929833 -0.0341790989041 0.036672718823 0.0113793108612 -0.0394277796149 0.0384086482227 0.00514970067888 -0.0410340279341 -0.033011417836 -0.0568285062909 0.00476831337437 -0.0294777080417 -0.0637631043792 0.00494760088623 -0.0280799940228 -0.055304210633 0.00326808029786 0.065537057817 0.0151417842135 -0.0360496491194 0.0621104352176 0.00453598890454 -0.0360744036734 0.0601614937186 0.00885820761323 -0.0400737561285 -0.0342719815671 -0.0143612474203 -0.0263750851154 -0.0299632400274 -0.0203246790916 -0.026556706056 -0.0292823426425 -0.016650011763 -0.0205444768071 -0.0120546650141 -0.0671373382211 -0.028148625046 -0.0106726381928 -0.0760597363114 -0.026934184134 -0.010915578343 -0.0659576877952 -0.0315063148737 -0.0122720496729 0.00743707921356 -0.0320300944149 -0.0171094201505 0.00397073151544 -0.0321909785271 -0.0181399043649 0.0110053224489 -0.0338392443955 0.0592680051923 -0.00785803515464 -0.0265315212309 0.0604395531118 -0.014644660987 -0.0287422351539 0.0602696500719 -0.00843365583569 -0.0316083207726 -0.0453874990344 0.0272053200752 0.00166924321093 -0.0572749041021 0.046243134886 0.00337952957489 -0.0484882481396 0.0242904219776 -0.00165670679417 -0.0280799940228 -0.055304210633 0.00326808029786 -0.0297503750771 -0.0631617978215 0.000223000184633 -0.0269599817693 -0.0495302826166 0.00157890853006 0.0690664798021 -0.0187059957534 -0.00380081683397 0.0643664449453 -0.0178353134543 0.000139734620461 0.0711212977767 -0.0141868051142 -0.00323123089038 0.0495941489935 -0.00505510251969 0.00281282933429 0.0533758401871 -0.00816207565367 0.00332604441792 0.0584446676075 -0.0131367407739 0.00669920956716 -0.0521116182208 0.0417004041374 -0.0192602630705 -0.0446118675172 0.0268158894032 -0.0240276791155 -0.0562584511936 0.0401268824935 -0.0175458882004 -0.0764897316694 0.0619541816413 0.00187294220086 -0.0859415605664 0.0709468647838 0.00737344101071 -0.0847748368979 0.0726360008121 0.00620254734531 -0.0207476206124 -0.00821672473103 -0.0326586216688 -0.0137986438349 -0.001824338804 -0.028773734346 -0.0144916502759 -0.00543817412108 -0.0263480637223 -0.0660762935877 0.0578988678753 0.0189452208579 -0.0671208351851 0.0611894726753 0.0208555608988 -0.0671717971563 0.0575826056302 0.0252393428236 -0.0280799940228 -0.055304210633 0.00326808029786 -0.0294777080417 -0.0637631043792 0.00494760088623 -0.0297503750771 -0.0631617978215 0.000223000184633 -0.0166946779937 -0.0649743825197 -0.0305308811367 -0.0187529120594 -0.0492971539497 -0.0287065207958 -0.0177184212953 -0.0485680811107 -0.0308595653623 0.0621104352176 0.00453598890454 -0.0360744036734 0.065537057817 0.0151417842135 -0.0360496491194 0.0675886794925 0.00517250876874 -0.0339571870863 0.0709631517529 -0.0282764472067 0.0042789876461 0.0622591376305 -0.020616741851 0.00515010254458 0.0697775110602 -0.0257171951234 0.000200476919417 0.0491875857115 0.00344011140987 0.0133795123547 0.0405975058675 0.0136088356376 0.009625967592 0.0424964986742 0.00406499719247 0.0110576944426 -0.0289716441184 -0.0219964478165 0.00619356799871 -0.0282046999782 -0.011605437845 0.00933898519725 -0.0324501506984 -0.014356858097 0.00908888690174 -0.0444579236209 0.00304091628641 -0.0277560744435 -0.0409325622022 0.00616488046944 -0.0320984274149 -0.0381523445249 -0.00304430956021 -0.029428480193 -0.015117011033 -0.0188090614974 -0.0260683037341 -0.0144916502759 -0.00543817412108 -0.0263480637223 -0.0097112422809 -0.0125950593501 -0.0233362093568 -0.0187529120594 -0.0492971539497 -0.0287065207958 -0.025938231498 -0.03466161713 -0.0289873387665 -0.0177184212953 -0.0485680811107 -0.0308595653623 0.0769819021225 -0.0300572067499 -0.00352066406049 0.075530923903 -0.0326715037227 -0.00224941363558 0.0721606612206 -0.022786539048 -0.00311841559596 -0.0281935930252 -0.027320895344 -0.00371586484835 -0.0337675176561 -0.0367394164205 -0.00267041777261 -0.0334986448288 -0.0282570403069 -0.00100804551039 -0.0503109395504 0.0511616170406 -0.0167050715536 -0.0381806567311 0.0407792925835 -0.019973538816 -0.0509763434529 0.0460034161806 -0.0177591200918 0.0447824895382 0.0373931154609 -0.0133784590289 0.0472641289234 0.0361711494625 -0.00451004551724 0.0543802753091 0.0360043048859 -0.0105734793469 0.0600458458066 -0.00507800886407 -0.0249140188098 0.0592680051923 -0.00785803515464 -0.0265315212309 0.0602696500719 -0.00843365583569 -0.0316083207726 0.0356194712222 0.0371010899544 -0.0225756745785 0.0319815389812 0.0352998748422 -0.0105315605178 0.0380271226168 0.0364866815507 -0.00783421378583 -0.0451306067407 0.00219421950169 -0.00086157215992 -0.0480200089514 0.0139896376058 -0.00342765916139 -0.0434817001224 0.000976005452685 -0.00737236114219 -0.0916560813785 0.0415009707212 0.0378023125231 -0.0876308903098 0.0384379811585 0.0428250767291 -0.0877385362983 0.0418181084096 0.0414660908282 0.0193834770471 -0.0151846110821 -0.0219733603299 0.00896836072206 -0.0178859829903 -0.0177515987307 0.0147100556642 -0.0147073278204 -0.0253510158509 -0.0781400799751 0.0399436429143 0.0323086194694 -0.0709582194686 0.0410792566836 0.0236960314214 -0.0712874382734 0.0450919307768 0.0275897365063 -0.0173668675125 -0.0188725441694 -0.0200483836234 -0.0209524612874 -0.0299015939236 -0.0216957069933 -0.0167125929147 -0.0282691419125 -0.0229621194303 0.0675886794925 0.00517250876874 -0.0339571870863 0.0688897669315 0.0143098514527 -0.034747581929 0.0710225701332 0.0116371689364 -0.0303473770618 0.0870006456971 -0.046893555671 0.000147852333612 0.0889291241765 -0.0584181435406 -0.00213292124681 0.0837335810065 -0.0474399290979 -0.000192326551769 -0.0111597320065 0.0221810676157 -0.0287142917514 -0.0121780969203 0.0159933697432 -0.0321105457842 -0.0234806258231 0.0277767758816 -0.028870517388 -0.0914800092578 0.0378939323127 0.0321716368198 -0.0918235704303 0.0354779586196 0.0378490537405 -0.0893638506532 0.0394080318511 0.0356427282095 0.0465668439865 -0.0175501257181 -0.036243584007 0.0420701652765 -0.00905739609152 -0.0347143113613 0.0504257716238 -0.016582140699 -0.0392883382738 -0.0444579236209 0.00304091628641 -0.0277560744435 -0.0414947420359 -0.00295710819773 -0.0225406978279 -0.0455527119339 0.00271498505026 -0.0177962481976 -0.04279313609 -0.0713429301977 0.00690987613052 -0.0426602773368 -0.0763025358319 0.00917287915945 -0.0377826243639 -0.0715468376875 0.00810029637069 -0.0143243018538 -0.0176195204258 -0.00678763026372 -0.0183539558202 -0.0179969426244 0.000481826165924 -0.0222899951041 -0.0185299292207 -0.00168474740349 0.00347416335717 0.00105483434163 -0.0370849557221 -0.0122720496729 0.00743707921356 -0.0320300944149 0.00251939380541 0.00667322427034 -0.0372835509479 0.0708144679666 0.000992352026515 -0.00854138284922 0.0697568207979 -0.00990358553827 -0.00423297146335 0.0665407851338 0.00159848632757 -0.00484928302467 0.0891577154398 -0.0754422545433 0.00495696906 0.0870789960027 -0.071105375886 -0.00195318716578 0.0874174982309 -0.0661162883043 0.0035401773639 -0.0568155124784 0.0680090263486 -0.00663143396378 -0.0493567548692 0.0630387067795 -0.0120902014896 -0.0637906864285 0.0637098252773 -0.00896131899208 -0.0190910883248 0.0310631301254 -0.0254277605563 -0.0200255811214 0.0362959876657 -0.0195930022746 -0.0089406510815 0.0311568155885 -0.0189374648035 -0.00350152561441 0.0275401733816 -0.0214963126928 0.0106190349907 0.02950688079 -0.021063869819 0.0100417975336 0.0276991538703 -0.0274829212576 -0.0745909139514 0.0392232574522 0.0197847299278 -0.0737345814705 0.038439001888 0.0121840443462 -0.0680031627417 0.0388116128743 0.0118984086439 -0.00733371544629 0.0176978223026 0.00535471225157 -0.0131820514798 0.0203566141427 0.00420704483986 -0.0164575334638 0.0125575726852 0.00686521595344 -0.0144916502759 -0.00543817412108 -0.0263480637223 -0.015117011033 -0.0188090614974 -0.0260683037341 -0.0208534952253 -0.0113473143429 -0.0300426613539 0.0405975058675 0.0136088356376 0.009625967592 0.0491875857115 0.00344011140987 0.0133795123547 0.0477037541568 0.0117063503712 0.0115124462172 0.0635530352592 -0.0652642175555 -0.0305935442448 0.068222001195 -0.0600504949689 -0.0280908290297 0.0649472996593 -0.0564471706748 -0.0301647242159 0.0420701652765 -0.00905739609152 -0.0347143113613 0.0465668439865 -0.0175501257181 -0.036243584007 0.0461902655661 -0.0180508662015 -0.0307826343924 0.0852778479457 -0.0646592378616 0.00590485380962 0.0818465650082 -0.0646916031837 0.004601073917 0.0814371109009 -0.0711335614324 0.00815863255411 -0.0847244933248 0.0610410422087 0.010350859724 -0.0889886766672 0.0597609467804 0.0147773399949 -0.0844842940569 0.0636154636741 0.0146970963106 -0.0280799940228 -0.055304210633 0.00326808029786 -0.0304189305753 -0.0502045191824 0.00526972860098 -0.033011417836 -0.0568285062909 0.00476831337437 0.0203334894031 0.0168978217989 -0.039546135813 0.00984027050436 0.0123339463025 -0.039398021996 0.0116761699319 0.0192524939775 -0.0374199561775 0.0734314844012 -0.0137593969703 -0.0012326046126 0.0798162445426 -0.0199222583324 -0.0021768680308 0.0711212977767 -0.0141868051142 -0.00323123089038 0.0442871451378 -0.00394489476457 -0.0271087102592 0.0432890281081 -0.00950278807431 -0.0291274245828 0.0512403547764 -0.00441910745576 -0.0253982823342 0.0831556394696 -0.0222345832735 0.00146252510604 0.0813561230898 -0.0232167206705 0.00461526820436 0.0839628651738 -0.0313502401114 0.00374555471353 0.0605014450848 -0.0130754690617 -0.0308906380087 0.0602696500719 -0.00843365583569 -0.0316083207726 0.0604395531118 -0.014644660987 -0.0287422351539 -0.0122720496729 0.00743707921356 -0.0320300944149 -0.0133467214182 0.00200212979689 -0.0306190121919 -0.0171094201505 0.00397073151544 -0.0321909785271 0.051399409771 0.0217050053179 -0.0416473895311 0.0504528395832 0.0169036872685 -0.0417337194085 0.0347270555794 0.027764454484 -0.0385278463364 -0.0723753124475 0.0711080506444 0.0155806122348 -0.0652576088905 0.0690394937992 0.0202288385481 -0.0661604255438 0.0740167275071 0.0206666216254 0.0579114630818 0.0292274262756 -0.0363327749074 0.060431715101 0.0229300074279 -0.0391151756048 0.0529840812087 0.0303148236126 -0.0373462289572 0.0649568662047 0.0234411917627 -0.0345332249999 0.0668411776423 0.0276774372905 -0.0293406844139 0.0694160535932 0.0198294073343 -0.0330271050334 -0.0477432273328 0.0646687820554 -0.00769540853798 -0.0606118962169 0.0685695037246 0.00690156780183 -0.0507754385471 0.0627422481775 0.000138281728141 -0.0911771580577 0.0422582998872 0.0333826653659 -0.0884201526642 0.046169500798 0.0338194072247 -0.0878712534904 0.0469209626317 0.0273304414004 0.0813561230898 -0.0232167206705 0.00461526820436 0.0754076316953 -0.0163155626506 0.0023912510369 0.075042180717 -0.0225737150759 0.0023139808327 -0.0379135832191 -0.00708554778248 0.00403723120689 -0.0364621728659 -4.06305734941e-05 0.00923090986907 -0.042618881911 -0.00202813232318 0.0045546782203 -0.022982083261 -0.0271414145827 -3.11253916152e-06 -0.0230524111539 -0.0281649492681 0.00343656959012 -0.0232568588108 -0.0392127744853 0.00331220822409 0.0418007522821 -0.00242996658199 -0.00372027652338 0.0539004430175 -0.00540307629853 -0.00894699897617 0.0495941489935 -0.00505510251969 0.00281282933429 0.0725733786821 -0.0235378220677 0.00565016036853 0.075042180717 -0.0225737150759 0.0023139808327 0.0754076316953 -0.0163155626506 0.0023912510369 0.0380271226168 0.0364866815507 -0.00783421378583 0.0472641289234 0.0361711494625 -0.00451004551724 0.0447824895382 0.0373931154609 -0.0133784590289 -0.0836498662829 0.0665282458067 0.00920514483005 -0.0859415605664 0.0709468647838 0.00737344101071 -0.0803875476122 0.0608413852751 0.00552876945585 -0.0789092034101 0.0428538136184 0.00382694439031 -0.0741797760129 0.0406901091337 0.00791172217578 -0.0737345814705 0.038439001888 0.0121840443462 0.0683374479413 -0.0672494471073 -0.0335944145918 0.0650694146752 -0.0661949291825 -0.0290601216257 0.0672754794359 -0.0748537257314 -0.028308160603 -0.0507754385471 0.0627422481775 0.000138281728141 -0.0647223517299 0.066875346005 0.0136522306129 -0.0497838333249 0.0559922680259 0.00280405301601 0.0300506222993 0.0192408021539 -0.0371698960662 0.035920906812 0.0201838165522 -0.0398071408272 0.036672718823 0.0113793108612 -0.0394277796149 -0.017013894394 -0.0347188636661 -0.030624659732 -0.0182791948318 -0.019947366789 -0.0281752515584 -0.0151859046891 -0.0280584804714 -0.02708007209 -0.00387709727511 -0.00124160922132 0.00781158497557 0.00759891513735 -0.00405428977683 0.00967596191913 0.00531596504152 0.00230240589008 0.0111257759854 0.0700432732701 0.0106197241694 -0.0254250112921 0.0710225701332 0.0116371689364 -0.0303473770618 0.0717578157783 0.015957320109 -0.0263392049819 -0.04279313609 -0.0713429301977 0.00690987613052 -0.0468597859144 -0.0764623731375 0.00214877328835 -0.0426602773368 -0.0763025358319 0.00917287915945 0.0577224753797 -0.020547606051 -0.0277862790972 0.0577097833157 -0.0276579707861 -0.0262848697603 0.0604395531118 -0.014644660987 -0.0287422351539 -0.0353548154235 -0.0399240851402 0.000486829754664 -0.0334986448288 -0.0282570403069 -0.00100804551039 -0.0337675176561 -0.0367394164205 -0.00267041777261 0.0565774738789 -0.00623939558864 -0.00480305217206 0.0598698109388 -0.00478675868362 0.00212140567601 0.0585702136159 -0.00812319666147 0.00419311970472 -0.035688880831 0.0322657860816 -0.00136898260098 -0.0400194935501 0.035411670804 -0.00117507681716 -0.0440319478512 0.0313652604818 0.0016547862906 -0.074530698359 0.0564152859151 0.000770429789554 -0.0684591978788 0.0459762923419 -0.00223574182019 -0.0736100152135 0.0473809987307 0.00139974115882 -0.0916560813785 0.0415009707212 0.0378023125231 -0.0911771580577 0.0422582998872 0.0333826653659 -0.0893638506532 0.0394080318511 0.0356427282095 -0.0181487128139 0.0191756095737 0.00725237466395 -0.0131820514798 0.0203566141427 0.00420704483986 -0.00962803792208 0.0256944280118 1.13362448246e-05 0.089011117816 -0.049574509263 0.0023703314364 0.0839628651738 -0.0313502401114 0.00374555471353 0.0843727812171 -0.0348832271993 0.00490371277556 0.0656465515494 0.00329640181735 -0.0110076479614 0.0583206899464 -0.00380795542151 -0.00911643542349 0.0622391216457 0.00130491273012 -0.0162859521806 -0.0342719815671 -0.0143612474203 -0.0263750851154 -0.0393666550517 -0.0106545481831 -0.0230054780841 -0.0414947420359 -0.00295710819773 -0.0225406978279 -0.0345421843231 -0.0537865906954 -3.66152089555e-05 -0.0363510511816 -0.059165596962 -0.00133417372126 -0.0356382876635 -0.0533351935446 0.00276124686934 0.0434862487018 0.032226357609 -0.036325853318 0.0348762497306 0.0356907360256 -0.0298048574477 0.0469395145774 0.0357567593455 -0.0306289978325 -0.0233208872378 0.0171473640949 0.00885204598308 -0.030961079523 0.00549497501925 0.0111165465787 -0.0272913184017 0.00181260507088 0.0117624523118 -0.0251056849957 -0.0178923550993 -0.0185515768826 -0.0216245427728 -0.0176681727171 -0.0117809949443 -0.0292823426425 -0.016650011763 -0.0205444768071 -0.00599885499105 -0.0184409264475 -0.015290110372 0.00896836072206 -0.0178859829903 -0.0177515987307 -0.00245248852298 -0.0189631339163 -0.0111456979066 -0.0745909139514 0.0392232574522 0.0197847299278 -0.0846682190895 0.0387630797923 0.0249170754105 -0.0803267359734 0.0397947318852 0.0164727605879 -0.0351863466203 -0.0765412300825 0.000269046984613 -0.0426602773368 -0.0763025358319 0.00917287915945 -0.0421986393631 -0.0759468376637 -0.00256403326057 -0.00338091235608 0.0199865065515 -0.0312582217157 -0.0111597320065 0.0221810676157 -0.0287142917514 -0.00435515260324 0.025612950325 -0.0247763358057 0.0747518464923 0.0119193941355 -0.00421227002516 0.0743348672986 0.00406845519319 -0.00470197200775 0.0735007673502 0.0154050234705 -0.0108875501901 -0.0884201526642 0.046169500798 0.0338194072247 -0.0911771580577 0.0422582998872 0.0333826653659 -0.0916560813785 0.0415009707212 0.0378023125231 -0.0390509627759 -0.0665916949511 0.00520234834403 -0.0377826243639 -0.0715468376875 0.00810029637069 -0.0349772572517 -0.0686603486538 0.00445972103626 0.0405975058675 0.0136088356376 0.009625967592 0.0477037541568 0.0117063503712 0.0115124462172 0.0422127395868 0.0228577423841 0.00999154523015 -0.0337675176561 -0.0367394164205 -0.00267041777261 -0.0300818383694 -0.0526309125125 -0.000229920042329 -0.0345421843231 -0.0537865906954 -3.66152089555e-05 -0.0361147299409 -0.0120508149266 0.00691540632397 -0.0336843542755 -0.0225331783295 0.00486868014559 -0.0324501506984 -0.014356858097 0.00908888690174 -0.0690960809588 0.0526289157569 0.0247765686363 -0.0739856958389 0.0511276461184 0.0285730604082 -0.0781400799751 0.0399436429143 0.0323086194694 -0.00275399489328 0.0223833154887 0.00140674877912 -0.00733371544629 0.0176978223026 0.00535471225157 0.0126880630851 0.0209196154028 0.00636390130967 -0.0386477708817 0.0575842261314 -0.0097325630486 -0.0444217473269 0.0602424219251 -0.00442242622375 -0.0322041623294 0.0489787906408 -0.00726023875177 -0.02261329256 -0.0363039076328 -0.031984012574 -0.025938231498 -0.03466161713 -0.0289873387665 -0.0252810381353 -0.0247280094773 -0.0293741747737 0.0655900388956 0.0184390544891 0.00736436760053 0.0680408999324 0.0129547277465 0.00671541178599 0.0706675350666 0.0171718429774 0.00147831998765 -0.02261329256 -0.0363039076328 -0.031984012574 -0.0201097112149 -0.0344850867987 -0.0324018150568 -0.0163839384913 -0.0424141436815 -0.0313666537404 0.0709100887179 0.0228983853012 -0.0246697273105 0.0717578157783 0.015957320109 -0.0263392049819 0.0694160535932 0.0198294073343 -0.0330271050334 -0.0845351070166 0.0501509457827 0.0333207882941 -0.0804472342134 0.0510345138609 0.0322105102241 -0.0754706263542 0.057246144861 0.0283697806299 0.0782531201839 -0.0722363963723 0.00564285600558 0.0814371109009 -0.0711335614324 0.00815863255411 0.0818465650082 -0.0646916031837 0.004601073917 -0.00944765098393 -0.0544546283782 -0.0279082152992 -0.00612018536776 -0.0598083510995 -0.0299312565476 -0.0123113365844 -0.0451743938029 -0.0291688665748 -0.0181487128139 0.0191756095737 0.00725237466395 -0.0221735388041 0.0248348526657 0.00527667580172 -0.0233208872378 0.0171473640949 0.00885204598308 -0.0615916401148 0.041047938168 0.00514492439106 -0.0622629895806 0.0477367714047 0.00780300609767 -0.0639205127954 0.0411553606391 0.00809910707176 0.0129595380276 -0.00801198463887 0.00812369771302 0.0163268223405 -0.0114426901564 0.00449271220714 0.0222891252488 -0.0072523932904 0.00697265705094 0.0598698109388 -0.00478675868362 0.00212140567601 0.0565774738789 -0.00623939558864 -0.00480305217206 0.0586783364415 -0.0037231084425 -0.00361427664757 -0.0177184212953 -0.0485680811107 -0.0308595653623 -0.0163839384913 -0.0424141436815 -0.0313666537404 -0.0124928699806 -0.05317331478 -0.032723814249 0.0795691013336 -0.0337514281273 0.00747773703188 0.0728958249092 -0.0278529170901 0.00544968014583 0.0709631517529 -0.0282764472067 0.0042789876461 0.0603620111942 0.0056228893809 0.0125811565667 0.0636151582003 -0.00344871124253 0.0112777063623 0.0643844082952 0.0092457216233 0.0105861956254 0.0148993665352 -0.016240587458 -0.00226025399752 0.0233577005565 -0.0130151677877 -0.00125345273409 0.0163268223405 -0.0114426901564 0.00449271220714 0.058990996331 -0.0744717493653 -0.0386376976967 0.0644002556801 -0.0638302341104 -0.0359592176974 0.066437445581 -0.0657137334347 -0.0362850166857 0.0837335810065 -0.0474399290979 -0.000192326551769 0.0889291241765 -0.0584181435406 -0.00213292124681 0.0838564783335 -0.0586024485528 -0.00192842667457 -0.0280799940228 -0.055304210633 0.00326808029786 -0.0269599817693 -0.0495302826166 0.00157890853006 -0.0232568588108 -0.0392127744853 0.00331220822409 -0.0421986393631 -0.0759468376637 -0.00256403326057 -0.0426602773368 -0.0763025358319 0.00917287915945 -0.0468597859144 -0.0764623731375 0.00214877328835 -0.0409325622022 0.00616488046944 -0.0320984274149 -0.0444579236209 0.00304091628641 -0.0277560744435 -0.0457198508084 0.01298922766 -0.0289378613234 0.00794281996787 -0.0124184312299 -0.0286089126021 0.0144752385095 -0.0070848762989 -0.034492328763 0.0147100556642 -0.0147073278204 -0.0253510158509 0.0600861534476 -0.00594944367185 -0.0371469557285 0.0621104352176 0.00453598890454 -0.0360744036734 0.0675886794925 0.00517250876874 -0.0339571870863 0.0380213819444 -0.000848870549817 -0.0383270718157 0.0393960885704 -0.00669425539672 -0.0340571328998 0.0301524624228 -0.00372725259513 -0.0304073672742 0.066437445581 -0.0657137334347 -0.0362850166857 0.0703099220991 -0.0588180907071 -0.0379472970963 0.0683374479413 -0.0672494471073 -0.0335944145918 0.0855906233191 -0.056857522577 0.00729697011411 0.0842658951879 -0.0518600940704 0.00558784976602 0.0809005796909 -0.0487979911268 0.00395165523514 -0.0164575334638 0.0125575726852 0.00686521595344 -0.0207055173814 0.00392329553142 0.00909946020693 -0.0152684766799 0.00252701970749 0.00554668903351 -0.0484882481396 0.0242904219776 -0.00165670679417 -0.0584039799869 0.0388491488993 0.00238339230418 -0.051504354924 0.0203009396791 -0.0043327328749 -0.0736100152135 0.0473809987307 0.00139974115882 -0.0690660402179 0.0400546975434 0.00533317448571 -0.0741797760129 0.0406901091337 0.00791172217578 0.0767389982939 -0.0762002691627 0.00715722655877 0.0842919945717 -0.076128102839 0.00855887308717 0.0852193087339 -0.0724491924047 0.00872989185154 -0.0137986438349 -0.001824338804 -0.028773734346 -0.0196293834597 -0.0012800822733 -0.0339007712901 -0.0171094201505 0.00397073151544 -0.0321909785271 0.0709100887179 0.0228983853012 -0.0246697273105 0.0694160535932 0.0198294073343 -0.0330271050334 0.0668411776423 0.0276774372905 -0.0293406844139 -0.0322233475745 -0.0618353933096 -0.00231006834656 -0.0370292626321 -0.0664719641209 -0.00097158877179 -0.0363510511816 -0.059165596962 -0.00133417372126 0.0469395145774 0.0357567593455 -0.0306289978325 0.0520869679749 0.0365738160908 -0.0220487043262 0.0584006085992 0.0334684699774 -0.0289356615394 0.0825529024005 -0.0241261273623 -0.00263262726367 0.0831556394696 -0.0222345832735 0.00146252510604 0.0839467793703 -0.0338173881173 -0.0013689303305 -0.0174274221063 -0.0494892522693 -0.0261709243059 -0.0171560104936 -0.0368272177875 -0.0223063975573 -0.0212532021105 -0.0387861654162 -0.0227633845061 -0.0446118675172 0.0268158894032 -0.0240276791155 -0.0521116182208 0.0417004041374 -0.0192602630705 -0.0412645675242 0.0296845398843 -0.0242789257318 0.0806738361716 -0.071406044066 -0.00161154090893 0.0805870443583 -0.075636588037 -0.00136879249476 0.0769691392779 -0.075030490756 0.002559523331 0.0747518464923 0.0119193941355 -0.00421227002516 0.0735007673502 0.0154050234705 -0.0108875501901 0.0736648738384 0.0202186964452 -0.00637979293242 0.00260267360136 0.0286370813847 -0.0185407903045 0.0020479504019 0.0282315947115 -0.00889560487121 0.00980327930301 0.028790736571 -0.00937963183969 0.0504257716238 -0.016582140699 -0.0392883382738 0.0514739230275 -0.0248519349843 -0.0344557315111 0.0465668439865 -0.0175501257181 -0.036243584007 -0.0870704501867 0.052732732147 0.0148491580039 -0.0889886766672 0.0597609467804 0.0147773399949 -0.0847244933248 0.0610410422087 0.010350859724 0.036672718823 0.0113793108612 -0.0394277796149 0.032403241843 0.0125592537224 -0.0369361899793 0.0300506222993 0.0192408021539 -0.0371698960662 0.0300506222993 0.0192408021539 -0.0371698960662 0.0261729359627 0.0250671524554 -0.0349872037768 0.035920906812 0.0201838165522 -0.0398071408272 -0.0791537687182 0.0380757376552 0.0263591650873 -0.0846682190895 0.0387630797923 0.0249170754105 -0.0745909139514 0.0392232574522 0.0197847299278 0.0697775110602 -0.0257171951234 0.000200476919417 0.0690664798021 -0.0187059957534 -0.00380081683397 0.0721606612206 -0.022786539048 -0.00311841559596 -0.0680031627417 0.0388116128743 0.0118984086439 -0.0737345814705 0.038439001888 0.0121840443462 -0.0646032914519 0.0391447842121 0.00296345702372 -0.0680218562484 0.0746396705508 0.0158250704408 -0.0647223517299 0.066875346005 0.0136522306129 -0.0686424598098 0.0706430822611 0.0113607710227 -0.0845351070166 0.0501509457827 0.0333207882941 -0.0796836540103 0.0613558962941 0.0230027772486 -0.084884122014 0.054450109601 0.0266954693943 0.0434862487018 0.032226357609 -0.036325853318 0.0456950850785 0.0270799733698 -0.0398734845221 0.0347270555794 0.027764454484 -0.0385278463364 0.0696674659848 0.0262376572937 -0.0128174172714 0.0711485892534 0.0247458182275 -0.00319633609615 0.0736648738384 0.0202186964452 -0.00637979293242 0.0347270555794 0.027764454484 -0.0385278463364 0.0261729359627 0.0250671524554 -0.0349872037768 0.0226895809174 0.0281979069114 -0.0306504108012 -0.0847244933248 0.0610410422087 0.010350859724 -0.0838714838028 0.0507906712592 0.00711987819523 -0.0870704501867 0.052732732147 0.0148491580039 0.036672718823 0.0113793108612 -0.0394277796149 0.035920906812 0.0201838165522 -0.0398071408272 0.0441741943359 0.0165656842291 -0.0412995144725 0.0348762497306 0.0356907360256 -0.0298048574477 0.0197386574 0.031497310847 -0.0197209063917 0.0356194712222 0.0371010899544 -0.0225756745785 0.0838564783335 -0.0586024485528 -0.00192842667457 0.0816290304065 -0.0504989773035 0.00101880810689 0.0837335810065 -0.0474399290979 -0.000192326551769 -0.0878021046519 0.033477794379 0.0395364612341 -0.0822595283389 0.0367101840675 0.0404899641871 -0.0876308903098 0.0384379811585 0.0428250767291 -0.0529348887503 0.0521784499288 0.0046043144539 -0.0635816827416 0.0559310466051 0.0108306938782 -0.0614165142179 0.0504314303398 0.0064328792505 -0.074530698359 0.0564152859151 0.000770429789554 -0.0803875476122 0.0608413852751 0.00552876945585 -0.0764897316694 0.0619541816413 0.00187294220086 0.0870789960027 -0.071105375886 -0.00195318716578 0.0858451649547 -0.0757835879922 -0.00102130440064 0.0805870443583 -0.075636588037 -0.00136879249476 -0.0822595283389 0.0367101840675 0.0404899641871 -0.0781400799751 0.0399436429143 0.0323086194694 -0.0812487527728 0.04326537624 0.0373680815101 0.0543802753091 0.0360043048859 -0.0105734793469 0.0572973936796 0.0331504121423 -0.000841111701448 0.0609902478755 0.0335634611547 -0.0094895362854 0.0547827705741 -0.0236574988812 -0.0368034355342 0.0504257716238 -0.016582140699 -0.0392883382738 0.0575529150665 -0.022210219875 -0.0367079302669 0.0514739230275 -0.0248519349843 -0.0344557315111 0.0504257716238 -0.016582140699 -0.0392883382738 0.0547827705741 -0.0236574988812 -0.0368034355342 -0.012282749638 -0.00724740931764 -2.84041252598e-05 -0.012700362131 0.00108930072747 0.00499837147072 -0.0160121638328 -0.00505490787327 0.00223769713193 0.0356194712222 0.0371010899544 -0.0225756745785 0.0380271226168 0.0364866815507 -0.00783421378583 0.0447824895382 0.0373931154609 -0.0133784590289 0.089011117816 -0.049574509263 0.0023703314364 0.0917203947902 -0.059632204473 0.00453671533614 0.0924971923232 -0.0599343143404 0.00196787621826 0.0842658951879 -0.0518600940704 0.00558784976602 0.0819737017155 -0.0426090881228 0.00562430731952 0.0809005796909 -0.0487979911268 0.00395165523514 0.0728958249092 -0.0278529170901 0.00544968014583 0.0648333206773 -0.0193411242217 0.00892408099025 0.0709631517529 -0.0282764472067 0.0042789876461 0.0374021679163 0.0269114822149 0.00774644361809 0.0422127395868 0.0228577423841 0.00999154523015 0.048808876425 0.0252794381231 0.00892338063568 -0.0381523445249 -0.00304430956021 -0.029428480193 -0.0383309051394 -0.00495197204873 -0.0253514498472 -0.0414947420359 -0.00295710819773 -0.0225406978279 0.0226106531918 0.0281752999872 -0.00177787418943 0.0336626619101 0.0301720779389 0.00381930125877 0.0347373187542 0.0342971347272 -0.00132000620943 0.0211553759873 0.0312036816031 -0.0245885774493 0.0197386574 0.031497310847 -0.0197209063917 0.0348762497306 0.0356907360256 -0.0298048574477 0.0555975027382 0.0286405310035 0.00528475176543 0.0585609525442 0.0207895357162 0.00992077030241 0.0627590343356 0.0254758372903 0.00493016792461 -0.0281191021204 0.0276504568756 -0.0284133274108 -0.0276381317526 0.0215675216168 -0.0321951173246 -0.0347637310624 0.0273377392441 -0.0288795996457 -0.0483878850937 0.0177123211324 -0.0199927464128 -0.046883624047 0.00825335178524 -0.0246345344931 -0.0473023541272 0.0122802108526 -0.0124696297571 0.0529840812087 0.0303148236126 -0.0373462289572 0.0434862487018 0.032226357609 -0.036325853318 0.0469395145774 0.0357567593455 -0.0306289978325 0.0210444331169 0.030273001641 -0.00706696789712 0.0074149183929 0.0266806911677 -0.00382403749973 0.0226106531918 0.0281752999872 -0.00177787418943 -0.0102530596778 0.0101205231622 0.0068081477657 -0.012700362131 0.00108930072747 0.00499837147072 0.00083865004126 0.00723583996296 0.0104546826333 0.0736648738384 0.0202186964452 -0.00637979293242 0.0735007673502 0.0154050234705 -0.0108875501901 0.0713142603636 0.0189655330032 -0.0151191568002 0.0554471611977 0.0115022500977 -0.042177259922 0.0504528395832 0.0169036872685 -0.0417337194085 0.051399409771 0.0217050053179 -0.0416473895311 -0.0409355126321 0.00761891668662 0.00908278673887 -0.0434098988771 0.0118352910504 0.00674757128581 -0.0448838733137 0.00699473544955 0.00491576688364 pyformex-0.8.6/pyformex/data/teapot.off0000644000211500021150000056145611402236731020024 0ustar benebene00000000000000OFF 5027 2016 0 16.750000 50.500000 0.000000 16.459900 50.500000 2.214800 16.681900 48.313499 2.214800 16.681900 48.313400 2.214700 16.965300 48.383999 0.000000 16.750000 50.500000 0.000000 16.459900 50.500000 2.214800 15.695300 50.500000 3.796800 15.935000 48.127800 3.796800 15.935000 48.127701 3.796700 16.681900 48.313499 2.214800 16.459801 50.500000 2.214700 15.695300 50.500000 3.796800 14.614200 50.500000 4.746000 14.878900 47.865200 4.746000 14.878800 47.865200 4.745900 15.935000 48.127800 3.796800 15.695300 50.500000 3.796700 14.614200 50.500000 4.746000 13.375000 50.500000 5.062400 13.668300 47.564098 5.062400 13.668200 47.563999 5.062300 14.878900 47.865200 4.746000 14.614100 50.500000 4.745900 13.375000 50.500000 5.062400 12.135700 50.500000 4.746000 12.457700 47.263100 4.746000 12.457600 47.263000 4.745900 13.668300 47.564098 5.062400 13.375000 50.500000 5.062300 12.135700 50.500000 4.746000 11.054600 50.500000 3.796800 11.401600 47.000500 3.796800 11.401500 47.000401 3.796700 12.457700 47.263100 4.746000 12.135700 50.500000 4.745900 11.054600 50.500000 3.796800 10.290000 50.500000 2.214800 10.654600 46.814701 2.214800 10.654600 46.814701 2.214700 11.401600 47.000500 3.796800 11.054500 50.500000 3.796700 10.290000 50.500000 2.214800 10.000000 50.500000 0.000000 10.371300 46.744301 0.000000 10.371200 46.744301 0.000000 10.654600 46.814701 2.214800 10.289900 50.500000 2.214700 16.965300 48.383999 0.000000 16.681900 48.313499 2.214800 17.365299 45.790298 2.214800 17.365200 45.790199 2.214700 17.628901 45.912102 0.000000 16.965200 48.383900 0.000000 16.681900 48.313499 2.214800 15.935000 48.127800 3.796800 16.670300 45.469200 3.796800 16.670200 45.469200 3.796700 17.365299 45.790298 2.214800 16.681900 48.313400 2.214700 15.935000 48.127800 3.796800 14.878900 47.865200 4.746000 15.687800 45.015202 4.746000 15.687800 45.015202 4.745900 16.670300 45.469200 3.796800 15.935000 48.127701 3.796700 14.878900 47.865200 4.746000 13.668300 47.564098 5.062400 14.561500 44.494801 5.062400 14.561400 44.494801 5.062300 15.687800 45.015202 4.746000 14.878800 47.865200 4.745900 13.668300 47.564098 5.062400 12.457700 47.263100 4.746000 13.435200 43.974400 4.746000 13.435100 43.974300 4.745900 14.561500 44.494801 5.062400 13.668200 47.563999 5.062300 12.457700 47.263100 4.746000 11.401600 47.000500 3.796800 12.452600 43.520500 3.796800 12.452500 43.520500 3.796700 13.435200 43.974400 4.746000 12.457600 47.263000 4.745900 11.401600 47.000500 3.796800 10.654600 46.814701 2.214800 11.757700 43.199402 2.214800 11.757600 43.199402 2.214700 12.452600 43.520500 3.796800 11.401500 47.000401 3.796700 10.654600 46.814701 2.214800 10.371300 46.744301 0.000000 11.494100 43.077599 0.000000 11.494000 43.077499 0.000000 11.757700 43.199402 2.214800 10.654600 46.814701 2.214700 17.628901 45.912102 0.000000 17.365299 45.790298 2.214800 18.535601 43.044102 2.214800 18.535601 43.044102 2.214700 18.767000 43.202801 0.000000 17.628901 45.912102 0.000000 17.365299 45.790298 2.214800 16.670300 45.469200 3.796800 17.925600 42.625801 3.796800 17.925600 42.625801 3.796700 18.535601 43.044102 2.214800 17.365200 45.790199 2.214700 16.670300 45.469200 3.796800 15.687800 45.015202 4.746000 17.063000 42.034302 4.746000 17.062901 42.034302 4.745900 17.925600 42.625801 3.796800 16.670200 45.469200 3.796700 15.687800 45.015202 4.746000 14.561500 44.494801 5.062400 16.074301 41.356300 5.062400 16.074301 41.356300 5.062300 17.063000 42.034302 4.746000 15.687800 45.015202 4.745900 14.561500 44.494801 5.062400 13.435200 43.974400 4.746000 15.085500 40.678299 4.746000 15.085400 40.678200 4.745900 16.074301 41.356300 5.062400 14.561400 44.494801 5.062300 13.435200 43.974400 4.746000 12.452600 43.520500 3.796800 14.223000 40.086800 3.796800 14.222900 40.086700 3.796700 15.085500 40.678299 4.746000 13.435100 43.974300 4.745900 12.452600 43.520500 3.796800 11.757700 43.199402 2.214800 13.613000 39.668499 2.214800 13.612900 39.668400 2.214700 14.223000 40.086800 3.796800 12.452500 43.520500 3.796700 11.757700 43.199402 2.214800 11.494100 43.077599 0.000000 13.381500 39.509800 0.000000 13.381500 39.509701 0.000000 13.613000 39.668499 2.214800 11.757600 43.199402 2.214700 18.767000 43.202801 0.000000 18.535601 43.044102 2.214800 20.218901 40.189098 2.214800 20.218901 40.188999 2.214700 20.406200 40.375000 0.000000 18.767000 43.202801 0.000000 18.535601 43.044102 2.214800 17.925600 42.625801 3.796800 19.725000 39.699299 3.796800 19.725000 39.699200 3.796700 20.218901 40.189098 2.214800 18.535601 43.044102 2.214700 17.925600 42.625801 3.796800 17.063000 42.034302 4.746000 19.026899 39.006699 4.746000 19.026800 39.006599 4.745900 19.725000 39.699299 3.796800 17.925600 42.625801 3.796700 17.063000 42.034302 4.746000 16.074301 41.356300 5.062400 18.226500 38.212799 5.062400 18.226400 38.212700 5.062300 19.026899 39.006699 4.746000 17.062901 42.034302 4.745900 16.074301 41.356300 5.062400 15.085500 40.678299 4.746000 17.426201 37.418900 4.746000 17.426201 37.418800 4.745900 18.226500 38.212799 5.062400 16.074301 41.356300 5.062300 15.085500 40.678299 4.746000 14.223000 40.086800 3.796800 16.728001 36.726398 3.796800 16.728001 36.726299 3.796700 17.426201 37.418900 4.746000 15.085400 40.678200 4.745900 14.223000 40.086800 3.796800 13.613000 39.668499 2.214800 16.234100 36.236500 2.214800 16.234100 36.236401 2.214700 16.728001 36.726398 3.796800 14.222900 40.086700 3.796700 13.613000 39.668499 2.214800 13.381500 39.509800 0.000000 16.046801 36.050701 0.000000 16.046801 36.050701 0.000000 16.234100 36.236500 2.214800 13.612900 39.668400 2.214700 20.406200 40.375000 0.000000 20.218901 40.189098 2.214800 22.440800 37.339199 2.214800 22.440701 37.339100 2.214700 22.572701 37.547100 0.000000 20.406200 40.375000 0.000000 20.218901 40.189098 2.214800 19.725000 39.699299 3.796800 22.093100 36.791302 3.796800 22.093000 36.791302 3.796700 22.440800 37.339199 2.214800 20.218901 40.188999 2.214700 19.725000 39.699299 3.796800 19.026899 39.006699 4.746000 21.601500 36.016701 4.746000 21.601400 36.016701 4.745900 22.093100 36.791302 3.796800 19.725000 39.699200 3.796700 19.026899 39.006699 4.746000 18.226500 38.212799 5.062400 21.037901 35.128700 5.062400 21.037901 35.128700 5.062300 21.601500 36.016701 4.746000 19.026800 39.006599 4.745900 18.226500 38.212799 5.062400 17.426201 37.418900 4.746000 20.474400 34.240700 4.746000 20.474300 34.240601 4.745900 21.037901 35.128700 5.062400 18.226400 38.212700 5.062300 17.426201 37.418900 4.746000 16.728001 36.726398 3.796800 19.982700 33.466099 3.796800 19.982700 33.466000 3.796700 20.474400 34.240700 4.746000 17.426201 37.418800 4.745900 16.728001 36.726398 3.796800 16.234100 36.236500 2.214800 19.635000 32.918201 2.214800 19.635000 32.918201 2.214700 19.982700 33.466099 3.796800 16.728001 36.726299 3.796700 19.503099 32.710300 0.000000 19.503000 32.710300 0.000000 19.635000 32.918201 2.214800 16.234100 36.236401 2.214700 22.572701 37.547100 0.000000 22.440800 37.339199 2.214800 25.227200 34.608398 2.214800 25.227100 34.608299 2.214700 25.292900 34.837799 0.000000 22.572701 37.547100 0.000000 22.440800 37.339199 2.214800 22.093100 36.791302 3.796800 25.054001 34.003601 3.796800 25.054001 34.003601 3.796700 25.227200 34.608398 2.214800 22.440701 37.339100 2.214700 22.093100 36.791302 3.796800 21.601500 36.016701 4.746000 24.809000 33.148399 4.746000 24.809000 33.148300 4.745900 25.054001 34.003601 3.796800 22.093000 36.791302 3.796700 21.601500 36.016701 4.746000 21.037901 35.128700 5.062400 24.528299 32.168201 5.062400 24.528200 32.168201 5.062300 24.809000 33.148399 4.746000 21.601400 36.016701 4.745900 21.037901 35.128700 5.062400 20.474400 34.240700 4.746000 24.247499 31.187901 4.746000 24.247400 31.187901 4.745900 24.528299 32.168201 5.062400 21.037901 35.128700 5.062300 20.474400 34.240700 4.746000 19.982700 33.466099 3.796800 24.002600 30.332800 3.796800 24.002501 30.332701 3.796700 24.247499 31.187901 4.746000 20.474300 34.240601 4.745900 19.982700 33.466099 3.796800 19.635000 32.918201 2.214800 23.829300 29.727900 2.214800 23.829201 29.727800 2.214700 24.002600 30.332800 3.796800 19.982700 33.466000 3.796700 19.635000 32.918201 2.214800 19.503099 32.710300 0.000000 23.763599 29.498501 0.000000 23.763500 29.498501 0.000000 23.829300 29.727900 2.214800 19.635000 32.918201 2.214700 25.292900 34.837799 0.000000 25.227200 34.608398 2.214800 28.603901 32.110600 2.214800 28.603901 32.110500 2.214700 28.593201 32.365898 0.000000 25.292900 34.837700 0.000000 25.227200 34.608398 2.214800 25.054001 34.003601 3.796800 28.632000 31.437700 3.796800 28.631901 31.437700 3.796700 28.603901 32.110600 2.214800 25.227100 34.608299 2.214700 25.054001 34.003601 3.796800 24.809000 33.148399 4.746000 28.671801 30.486200 4.746000 28.671801 30.486200 4.745900 28.632000 31.437700 3.796800 25.054001 34.003601 3.796700 24.809000 33.148399 4.746000 24.528299 32.168201 5.062400 28.717400 29.395500 5.062400 28.717300 29.395500 5.062300 28.671801 30.486200 4.746000 24.809000 33.148300 4.745900 24.528299 32.168201 5.062400 24.247499 31.187901 4.746000 28.762899 28.304800 4.746000 28.762800 28.304800 4.745900 28.717400 29.395500 5.062400 24.528200 32.168201 5.062300 24.247499 31.187901 4.746000 24.002600 30.332800 3.796800 28.802700 27.353300 3.796800 28.802700 27.353300 3.796700 28.762899 28.304800 4.746000 24.247400 31.187901 4.745900 24.002600 30.332800 3.796800 23.829300 29.727900 2.214800 28.830799 26.680300 2.214800 28.830700 26.680201 2.214700 28.802700 27.353300 3.796800 24.002501 30.332701 3.796700 23.829300 29.727900 2.214800 23.763599 29.498501 0.000000 28.841499 26.425100 0.000000 28.841400 26.425100 0.000000 28.830799 26.680300 2.214800 23.829201 29.727800 2.214700 28.593201 32.365898 0.000000 28.603901 32.110600 2.214800 32.596600 29.959900 2.214800 32.596500 29.959801 2.214700 32.500000 30.250000 0.000000 28.593201 32.365799 0.000000 28.603901 32.110600 2.214800 28.632000 31.437700 3.796800 32.851501 29.195299 3.796800 32.851501 29.195200 3.796700 32.596600 29.959900 2.214800 28.603901 32.110500 2.214700 28.632000 31.437700 3.796800 28.671801 30.486200 4.746000 33.211899 28.114201 4.746000 33.211800 28.114201 4.745900 32.851501 29.195299 3.796800 28.631901 31.437700 3.796700 28.671801 30.486200 4.746000 28.717400 29.395500 5.062400 33.625000 26.875000 5.062400 33.625000 26.875000 5.062300 33.211899 28.114201 4.746000 28.671801 30.486200 4.745900 28.717400 29.395500 5.062400 28.762899 28.304800 4.746000 34.037998 25.635700 4.746000 34.037899 25.635700 4.745900 33.625000 26.875000 5.062400 28.717300 29.395500 5.062300 28.762899 28.304800 4.746000 28.802700 27.353300 3.796800 34.398399 24.554600 3.796800 34.398300 24.554501 3.796700 34.037998 25.635700 4.746000 28.762800 28.304800 4.745900 28.802700 27.353300 3.796800 28.830799 26.680300 2.214800 34.653301 23.790001 2.214800 34.653301 23.790001 2.214700 34.398399 24.554600 3.796800 28.802700 27.353300 3.796700 28.830799 26.680300 2.214800 28.841499 26.425100 0.000000 34.750000 23.500000 0.000000 34.750000 23.500000 0.000000 34.653301 23.790001 2.214800 28.830700 26.680201 2.214700 77.500000 64.000000 33.749901 70.623398 64.000000 33.074902 70.195801 59.573601 35.131401 77.500000 59.573601 35.848301 70.623398 64.000000 33.074902 64.253098 64.000000 31.134300 63.429501 59.573601 33.070099 63.429501 59.573601 33.070000 70.195801 59.573601 35.131401 70.623299 64.000000 33.074902 64.253098 64.000000 31.134300 58.515598 64.000000 28.054600 57.335201 59.573601 29.798901 57.335201 59.573601 29.798901 63.429501 59.573601 33.070099 64.252998 64.000000 31.134300 58.515598 64.000000 28.054600 53.537399 64.000000 23.962400 52.047600 59.573601 25.452299 52.047501 59.573601 25.452200 57.335201 59.573601 29.798901 58.515499 64.000000 28.054501 53.537399 64.000000 23.962400 49.445301 64.000000 18.984301 47.701000 59.573601 20.164700 47.701000 59.573601 20.164600 52.047600 59.573601 25.452299 53.537300 64.000000 23.962400 49.445301 64.000000 18.984301 46.365601 64.000000 13.246800 44.429798 59.573601 14.070400 44.429699 59.573601 14.070400 46.365601 64.000000 13.246800 44.424900 64.000000 6.876500 42.368500 59.573601 7.304100 42.368401 59.573601 7.304100 43.750000 64.000000 0.000000 41.651600 59.573601 0.000000 41.651501 59.573601 0.000000 69.781700 55.166901 37.123199 69.781700 55.166901 37.123100 77.500000 55.166901 37.880798 70.195801 59.573601 35.131401 63.429501 59.573601 33.070099 62.631699 55.166901 34.945000 62.631599 55.166901 34.944901 56.192001 55.166901 31.488400 56.192001 55.166901 31.488300 62.631699 55.166901 34.945000 63.429501 59.573601 33.070000 57.335201 59.573601 29.798901 52.047600 59.573601 25.452299 50.604500 55.166901 26.895399 50.604401 55.166901 26.895300 52.047600 59.573601 25.452299 47.701000 59.573601 20.164700 46.011501 55.166901 21.307899 46.011501 55.166901 21.307800 50.604500 55.166901 26.895399 52.047501 59.573601 25.452200 47.701000 59.573601 20.164700 44.429798 59.573601 14.070400 42.554901 55.166901 14.868200 42.554901 55.166901 14.868200 46.011501 55.166901 21.307899 47.701000 59.573601 20.164600 44.429798 59.573601 14.070400 42.368500 59.573601 7.304100 40.376701 55.166901 7.718200 40.376701 55.166901 7.718200 42.554901 55.166901 14.868200 44.429699 59.573601 14.070400 42.368500 59.573601 7.304100 41.651600 59.573601 0.000000 39.619099 55.166901 0.000000 39.618999 55.166901 0.000000 40.376701 55.166901 7.718200 42.368401 59.573601 7.304100 77.500000 55.166901 37.880798 69.781700 55.166901 37.123199 69.394501 50.799900 38.985802 69.394501 50.799900 38.985802 77.500000 50.799900 39.781399 77.500000 55.166901 37.880699 69.781700 55.166901 37.123199 62.631699 55.166901 34.945000 61.885700 50.799900 36.698399 61.885700 50.799900 36.698299 69.394501 50.799900 38.985802 69.781700 55.166901 37.123100 62.631699 55.166901 34.945000 56.192001 55.166901 31.488400 55.122898 50.799900 33.068298 55.122799 50.799900 33.068199 61.885700 50.799900 36.698399 62.631599 55.166901 34.944901 56.192001 55.166901 31.488400 50.604500 55.166901 26.895399 49.255100 50.799900 28.244801 49.255100 50.799900 28.244801 55.122898 50.799900 33.068298 56.192001 55.166901 31.488300 50.604500 55.166901 26.895399 46.011501 55.166901 21.307899 44.431599 50.799900 22.377001 44.431499 50.799900 22.377001 49.255100 50.799900 28.244801 50.604401 55.166901 26.895300 40.801498 50.799900 15.614200 40.801399 50.799900 15.614100 44.431599 50.799900 22.377001 46.011501 55.166901 21.307800 38.514099 50.799900 8.105400 38.514000 50.799900 8.105400 40.376701 55.166901 7.718200 39.619099 55.166901 0.000000 37.718498 50.799900 0.000000 37.718399 50.799900 0.000000 69.047501 46.492100 40.654598 69.047501 46.492001 40.654499 77.500000 46.492100 41.484299 77.500000 50.799900 39.781300 69.394501 50.799900 38.985802 61.885700 50.799900 36.698399 61.217300 46.492100 38.269299 61.217300 46.492001 38.269199 61.885700 50.799900 36.698399 55.122898 50.799900 33.068298 54.165001 46.492100 34.483799 54.165001 46.492001 34.483700 61.217300 46.492100 38.269299 61.885700 50.799900 36.698299 48.046001 46.492100 29.453899 48.046001 46.492001 29.453800 54.165001 46.492100 34.483799 55.122799 50.799900 33.068199 49.255100 50.799900 28.244801 44.431599 50.799900 22.377001 43.016102 46.492100 23.334900 43.016102 46.492001 23.334801 44.431599 50.799900 22.377001 40.801498 50.799900 15.614200 39.230598 46.492100 16.282600 39.230499 46.492001 16.282600 43.016102 46.492100 23.334900 44.431499 50.799900 22.377001 40.801498 50.799900 15.614200 38.514099 50.799900 8.105400 36.845299 46.492100 8.452400 36.845200 46.492001 8.452400 39.230598 46.492100 16.282600 40.801399 50.799900 15.614100 38.514099 50.799900 8.105400 37.718498 50.799900 0.000000 36.015598 46.492100 0.000000 36.015499 46.492001 0.000000 36.845299 46.492100 8.452400 38.514000 50.799900 8.105400 77.500000 46.492100 41.484299 69.047501 46.492100 40.654598 68.754303 42.263500 42.065102 68.754303 42.263500 42.065102 77.500000 42.263500 42.923500 77.500000 46.492001 41.484200 69.047501 46.492100 40.654598 61.217300 46.492100 38.269299 60.652401 42.263500 39.597000 60.652401 42.263500 39.597000 68.754303 42.263500 42.065102 69.047501 46.492001 40.654499 61.217300 46.492100 38.269299 54.165001 46.492100 34.483799 53.355400 42.263500 35.680199 53.355400 42.263500 35.680099 60.652401 42.263500 39.597000 61.217300 46.492001 38.269199 54.165001 46.492100 34.483799 48.046001 46.492100 29.453899 47.024200 42.263500 30.475700 47.024200 42.263500 30.475700 53.355400 42.263500 35.680199 54.165001 46.492001 34.483700 48.046001 46.492100 29.453899 43.016102 46.492100 23.334900 41.819698 42.263500 24.144501 41.819599 42.263500 24.144501 47.024200 42.263500 30.475700 48.046001 46.492001 29.453800 43.016102 46.492100 23.334900 39.230598 46.492100 16.282600 37.902901 42.263500 16.847500 37.902901 42.263500 16.847401 41.819698 42.263500 24.144501 43.016102 46.492001 23.334801 39.230598 46.492100 16.282600 36.845299 46.492100 8.452400 35.434799 42.263500 8.745600 35.434700 42.263500 8.745500 37.902901 42.263500 16.847500 39.230499 46.492001 16.282600 36.845299 46.492100 8.452400 36.015598 46.492100 0.000000 34.576401 42.263500 0.000000 34.576401 42.263500 0.000000 35.434799 42.263500 8.745600 36.845200 46.492001 8.452400 68.528198 38.133701 43.152500 68.528099 38.133701 43.152500 77.500000 38.133701 44.033199 77.500000 42.263500 42.923500 60.216900 38.133701 40.620602 60.216801 38.133701 40.620602 53.355400 42.263500 35.680199 52.731300 38.133701 36.602600 46.236401 38.133701 31.263500 46.236401 38.133701 31.263500 53.355400 42.263500 35.680099 41.819698 42.263500 24.144501 40.897301 38.133701 24.768600 41.819698 42.263500 24.144501 37.902901 42.263500 16.847500 36.879299 38.133701 17.283001 36.879200 38.133701 17.283001 40.897301 38.133701 24.768600 41.819599 42.263500 24.144501 37.902901 42.263500 16.847500 35.434799 42.263500 8.745600 34.347401 38.133701 8.971700 34.347401 38.133701 8.971600 36.879299 38.133701 17.283001 37.902901 42.263500 16.847401 33.466702 38.133701 0.000000 33.466702 38.133701 0.000000 34.347401 38.133701 8.971700 35.434700 42.263500 8.745500 77.500000 38.133701 44.033199 68.528198 38.133701 43.152500 68.382698 34.122601 43.852299 68.382599 34.122601 43.852200 77.500000 34.122601 44.747299 77.500000 38.133701 44.033100 68.528198 38.133701 43.152500 60.216900 38.133701 40.620602 59.936600 34.122601 41.279301 59.936501 34.122601 41.279301 68.382698 34.122601 43.852299 68.528099 38.133701 43.152500 52.329601 34.122601 37.196201 52.329601 34.122601 37.196201 59.936600 34.122601 41.279301 60.216801 38.133701 40.620602 45.729401 34.122601 31.770500 40.303699 34.122601 25.170300 40.303600 34.122601 25.170200 40.897301 38.133701 24.768600 36.879299 38.133701 17.283001 36.220600 34.122601 17.563299 36.220600 34.122601 17.563200 36.879299 38.133701 17.283001 34.347401 38.133701 8.971700 33.647598 34.122601 9.117200 33.647499 34.122601 9.117100 36.220600 34.122601 17.563299 36.879200 38.133701 17.283001 32.752602 34.122601 0.000000 32.752602 34.122601 0.000000 33.647598 34.122601 9.117200 34.347401 38.133701 8.971600 77.500000 34.122601 44.747299 68.382698 34.122601 43.852299 68.331200 30.250000 44.099899 68.331100 30.250000 44.099800 77.500000 30.250000 45.000000 77.500000 34.122601 44.747200 68.382698 34.122601 43.852299 59.936600 34.122601 41.279301 59.837502 30.250000 41.512501 59.837502 30.250000 41.512501 68.331200 30.250000 44.099899 68.382599 34.122601 43.852200 52.187500 30.250000 37.406200 52.187500 30.250000 37.406200 59.837502 30.250000 41.512501 59.936501 34.122601 41.279301 45.549999 30.250000 31.949900 45.549900 30.250000 31.949800 40.303699 34.122601 25.170300 40.093700 30.250000 25.312500 40.303699 34.122601 25.170300 36.220600 34.122601 17.563299 35.987400 30.250000 17.662500 35.987400 30.250000 17.662500 40.093700 30.250000 25.312500 40.303600 34.122601 25.170200 36.220600 34.122601 17.563299 33.647598 34.122601 9.117200 33.400002 30.250000 9.168700 33.400002 30.250000 9.168700 35.987400 30.250000 17.662500 36.220600 34.122601 17.563200 32.500000 30.250000 0.000000 32.500000 30.250000 0.000000 33.400002 30.250000 9.168700 33.647499 34.122601 9.117100 67.057404 10.075700 2.171000 66.844299 10.075800 0.000000 77.500000 10.000000 0.000000 67.669998 10.075700 4.182300 67.057404 10.075800 2.171000 77.500000 10.000000 0.000000 68.642403 10.075700 5.993700 67.670097 10.075800 4.182300 77.500000 10.000000 0.000000 69.934502 10.075700 7.565400 68.642403 10.075800 5.993700 77.500000 10.000000 0.000000 71.506203 10.075700 8.857500 69.934502 10.075800 7.565500 77.500000 10.000000 0.000000 73.317497 10.075700 9.829700 71.506203 10.075800 8.857500 77.500000 10.000000 0.000000 75.328903 10.075700 10.442500 73.317596 10.075800 9.829800 77.500000 10.000000 0.000000 77.500000 10.075700 10.655500 75.328903 10.075800 10.442500 77.500000 10.000000 0.000000 66.844299 10.075800 0.000000 67.057404 10.075800 2.171000 59.076099 10.290000 3.830400 59.076000 10.289900 3.830300 58.700100 10.290000 0.000000 66.844200 10.075700 0.000000 67.057404 10.075800 2.171000 67.670097 10.075800 4.182300 60.157101 10.290000 7.378900 60.157101 10.289900 7.378900 59.076099 10.290000 3.830400 67.057404 10.075700 2.171000 67.670097 10.075800 4.182300 68.642403 10.075800 5.993700 61.872601 10.290000 10.574800 61.872601 10.289900 10.574700 60.157101 10.290000 7.378900 67.669998 10.075700 4.182300 68.642403 10.075800 5.993700 69.934502 10.075800 7.565500 64.152100 10.290000 13.347800 64.152000 10.289900 13.347800 61.872601 10.290000 10.574800 68.642403 10.075700 5.993700 69.934502 10.075800 7.565500 71.506203 10.075800 8.857500 66.925102 10.290000 15.627300 66.925102 10.289900 15.627300 64.152100 10.290000 13.347800 69.934502 10.075700 7.565400 71.506203 10.075800 8.857500 73.317596 10.075800 9.829800 70.121002 10.290000 17.342800 70.121002 10.289900 17.342800 66.925102 10.290000 15.627300 71.506203 10.075700 8.857500 73.317596 10.075800 9.829800 75.328903 10.075800 10.442500 73.669502 10.290000 18.423800 73.669502 10.289900 18.423700 70.121002 10.290000 17.342800 73.317497 10.075700 9.829700 75.328903 10.075800 10.442500 77.500000 10.075800 10.655600 77.500000 10.290000 18.799801 77.500000 10.289900 18.799801 73.669502 10.290000 18.423800 75.328903 10.075700 10.442500 58.700100 10.290000 0.000000 59.076099 10.290000 3.830400 53.226601 10.622900 5.046600 53.226601 10.622900 5.046500 52.731300 10.622900 0.000000 58.700001 10.289900 0.000000 59.076099 10.290000 3.830400 60.157101 10.290000 7.378900 54.650799 10.622900 9.721700 54.650700 10.622900 9.721600 53.226601 10.622900 5.046600 59.076000 10.289900 3.830300 60.157101 10.290000 7.378900 61.872601 10.290000 10.574800 56.910999 10.622900 13.932300 56.910900 10.622900 13.932200 54.650799 10.622900 9.721700 60.157101 10.289900 7.378900 61.872601 10.290000 10.574800 64.152100 10.290000 13.347800 59.914200 10.622900 17.585699 59.914101 10.622900 17.585600 56.910999 10.622900 13.932300 61.872601 10.289900 10.574700 64.152100 10.290000 13.347800 66.925102 10.290000 15.627300 63.567600 10.622900 20.588900 63.567600 10.622900 20.588800 59.914200 10.622900 17.585699 64.152000 10.289900 13.347800 66.925102 10.290000 15.627300 70.121002 10.290000 17.342800 67.778198 10.622900 22.849100 67.778099 10.622900 22.849100 63.567600 10.622900 20.588900 66.925102 10.289900 15.627300 70.121002 10.290000 17.342800 73.669502 10.290000 18.423800 72.453300 10.622900 24.273300 72.453300 10.622900 24.273300 67.778198 10.622900 22.849100 70.121002 10.289900 17.342800 73.669502 10.290000 18.423800 77.500000 10.290000 18.799801 77.500000 10.622900 24.768600 77.500000 10.622900 24.768600 72.453300 10.622900 24.273300 73.669502 10.289900 18.423700 52.731300 10.622900 0.000000 53.226601 10.622900 5.046600 49.179501 11.054600 5.888000 49.179501 11.054500 5.888000 48.601501 11.054600 0.000000 52.731300 10.622900 0.000000 53.226601 10.622900 5.046600 54.650799 10.622900 9.721700 50.841099 11.054600 11.342600 50.841000 11.054500 11.342500 49.179501 11.054600 5.888000 53.226601 10.622900 5.046500 54.650799 10.622900 9.721700 56.910999 10.622900 13.932300 53.478100 11.054600 16.255301 53.478001 11.054500 16.255301 50.841099 11.054600 11.342600 54.650700 10.622900 9.721600 56.910999 10.622900 13.932300 59.914200 10.622900 17.585699 56.982101 11.054600 20.517799 56.982101 11.054500 20.517700 53.478100 11.054600 16.255301 56.910900 10.622900 13.932200 59.914200 10.622900 17.585699 63.567600 10.622900 20.588900 61.244598 11.054600 24.021799 61.244499 11.054500 24.021700 56.982101 11.054600 20.517799 59.914101 10.622900 17.585600 63.567600 10.622900 20.588900 67.778198 10.622900 22.849100 66.157303 11.054600 26.658800 66.157303 11.054500 26.658800 61.244598 11.054600 24.021799 63.567600 10.622900 20.588800 71.611900 11.054600 28.320400 71.611900 11.054500 28.320400 66.157303 11.054600 26.658800 67.778099 10.622900 22.849100 77.500000 11.054600 28.898399 77.500000 11.054500 28.898300 48.601501 11.054600 0.000000 49.179501 11.054600 5.888000 46.605202 11.565500 6.423200 46.605202 11.565500 6.423200 45.974701 11.565500 0.000000 48.601501 11.054500 0.000000 49.179501 11.054600 5.888000 50.841099 11.054600 11.342600 48.417900 11.565500 12.373600 48.417900 11.565500 12.373600 46.605202 11.565500 6.423200 49.179501 11.054500 5.888000 50.841099 11.054600 11.342600 53.478100 11.054600 16.255301 51.294601 11.565500 17.732901 51.294601 11.565500 17.732901 48.417900 11.565500 12.373600 50.841000 11.054500 11.342500 53.478100 11.054600 16.255301 56.982101 11.054600 20.517799 55.117001 11.565500 22.382900 55.117001 11.565500 22.382900 51.294601 11.565500 17.732901 53.478001 11.054500 16.255301 56.982101 11.054600 20.517799 61.244598 11.054600 24.021799 59.766998 11.565500 26.205299 59.766899 11.565500 26.205200 55.117001 11.565500 22.382900 56.982101 11.054500 20.517700 61.244598 11.054600 24.021799 66.157303 11.054600 26.658800 65.126297 11.565500 29.082001 65.126198 11.565500 29.082001 59.766998 11.565500 26.205299 61.244499 11.054500 24.021700 66.157303 11.054600 26.658800 71.611900 11.054600 28.320400 71.076698 11.565500 30.894699 71.076599 11.565500 30.894600 65.126297 11.565500 29.082001 66.157303 11.054500 26.658800 71.611900 11.054600 28.320400 77.500000 11.054600 28.898399 77.500000 11.565500 31.525200 77.500000 11.565500 31.525101 71.076698 11.565500 30.894699 71.611900 11.054500 28.320400 45.174301 12.135700 6.720700 45.174301 12.135700 6.720600 44.514599 12.135700 0.000000 45.974701 11.565500 0.000000 47.070999 12.135700 12.946700 47.070900 12.135700 12.946700 50.080898 12.135700 18.554199 50.080799 12.135700 18.554100 54.080299 12.135700 23.419500 54.080200 12.135700 23.419500 59.766998 11.565500 26.205299 58.945702 12.135700 27.419001 59.766998 11.565500 26.205299 65.126297 11.565500 29.082001 64.553200 12.135700 30.428900 64.553101 12.135700 30.428801 58.945702 12.135700 27.419001 59.766899 11.565500 26.205200 65.126297 11.565500 29.082001 71.076698 11.565500 30.894699 70.779198 12.135700 32.325600 70.779099 12.135700 32.325500 64.553200 12.135700 30.428900 65.126198 11.565500 29.082001 71.076698 11.565500 30.894699 77.500000 11.565500 31.525200 77.500000 12.135700 32.985298 77.500000 12.135700 32.985199 70.779198 12.135700 32.325600 71.076599 11.565500 30.894600 44.514599 12.135700 0.000000 45.174301 12.135700 6.720700 44.557400 12.745400 6.849000 44.557301 12.745400 6.848900 43.885101 12.745400 0.000000 44.514500 12.135700 0.000000 45.174301 12.135700 6.720700 47.070999 12.135700 12.946700 46.490200 12.745400 13.193800 46.490200 12.745400 13.193700 44.557400 12.745400 6.849000 45.174301 12.135700 6.720600 47.070999 12.135700 12.946700 50.080898 12.135700 18.554199 49.557598 12.745400 18.908300 49.557499 12.745400 18.908300 46.490200 12.745400 13.193800 47.070900 12.135700 12.946700 50.080898 12.135700 18.554199 54.080299 12.135700 23.419500 53.633400 12.745400 23.866501 53.633301 12.745400 23.866501 49.557598 12.745400 18.908300 50.080799 12.135700 18.554100 58.591599 12.745400 27.942301 58.591499 12.745400 27.942301 53.633400 12.745400 23.866501 54.080200 12.135700 23.419500 58.945702 12.135700 27.419001 64.553200 12.135700 30.428900 64.306099 12.745400 31.009701 64.306000 12.745400 31.009701 64.553200 12.135700 30.428900 70.779198 12.135700 32.325600 70.650902 12.745400 32.942501 70.650902 12.745400 32.942501 64.306099 12.745400 31.009701 64.553101 12.135700 30.428801 70.779198 12.135700 32.325600 77.500000 12.135700 32.985298 77.500000 12.745400 33.614799 77.500000 12.745400 33.614700 70.650902 12.745400 32.942501 70.779099 12.135700 32.325500 43.885101 12.745400 0.000000 44.557400 12.745400 6.849000 44.424900 13.375000 6.876500 43.750000 13.375000 0.000000 44.557400 12.745400 6.849000 46.490200 12.745400 13.193800 46.365601 13.375000 13.246800 46.365601 13.375000 13.246800 44.424900 13.375000 6.876500 44.557301 12.745400 6.848900 46.490200 12.745400 13.193800 49.557598 12.745400 18.908300 49.445301 13.375000 18.984301 49.445301 13.375000 18.984301 46.365601 13.375000 13.246800 46.490200 12.745400 13.193700 49.557598 12.745400 18.908300 53.633400 12.745400 23.866501 53.537399 13.375000 23.962400 53.537300 13.375000 23.962400 49.445301 13.375000 18.984301 49.557499 12.745400 18.908300 53.633400 12.745400 23.866501 58.591599 12.745400 27.942301 58.515598 13.375000 28.054600 58.515499 13.375000 28.054501 53.537399 13.375000 23.962400 53.633301 12.745400 23.866501 58.591599 12.745400 27.942301 64.306099 12.745400 31.009701 64.253098 13.375000 31.134300 64.252998 13.375000 31.134300 58.515598 13.375000 28.054600 58.591499 12.745400 27.942301 70.623398 13.375000 33.075001 70.623299 13.375000 33.075001 64.253098 13.375000 31.134300 64.306000 12.745400 31.009701 77.500000 12.745400 33.614799 77.500000 13.375000 33.750000 46.987400 64.968903 6.343800 46.630001 64.000000 6.418100 46.000000 64.000000 0.000000 46.364700 64.968903 0.000000 48.777699 64.969002 12.220500 48.441200 64.000000 12.363700 46.630001 64.000000 6.418100 46.630001 64.000000 6.418000 46.987400 64.968903 6.343800 48.777599 64.969002 12.220400 51.618801 64.969002 17.513500 51.315601 64.000000 17.718700 48.441200 64.000000 12.363700 48.441200 64.000000 12.363600 48.777699 64.969002 12.220500 51.618801 64.969002 17.513500 55.393902 64.968903 22.106001 55.134998 64.000000 22.364901 59.986401 64.968903 25.881100 59.781200 64.000000 26.184299 55.134998 64.000000 22.364901 55.134899 64.000000 22.364901 55.393902 64.968903 22.106001 59.986401 64.968903 25.881001 65.279404 64.968903 28.722200 65.136200 64.000000 29.058701 59.781200 64.000000 26.184299 59.781200 64.000000 26.184200 59.986401 64.968903 25.881100 65.279404 64.968903 28.722200 71.156097 64.968903 30.512501 71.081802 64.000000 30.869900 65.136200 64.000000 29.058701 65.136101 64.000000 29.058701 65.279404 64.968903 28.722200 71.155998 64.968903 30.512501 77.500000 64.968903 31.135201 77.500000 64.000000 31.499901 71.081802 64.000000 30.869900 71.081802 64.000000 30.869801 71.156097 64.968903 30.512501 77.500000 64.968903 31.135201 47.060600 65.661102 6.328500 46.364700 64.968903 0.000000 46.439400 65.661102 0.000000 47.060600 65.661102 6.328400 48.846600 65.661102 12.191200 48.777699 64.969002 12.220500 46.987400 64.968903 6.343800 48.846500 65.661102 12.191200 51.680901 65.661102 17.471500 48.777599 64.969002 12.220400 48.846600 65.661102 12.191200 51.680901 65.661102 17.471500 55.446999 65.661102 22.052900 55.446899 65.661102 22.052900 60.028400 65.661102 25.819000 59.986401 64.968903 25.881100 65.308701 65.661102 28.653299 59.986401 64.968903 25.881001 60.028400 65.661102 25.819000 65.308701 65.661102 28.653200 71.171402 65.661102 30.439301 71.156097 64.968903 30.512501 77.500000 65.661102 31.060499 71.155998 64.968903 30.512501 71.171402 65.661102 30.439301 77.500000 65.661102 31.060400 46.901299 66.076401 6.361700 47.060600 65.661102 6.328500 46.439400 65.661102 0.000000 46.439301 65.661102 0.000000 46.276798 66.076401 0.000000 46.901199 66.076401 6.361700 48.696602 66.076401 12.255000 48.846600 65.661102 12.191200 47.060600 65.661102 6.328500 47.060600 65.661102 6.328400 46.901299 66.076401 6.361700 48.696602 66.076401 12.255000 51.545700 66.076401 17.563000 48.846500 65.661102 12.191200 48.696602 66.076401 12.255000 51.545700 66.076401 17.562901 55.331501 66.076401 22.168400 55.446999 65.661102 22.052900 51.680901 65.661102 17.471500 55.331501 66.076401 22.168301 59.936901 66.076401 25.954201 55.446899 65.661102 22.052900 55.331501 66.076401 22.168400 59.936901 66.076401 25.954201 65.244904 66.076401 28.803301 65.308701 65.661102 28.653299 71.138199 66.076401 30.598600 65.308701 65.661102 28.653200 65.244904 66.076401 28.803301 71.138100 66.076401 30.598600 77.500000 66.076401 31.223101 77.500000 65.661102 31.060499 46.561001 66.214798 6.432400 46.901299 66.076401 6.361700 46.276798 66.076401 0.000000 46.276699 66.076401 0.000000 45.929600 66.214798 0.000000 46.561001 66.214699 6.432400 48.376301 66.214798 12.391300 46.901199 66.076401 6.361700 46.561001 66.214798 6.432400 48.376301 66.214699 12.391300 51.257099 66.214798 17.758200 51.545700 66.076401 17.563000 48.696602 66.076401 12.255000 51.257000 66.214699 17.758101 55.084999 66.214798 22.414900 55.331501 66.076401 22.168400 51.545700 66.076401 17.563000 51.545700 66.076401 17.562901 51.257099 66.214798 17.758200 55.084900 66.214699 22.414801 59.741600 66.214798 26.242800 55.331501 66.076401 22.168301 55.084999 66.214798 22.414900 59.741600 66.214699 26.242701 65.108597 66.214798 29.123600 65.108498 66.214699 29.123600 71.067497 66.214798 30.938900 71.138199 66.076401 30.598600 65.244904 66.076401 28.803301 71.067398 66.214699 30.938801 77.500000 66.214798 31.570299 71.138100 66.076401 30.598600 71.067497 66.214798 30.938900 77.500000 66.214699 31.570200 46.091599 66.076401 6.530000 46.561001 66.214798 6.432400 45.929600 66.214798 0.000000 45.929501 66.214699 0.000000 45.450600 66.076401 0.000000 46.091499 66.076401 6.530000 47.934502 66.076401 12.579300 48.376301 66.214798 12.391300 46.561001 66.214798 6.432400 46.561001 66.214699 6.432400 46.091599 66.076401 6.530000 47.934502 66.076401 12.579200 50.859001 66.076401 18.027700 51.257099 66.214798 17.758200 48.376301 66.214798 12.391300 48.376301 66.214699 12.391300 47.934502 66.076401 12.579300 50.859001 66.076401 18.027700 54.744900 66.076401 22.754999 55.084999 66.214798 22.414900 51.257099 66.214798 17.758200 51.257000 66.214699 17.758101 50.859001 66.076401 18.027700 54.744801 66.076401 22.754900 59.472198 66.076401 26.640900 59.741600 66.214798 26.242800 55.084999 66.214798 22.414900 55.084900 66.214699 22.414801 54.744900 66.076401 22.754999 59.472099 66.076401 26.640800 64.920601 66.076401 29.565399 65.108597 66.214798 29.123600 59.741600 66.214798 26.242800 59.741600 66.214699 26.242701 59.472198 66.076401 26.640900 64.920601 66.076401 29.565300 70.969902 66.076401 31.408300 71.067497 66.214798 30.938900 65.108597 66.214798 29.123600 65.108498 66.214699 29.123600 64.920601 66.076401 29.565399 70.969902 66.076401 31.408300 77.500000 66.076401 32.049301 77.500000 66.214798 31.570299 71.067497 66.214798 30.938900 71.067398 66.214699 30.938801 70.969902 66.076401 31.408300 77.500000 66.076401 32.049301 45.544701 65.661102 6.643700 46.091599 66.076401 6.530000 45.450600 66.076401 0.000000 45.450500 66.076401 0.000000 44.892502 65.661102 0.000000 45.544701 65.661102 6.643700 47.419601 65.661102 12.798400 47.934502 66.076401 12.579300 46.091599 66.076401 6.530000 46.091499 66.076401 6.530000 45.544701 65.661102 6.643700 47.419601 65.661102 12.798300 50.395000 65.661102 18.341600 47.934502 66.076401 12.579200 47.419601 65.661102 12.798400 50.395000 65.661102 18.341600 54.348701 65.661102 23.151199 54.744900 66.076401 22.754999 50.859001 66.076401 18.027700 54.348701 65.661102 23.151100 59.158298 65.661102 27.104900 59.472198 66.076401 26.640900 54.744900 66.076401 22.754999 54.744801 66.076401 22.754900 54.348701 65.661102 23.151199 59.158199 65.661102 27.104900 64.701500 65.661102 30.080299 64.920601 66.076401 29.565399 59.472198 66.076401 26.640900 59.472099 66.076401 26.640800 59.158298 65.661102 27.104900 64.701401 65.661102 30.080200 70.856201 65.661102 31.955200 64.920601 66.076401 29.565300 64.701500 65.661102 30.080299 70.856201 65.661102 31.955200 77.500000 65.661102 32.607399 77.500000 65.661102 32.607300 44.971901 64.968903 6.762800 44.308102 64.968903 0.000000 46.880402 64.968903 13.027800 47.419601 65.661102 12.798400 45.544701 65.661102 6.643700 46.880402 64.968903 13.027700 49.909199 64.968903 18.670401 47.419601 65.661102 12.798300 46.880402 64.968903 13.027800 49.909100 64.968903 18.670401 53.933701 64.968903 23.566200 54.348701 65.661102 23.151199 58.829498 64.968903 27.590700 59.158298 65.661102 27.104900 54.348701 65.661102 23.151199 54.348701 65.661102 23.151100 53.933701 64.968903 23.566200 58.829399 64.968903 27.590700 64.472099 64.968903 30.619499 64.701500 65.661102 30.080299 59.158298 65.661102 27.104900 59.158199 65.661102 27.104900 58.829498 64.968903 27.590700 64.472000 64.968903 30.619400 70.737099 64.968903 32.528000 64.701401 65.661102 30.080200 64.472099 64.968903 30.619499 70.737000 64.968903 32.527901 77.500000 64.968903 33.191799 77.500000 65.661102 32.607399 70.856201 65.661102 31.955200 77.500000 64.968903 33.191700 46.880402 64.968903 13.027800 49.445301 64.000000 18.984301 49.909199 64.968903 18.670401 46.880402 64.968903 13.027800 46.880402 64.968903 13.027700 53.537399 64.000000 23.962400 49.909100 64.968903 18.670401 49.445301 64.000000 18.984301 53.537300 64.000000 23.962400 58.515598 64.000000 28.054600 58.829498 64.968903 27.590700 53.933701 64.968903 23.566200 58.515499 64.000000 28.054501 64.253098 64.000000 31.134300 64.472099 64.968903 30.619499 58.829498 64.968903 27.590700 58.829399 64.968903 27.590700 58.515598 64.000000 28.054600 64.252998 64.000000 31.134300 70.623398 64.000000 33.074902 70.737099 64.968903 32.528000 64.472099 64.968903 30.619499 64.472000 64.968903 30.619400 64.253098 64.000000 31.134300 70.623299 64.000000 33.074902 77.500000 64.000000 33.749901 77.500000 64.968903 33.191799 70.737099 64.968903 32.528000 70.737000 64.968903 32.527901 70.781601 69.629303 1.396700 73.089897 70.750000 0.916800 73.000000 70.750000 0.000000 70.644501 69.629303 0.000000 71.175797 69.629303 2.690700 73.348701 70.750000 1.766200 73.089897 70.750000 0.916800 73.089798 70.750000 0.916800 70.781601 69.629303 1.396700 71.175697 69.629303 2.690700 71.801300 69.629402 3.856100 73.759300 70.750000 2.531200 73.348701 70.750000 1.766200 73.348701 70.750000 1.766100 71.175797 69.629303 2.690700 71.801300 69.629402 3.856100 72.632599 69.629303 4.867300 74.305000 70.750000 3.194900 73.759300 70.750000 2.531200 73.759300 70.750000 2.531100 71.801300 69.629402 3.856100 72.632500 69.629303 4.867300 73.643700 69.629303 5.698600 74.968697 70.750000 3.740600 74.305000 70.750000 3.194900 73.643600 69.629303 5.698500 74.809196 69.629303 6.324100 75.733704 70.750000 4.151200 74.968697 70.750000 3.740600 74.968597 70.750000 3.740600 73.643700 69.629303 5.698600 74.809097 69.629303 6.324100 76.103203 69.629303 6.718300 76.583099 70.750000 4.409900 75.733704 70.750000 4.151200 75.733704 70.750000 4.151100 74.809196 69.629303 6.324100 76.103203 69.629303 6.718200 77.500000 69.629303 6.855400 77.500000 70.750000 4.499900 76.583099 70.750000 4.409900 76.583000 70.750000 4.409900 76.103203 69.629303 6.718300 67.439598 68.746002 2.091600 70.644501 69.629303 0.000000 67.234299 68.746002 0.000000 67.439499 68.746002 2.091500 68.029900 68.746002 4.029200 71.175797 69.629303 2.690700 70.781601 69.629303 1.396700 68.029800 68.746002 4.029200 68.966698 68.746002 5.774400 71.175697 69.629303 2.690700 68.029900 68.746002 4.029200 68.966599 68.746002 5.774400 70.211403 68.746002 7.288500 72.632599 69.629303 4.867300 71.801300 69.629402 3.856100 70.211403 68.746002 7.288400 71.725502 68.746002 8.533200 73.643700 69.629303 5.698600 72.632599 69.629303 4.867300 72.632500 69.629303 4.867300 70.211403 68.746002 7.288500 71.725502 68.746002 8.533200 73.470703 68.746002 9.470000 74.809196 69.629303 6.324100 73.643700 69.629303 5.698600 73.643600 69.629303 5.698500 71.725502 68.746002 8.533200 73.470703 68.746002 9.470000 75.408302 68.746002 10.060300 76.103203 69.629303 6.718300 74.809196 69.629303 6.324100 74.809097 69.629303 6.324100 73.470703 68.746002 9.470000 75.408302 68.746002 10.060200 77.500000 68.746002 10.265600 76.103203 69.629303 6.718200 75.408302 68.746002 10.060300 77.500000 68.746002 10.265600 63.477501 68.020897 2.915300 67.439598 68.746002 2.091600 67.234299 68.746002 0.000000 67.234200 68.746002 0.000000 63.191399 68.020897 0.000000 63.477501 68.020798 2.915200 64.300301 68.020897 5.616100 68.029900 68.746002 4.029200 67.439598 68.746002 2.091600 67.439499 68.746002 2.091500 63.477501 68.020897 2.915300 64.300301 68.020798 5.616000 65.605904 68.020897 8.048500 68.966698 68.746002 5.774400 68.029900 68.746002 4.029200 68.029800 68.746002 4.029200 64.300301 68.020897 5.616100 65.605904 68.020798 8.048500 67.340797 68.020897 10.159000 70.211403 68.746002 7.288500 68.966698 68.746002 5.774400 68.966599 68.746002 5.774400 65.605904 68.020897 8.048500 67.340698 68.020798 10.159000 69.451401 68.020897 11.894000 70.211403 68.746002 7.288400 67.340797 68.020897 10.159000 69.451401 68.020798 11.894000 71.883797 68.020897 13.199600 71.883698 68.020798 13.199600 74.584602 68.020897 14.022400 75.408302 68.746002 10.060300 73.470703 68.746002 9.470000 74.584602 68.020798 14.022300 77.500000 68.020897 14.308500 75.408302 68.746002 10.060200 74.584602 68.020897 14.022400 77.500000 68.020798 14.308500 59.308701 67.375000 3.782100 63.477501 68.020897 2.915300 63.191399 68.020897 0.000000 63.191299 68.020798 0.000000 58.937500 67.375000 0.000000 59.308701 67.375000 3.782000 60.375999 67.375000 7.285700 64.300301 68.020897 5.616100 63.477501 68.020897 2.915300 63.477501 68.020798 2.915200 59.308701 67.375000 3.782100 60.375900 67.375000 7.285600 62.069901 67.375000 10.441400 65.605904 68.020897 8.048500 64.300301 68.020897 5.616100 64.300301 68.020798 5.616000 60.375999 67.375000 7.285700 62.069901 67.375000 10.441300 64.320602 67.375000 13.179300 67.340797 68.020897 10.159000 65.605904 68.020897 8.048500 65.605904 68.020798 8.048500 62.069901 67.375000 10.441400 64.320602 67.375000 13.179300 67.058502 67.375000 15.430000 69.451401 68.020897 11.894000 67.340797 68.020897 10.159000 67.340698 68.020798 10.159000 64.320602 67.375000 13.179300 67.058502 67.375000 15.430000 70.214203 67.375000 17.123899 71.883797 68.020897 13.199600 69.451401 68.020897 11.894000 69.451401 68.020798 11.894000 67.058502 67.375000 15.430000 70.214203 67.375000 17.123800 73.717796 67.375000 18.191200 74.584602 68.020897 14.022400 71.883797 68.020897 13.199600 71.883698 68.020798 13.199600 70.214203 67.375000 17.123899 73.717697 67.375000 18.191200 77.500000 67.375000 18.562401 77.500000 68.020897 14.308500 74.584602 68.020897 14.022400 74.584602 68.020798 14.022300 73.717796 67.375000 18.191200 77.500000 67.375000 18.562401 55.346600 66.728996 4.605800 59.308701 67.375000 3.782100 58.937500 67.375000 0.000000 58.937500 67.375000 0.000000 54.894501 66.728996 0.000000 55.346500 66.728897 4.605800 56.646400 66.728996 8.872600 60.375999 67.375000 7.285700 59.308701 67.375000 3.782100 59.308701 67.375000 3.782000 55.346600 66.728996 4.605800 56.646400 66.728897 8.872500 58.709202 66.728996 12.715500 62.069901 67.375000 10.441400 60.375999 67.375000 7.285700 60.375900 67.375000 7.285600 56.646400 66.728996 8.872600 58.709202 66.728897 12.715400 61.450100 66.728996 16.049801 62.069901 67.375000 10.441300 58.709202 66.728996 12.715500 61.450001 66.728897 16.049801 64.784401 66.728996 18.790701 64.784401 66.728897 18.790701 68.627296 66.728996 20.853500 70.214203 67.375000 17.123899 67.058502 67.375000 15.430000 68.627197 66.728897 20.853500 72.894096 66.728996 22.153299 73.717796 67.375000 18.191200 70.214203 67.375000 17.123899 70.214203 67.375000 17.123800 68.627296 66.728996 20.853500 72.893997 66.728897 22.153200 77.500000 66.728996 22.605400 73.717697 67.375000 18.191200 72.894096 66.728996 22.153299 77.500000 66.728897 22.605400 52.004601 66.003899 5.300600 55.346600 66.728996 4.605800 54.894501 66.728996 0.000000 54.894501 66.728897 0.000000 51.484299 66.003899 0.000000 52.004601 66.003799 5.300600 53.500500 66.003899 10.211100 56.646400 66.728996 8.872600 55.346600 66.728996 4.605800 55.346500 66.728897 4.605800 52.004601 66.003899 5.300600 53.500401 66.003799 10.211000 55.874500 66.003899 14.633700 58.709202 66.728996 12.715500 56.646400 66.728996 8.872600 56.646400 66.728897 8.872500 53.500500 66.003899 10.211100 55.874500 66.003799 14.633700 59.028900 66.003899 18.471001 61.450100 66.728996 16.049801 58.709202 66.728996 12.715500 58.709202 66.728897 12.715400 55.874500 66.003899 14.633700 59.028900 66.003799 18.471001 62.866199 66.003899 21.625401 64.784401 66.728996 18.790701 61.450100 66.728996 16.049801 61.450001 66.728897 16.049801 59.028900 66.003899 18.471001 62.866100 66.003799 21.625401 67.288803 66.003899 23.999399 68.627296 66.728996 20.853500 64.784401 66.728996 18.790701 64.784401 66.728897 18.790701 62.866199 66.003899 21.625401 67.288803 66.003799 23.999300 72.199303 66.003899 25.495300 72.894096 66.728996 22.153299 68.627296 66.728996 20.853500 68.627197 66.728897 20.853500 67.288803 66.003899 23.999399 72.199303 66.003799 25.495300 77.500000 66.003899 26.015600 77.500000 66.728996 22.605400 72.894096 66.728996 22.153299 72.893997 66.728897 22.153200 72.199303 66.003899 25.495300 77.500000 66.003799 26.015600 49.696301 65.120598 5.780600 52.004601 66.003899 5.300600 51.484299 66.003899 0.000000 51.484200 66.003799 0.000000 49.128899 65.120598 0.000000 49.696301 65.120499 5.780600 51.327599 65.120598 11.135600 53.500500 66.003899 10.211100 52.004601 66.003899 5.300600 52.004601 66.003799 5.300600 49.696301 65.120598 5.780600 51.327499 65.120499 11.135600 53.916500 65.120598 15.958700 55.874500 66.003899 14.633700 53.500500 66.003899 10.211100 53.500401 66.003799 10.211000 51.327599 65.120598 11.135600 53.916500 65.120499 15.958700 57.356499 65.120598 20.143400 59.028900 66.003899 18.471001 55.874500 66.003899 14.633700 55.874500 66.003799 14.633700 53.916500 65.120598 15.958700 57.356400 65.120499 20.143400 61.541199 65.120598 23.583401 62.866199 66.003899 21.625401 59.028900 66.003899 18.471001 59.028900 66.003799 18.471001 57.356499 65.120598 20.143400 61.541100 65.120499 23.583401 66.364304 65.120598 26.172300 67.288803 66.003899 23.999399 62.866199 66.003899 21.625401 62.866100 66.003799 21.625401 61.541199 65.120598 23.583401 66.364304 65.120499 26.172300 71.719299 65.120598 27.803600 72.199303 66.003899 25.495300 67.288803 66.003899 23.999399 67.288803 66.003799 23.999300 66.364304 65.120598 26.172300 71.719200 65.120499 27.803600 77.500000 65.120598 28.371000 77.500000 66.003899 26.015600 72.199303 66.003899 25.495300 72.199303 66.003799 25.495300 71.719299 65.120598 27.803600 77.500000 65.120499 28.371000 48.834900 64.000000 5.959600 49.696301 65.120598 5.780600 49.128899 65.120598 0.000000 49.128799 65.120499 0.000000 48.250000 64.000000 0.000000 48.834801 64.000000 5.959500 50.516800 64.000000 11.480600 51.327599 65.120598 11.135600 49.696301 65.120598 5.780600 49.696301 65.120499 5.780600 48.834900 64.000000 5.959600 50.516701 64.000000 11.480600 53.185902 64.000000 16.453100 53.916500 65.120598 15.958700 51.327599 65.120598 11.135600 51.327499 65.120499 11.135600 50.516800 64.000000 11.480600 53.185902 64.000000 16.453100 56.732399 64.000000 20.767401 57.356499 65.120598 20.143400 53.916500 65.120598 15.958700 53.916500 65.120499 15.958700 53.185902 64.000000 16.453100 56.732300 64.000000 20.767401 61.046799 64.000000 24.313999 61.541199 65.120598 23.583401 57.356499 65.120598 20.143400 57.356400 65.120499 20.143400 56.732399 64.000000 20.767401 61.046700 64.000000 24.313900 66.019302 64.000000 26.983101 66.364304 65.120598 26.172300 61.541199 65.120598 23.583401 61.541100 65.120499 23.583401 61.046799 64.000000 24.313999 66.019302 64.000000 26.983101 71.540298 64.000000 28.664900 71.719299 65.120598 27.803600 66.364304 65.120598 26.172300 66.364304 65.120499 26.172300 66.019302 64.000000 26.983101 71.540199 64.000000 28.664801 77.500000 64.000000 29.249901 77.500000 65.120598 28.371000 71.719299 65.120598 27.803600 71.719200 65.120499 27.803600 71.540298 64.000000 28.664900 77.500000 64.000000 29.249901 33.873699 26.687099 9.070200 32.500000 30.250000 0.000000 32.983299 26.687099 0.000000 33.873600 26.687000 9.070100 36.433399 26.687099 17.472700 35.987400 30.250000 17.662399 33.400002 30.250000 9.168700 36.433300 26.687000 17.472700 40.495499 26.687099 25.040501 40.093700 30.250000 25.312500 35.987400 30.250000 17.662399 35.987400 30.250000 17.662300 36.433399 26.687099 17.472700 40.495399 26.687000 25.040501 45.893200 26.687099 31.606701 45.549999 30.250000 31.950001 40.093700 30.250000 25.312500 45.893101 26.687000 31.606701 52.459400 26.687099 37.004398 52.187500 30.250000 37.406200 45.549999 30.250000 31.950001 45.549900 30.250000 31.950001 45.893200 26.687099 31.606701 52.459400 26.687000 37.004299 60.027199 26.687099 41.066502 60.027100 26.687000 41.066502 68.429703 26.687099 43.626202 68.429703 26.687000 43.626202 77.500000 26.687099 44.516602 77.500000 30.250000 45.000000 68.331200 30.250000 44.099899 68.331100 30.250000 44.099800 68.429703 26.687099 43.626202 77.500000 26.687000 44.516602 35.122601 23.579100 8.810500 33.873699 26.687099 9.070200 32.983299 26.687099 0.000000 32.983200 26.687000 0.000000 34.257801 23.579100 0.000000 35.122601 23.579000 8.810500 37.609001 23.579100 16.972500 36.433399 26.687099 17.472700 33.873699 26.687099 9.070200 33.873600 26.687000 9.070100 35.122601 23.579100 8.810500 37.609001 23.579000 16.972401 41.554901 23.579100 24.323700 40.495499 26.687099 25.040501 36.433399 26.687099 17.472700 36.433300 26.687000 17.472700 37.609001 23.579100 16.972500 41.554901 23.579000 24.323601 46.798000 23.579100 30.701900 45.893200 26.687099 31.606701 40.495499 26.687099 25.040501 40.495399 26.687000 25.040501 41.554901 23.579100 24.323700 46.798000 23.579000 30.701900 53.176201 23.579100 35.945000 52.459400 26.687099 37.004398 45.893200 26.687099 31.606701 45.893101 26.687000 31.606701 46.798000 23.579100 30.701900 53.176201 23.579000 35.944901 60.527401 23.579100 39.890900 60.027199 26.687099 41.066502 52.459400 26.687099 37.004398 52.459400 26.687000 37.004299 53.176201 23.579100 35.945000 60.527401 23.579000 39.890800 68.689400 23.579100 42.377300 68.429703 26.687099 43.626202 60.027199 26.687099 41.066502 60.027100 26.687000 41.066502 60.527401 23.579100 39.890900 68.689301 23.579000 42.377300 77.500000 23.579100 43.242100 77.500000 26.687099 44.516602 68.429703 26.687099 43.626202 68.429703 26.687000 43.626202 68.689400 23.579100 42.377300 77.500000 23.579000 43.242001 36.888302 20.906099 8.443400 35.122601 23.579100 8.810500 34.257801 23.579100 0.000000 34.257801 23.579000 0.000000 36.059502 20.906099 0.000000 36.888302 20.906000 8.443400 39.271198 20.906099 16.265301 37.609001 23.579100 16.972500 35.122601 23.579100 8.810500 35.122601 23.579000 8.810500 36.888302 20.906099 8.443400 39.271099 20.906000 16.265301 43.052601 20.906099 23.310200 41.554901 23.579100 24.323700 37.609001 23.579100 16.972500 37.609001 23.579000 16.972401 39.271198 20.906099 16.265301 43.052601 20.906000 23.310101 48.077202 20.906099 29.422701 46.798000 23.579100 30.701900 41.554901 23.579100 24.323700 41.554901 23.579000 24.323601 43.052601 20.906099 23.310200 48.077202 20.906000 29.422701 54.189701 20.906099 34.447300 53.176201 23.579100 35.945000 46.798000 23.579100 30.701900 46.798000 23.579000 30.701900 48.077202 20.906099 29.422701 54.189701 20.906000 34.447201 61.234600 20.906099 38.228699 60.527401 23.579100 39.890900 53.176201 23.579100 35.945000 53.176201 23.579000 35.944901 54.189701 20.906099 34.447300 61.234600 20.906000 38.228600 69.056503 20.906099 40.611599 68.689400 23.579100 42.377300 60.527401 23.579100 39.890900 60.527401 23.579000 39.890800 61.234600 20.906099 38.228699 69.056503 20.906000 40.611500 77.500000 20.906099 41.440399 77.500000 23.579100 43.242100 68.689400 23.579100 42.377300 68.689301 23.579000 42.377300 69.056503 20.906099 40.611599 77.500000 20.906000 41.440300 38.912399 18.648399 8.022600 36.888302 20.906099 8.443400 36.059502 20.906099 0.000000 36.059502 20.906000 0.000000 38.125000 18.648399 0.000000 38.912300 18.648300 8.022600 41.176498 18.648399 15.454600 39.271198 20.906099 16.265301 36.888302 20.906099 8.443400 36.888302 20.906000 8.443400 38.912399 18.648399 8.022600 41.176399 18.648300 15.454600 44.769501 18.648399 22.148399 43.052601 20.906099 23.310200 39.271198 20.906099 16.265301 39.271099 20.906000 16.265301 41.176498 18.648399 15.454600 44.769501 18.648300 22.148300 49.543701 18.648399 27.956200 48.077202 20.906099 29.422701 43.052601 20.906099 23.310200 43.052601 20.906000 23.310101 44.769501 18.648399 22.148399 49.543701 18.648300 27.956100 55.351501 18.648399 32.730400 54.189701 20.906099 34.447300 48.077202 20.906099 29.422701 48.077202 20.906000 29.422701 49.543701 18.648399 27.956200 55.351501 18.648300 32.730400 62.045300 18.648399 36.323399 61.234600 20.906099 38.228699 54.189701 20.906099 34.447300 54.189701 20.906000 34.447201 55.351501 18.648399 32.730400 62.045200 18.648300 36.323299 69.477303 18.648399 38.587502 69.056503 20.906099 40.611599 61.234600 20.906099 38.228699 61.234600 20.906000 38.228600 62.045300 18.648399 36.323399 69.477303 18.648300 38.587502 77.500000 18.648399 39.375000 77.500000 20.906099 41.440399 69.056503 20.906099 40.611599 69.056503 20.906000 40.611500 69.477303 18.648399 38.587502 77.500000 18.648300 39.375000 40.936600 16.786200 7.601800 38.912399 18.648399 8.022600 38.125000 18.648399 0.000000 38.125000 18.648300 0.000000 40.190399 16.786200 0.000000 40.936501 16.786100 7.601700 43.081902 16.786200 14.644000 41.176498 18.648399 15.454600 38.912399 18.648399 8.022600 38.912300 18.648300 8.022600 40.936600 16.786200 7.601800 43.081902 16.786100 14.644000 46.486401 16.786200 20.986601 44.769501 18.648399 22.148399 41.176498 18.648399 15.454600 41.176399 18.648300 15.454600 43.081902 16.786200 14.644000 46.486401 16.786100 20.986601 51.010201 16.786200 26.489700 49.543701 18.648399 27.956200 44.769501 18.648399 22.148399 44.769501 18.648300 22.148300 46.486401 16.786200 20.986601 51.010201 16.786100 26.489700 56.513302 16.786200 31.013500 55.351501 18.648399 32.730400 49.543701 18.648399 27.956200 49.543701 18.648300 27.956100 51.010201 16.786200 26.489700 56.513302 16.786100 31.013500 62.855900 16.786200 34.417999 62.045300 18.648399 36.323399 55.351501 18.648399 32.730400 55.351501 18.648300 32.730400 56.513302 16.786200 31.013500 62.855801 16.786100 34.417900 69.898102 16.786200 36.563301 69.477303 18.648399 38.587502 62.045300 18.648399 36.323399 62.045200 18.648300 36.323299 62.855900 16.786200 34.417999 69.898102 16.786100 36.563301 77.500000 16.786200 37.309502 77.500000 18.648399 39.375000 69.477303 18.648399 38.587502 69.477303 18.648300 38.587502 69.898102 16.786200 36.563301 77.500000 16.786100 37.309502 42.702301 15.299800 7.234700 40.936600 16.786200 7.601800 40.190399 16.786200 0.000000 40.190300 16.786100 0.000000 41.992100 15.299800 0.000000 42.702301 15.299700 7.234700 44.743999 15.299800 13.936800 43.081902 16.786200 14.644000 40.936600 16.786200 7.601800 40.936501 16.786100 7.601700 42.702301 15.299800 7.234700 44.743900 15.299700 13.936800 47.984100 15.299800 19.973101 46.486401 16.786200 20.986601 43.081902 16.786200 14.644000 43.081902 16.786100 14.644000 44.743999 15.299800 13.936800 47.984100 15.299700 19.973101 52.289398 15.299800 25.210501 51.010201 16.786200 26.489700 46.486401 16.786200 20.986601 46.486401 16.786100 20.986601 47.984100 15.299800 19.973101 52.289299 15.299700 25.210501 57.526798 15.299800 29.515800 56.513302 16.786200 31.013500 51.010201 16.786200 26.489700 51.010201 16.786100 26.489700 52.289398 15.299800 25.210501 57.526699 15.299700 29.515800 63.563099 15.299800 32.755901 62.855900 16.786200 34.417999 56.513302 16.786200 31.013500 56.513302 16.786100 31.013500 57.526798 15.299800 29.515800 63.563000 15.299700 32.755901 70.265198 15.299800 34.797600 69.898102 16.786200 36.563301 62.855900 16.786200 34.417999 62.855801 16.786100 34.417900 63.563099 15.299800 32.755901 70.265099 15.299700 34.797501 77.500000 15.299800 35.507801 77.500000 16.786200 37.309502 69.898102 16.786200 36.563301 69.898102 16.786100 36.563301 70.265198 15.299800 34.797600 77.500000 15.299700 35.507801 43.951199 14.169300 6.975000 42.702301 15.299800 7.234700 41.992100 15.299800 0.000000 41.992001 15.299700 0.000000 43.266602 14.169300 0.000000 43.951099 14.169300 6.974900 45.919601 14.169300 13.436600 44.743999 15.299800 13.936800 42.702301 15.299800 7.234700 42.702301 15.299700 7.234700 43.951199 14.169300 6.975000 45.919601 14.169300 13.436500 49.043400 14.169300 19.256201 47.984100 15.299800 19.973101 44.743999 15.299800 13.936800 44.743900 15.299700 13.936800 45.919601 14.169300 13.436600 49.043301 14.169300 19.256201 53.194199 14.169300 24.305700 52.289398 15.299800 25.210501 47.984100 15.299800 19.973101 47.984100 15.299700 19.973101 49.043400 14.169300 19.256201 53.194099 14.169300 24.305700 58.243698 14.169300 28.456499 57.526798 15.299800 29.515800 52.289398 15.299800 25.210501 52.289299 15.299700 25.210501 53.194199 14.169300 24.305700 58.243599 14.169300 28.456400 64.063301 14.169300 31.580299 63.563099 15.299800 32.755901 57.526798 15.299800 29.515800 57.526699 15.299700 29.515800 58.243698 14.169300 28.456499 64.063301 14.169300 31.580200 70.524902 14.169300 33.548698 70.265198 15.299800 34.797600 63.563099 15.299800 32.755901 63.563000 15.299700 32.755901 64.063301 14.169300 31.580299 70.524902 14.169300 33.548599 77.500000 14.169300 34.233299 77.500000 15.299800 35.507801 70.265198 15.299800 34.797600 70.265099 15.299700 34.797501 70.524902 14.169300 33.548698 77.500000 14.169300 34.233200 43.951199 14.169300 6.975000 43.266602 14.169300 0.000000 46.365601 13.375000 13.246800 45.919601 14.169300 13.436600 43.951199 14.169300 6.975000 43.951099 14.169300 6.974900 49.445301 13.375000 18.984301 49.043400 14.169300 19.256201 45.919601 14.169300 13.436600 45.919601 14.169300 13.436500 53.537399 13.375000 23.962400 53.194199 14.169300 24.305700 49.043400 14.169300 19.256201 49.043301 14.169300 19.256201 58.515598 13.375000 28.054600 58.243698 14.169300 28.456499 53.194199 14.169300 24.305700 53.194099 14.169300 24.305700 64.253098 13.375000 31.134300 64.063301 14.169300 31.580299 58.243698 14.169300 28.456499 58.243599 14.169300 28.456400 70.623398 13.375000 33.075001 70.524902 14.169300 33.548698 64.063301 14.169300 31.580299 64.063301 14.169300 31.580200 77.500000 13.375000 33.750000 77.500000 14.169300 34.233299 70.524902 14.169300 33.548698 70.524902 14.169300 33.548599 115.750000 42.062500 0.000000 115.750000 41.264801 4.872600 122.305000 42.024399 4.742500 122.305000 42.024300 4.742400 122.162003 42.728199 0.000000 115.750000 42.062500 0.000000 115.750000 39.162102 8.353100 122.684998 40.168800 8.130100 115.750000 39.162102 8.353100 115.750000 36.189201 10.441400 123.221001 37.545300 10.162600 123.221001 37.545200 10.162500 122.684998 40.168800 8.130100 115.750000 39.162102 8.353000 115.750000 36.189201 10.441400 115.750000 32.781200 11.137400 123.835999 34.537899 10.840200 123.835999 34.537800 10.840200 123.221001 37.545300 10.162600 115.750000 36.189201 10.441300 115.750000 32.781200 11.137400 115.750000 29.373199 10.441400 124.450996 31.530500 10.162600 124.450996 31.530500 10.162500 123.835999 34.537899 10.840200 115.750000 32.781200 11.137300 115.750000 29.373199 10.441400 115.750000 26.400299 8.353100 124.987000 28.907101 8.130100 124.987000 28.907101 8.130100 124.450996 31.530500 10.162600 115.750000 29.373100 10.441300 115.750000 26.400299 8.353100 115.750000 24.297600 4.872600 125.365997 27.051500 4.742500 125.365997 27.051500 4.742400 124.987000 28.907101 8.130100 115.750000 26.400200 8.353000 115.750000 24.297600 4.872600 115.750000 23.500000 0.000000 125.510002 26.347601 0.000000 125.510002 26.347601 0.000000 125.365997 27.051500 4.742500 115.750000 24.297501 4.872600 122.162003 42.728199 0.000000 122.305000 42.024399 4.742500 126.769997 43.949600 4.399600 126.769997 43.949600 4.399600 126.542999 44.541000 0.000000 122.161003 42.728100 0.000000 127.366997 42.390400 7.542300 127.366997 42.390301 7.542300 126.769997 43.949600 4.399600 122.305000 42.024300 4.742400 123.221001 37.545300 10.162600 128.212006 40.186100 9.427900 123.221001 37.545300 10.162600 123.835999 34.537899 10.840200 129.179993 37.659100 10.056400 129.179993 37.659000 10.056400 128.212006 40.186100 9.427900 123.221001 37.545200 10.162500 123.835999 34.537899 10.840200 124.450996 31.530500 10.162600 130.147995 35.132198 9.427900 130.147995 35.132099 9.427900 129.179993 37.659100 10.056400 123.835999 34.537800 10.840200 130.992004 32.927898 7.542300 130.992004 32.927799 7.542300 130.147995 35.132198 9.427900 124.450996 31.530500 10.162500 125.365997 27.051500 4.742500 131.589996 31.368700 4.399600 131.815994 30.777300 0.000000 131.815994 30.777201 0.000000 131.589996 31.368700 4.399600 125.365997 27.051500 4.742400 129.662994 46.755100 3.914900 129.395004 47.223801 0.000000 126.769997 43.949600 4.399600 127.366997 42.390400 7.542300 130.371002 45.519199 6.711200 130.371002 45.519100 6.711200 131.371994 43.771999 8.389000 131.371994 43.771900 8.388900 130.371002 45.519199 6.711200 127.366997 42.390301 7.542300 129.179993 37.659100 10.056400 132.520004 41.769100 8.948300 129.179993 37.659100 10.056400 130.147995 35.132198 9.427900 133.667007 39.766201 8.389000 133.667007 39.766201 8.388900 132.520004 41.769100 8.948300 129.179993 37.659000 10.056400 130.147995 35.132198 9.427900 130.992004 32.927898 7.542300 134.667999 38.019001 6.711200 134.667999 38.019001 6.711200 133.667007 39.766201 8.389000 130.147995 35.132099 9.427900 135.376007 36.783199 3.914900 135.376007 36.783100 3.914900 134.667999 38.019001 6.711200 130.992004 32.927799 7.542300 131.589996 31.368700 4.399600 131.815994 30.777300 0.000000 135.643997 36.314400 0.000000 135.643997 36.314301 0.000000 131.509003 50.155499 3.359100 131.509003 50.155399 3.359100 131.218994 50.500000 0.000000 129.395004 47.223801 0.000000 130.371002 45.519199 6.711200 132.272995 49.247501 5.758500 130.371002 45.519199 6.711200 131.371994 43.771999 8.389000 133.354004 47.963799 7.198200 133.354004 47.963699 7.198200 132.272995 49.247501 5.758500 130.371002 45.519100 6.711200 134.593994 46.492100 7.678100 134.593994 46.492001 7.678100 133.354004 47.963799 7.198200 131.371994 43.771900 8.388900 133.667007 39.766201 8.389000 135.832993 45.020500 7.198200 136.914001 43.736801 5.758500 136.914001 43.736801 5.758500 133.667007 39.766201 8.388900 135.376007 36.783199 3.914900 137.679001 42.828701 3.359100 135.376007 36.783199 3.914900 135.643997 36.314400 0.000000 137.968994 42.484299 0.000000 137.968994 42.484200 0.000000 137.679001 42.828701 3.359100 135.376007 36.783100 3.914900 131.218994 50.500000 0.000000 131.509003 50.155499 3.359100 132.826996 53.865601 2.803400 132.514999 54.092499 0.000000 133.647995 53.267502 4.805900 133.647995 53.267502 4.805900 131.509003 50.155399 3.359100 132.272995 49.247501 5.758500 133.354004 47.963799 7.198200 134.809006 52.421799 6.007300 134.809006 52.421700 6.007200 133.354004 47.963799 7.198200 134.593994 46.492100 7.678100 136.141006 51.452499 6.407800 136.141006 51.452400 6.407800 134.809006 52.421799 6.007300 133.354004 47.963699 7.198200 137.472000 50.483101 6.007300 137.472000 50.483101 6.007200 136.141006 51.452499 6.407800 134.593994 46.492001 7.678100 138.632996 49.637501 4.805900 139.453995 49.039299 2.803400 139.453995 49.039200 2.803400 137.968994 42.484299 0.000000 139.766006 48.812500 0.000000 134.139008 57.599899 2.318600 134.139008 57.599800 2.318500 133.785004 57.724602 0.000000 132.514999 54.092400 0.000000 135.070007 57.271400 3.974800 134.809006 52.421799 6.007300 136.388000 56.806900 4.968500 134.809006 52.421799 6.007300 136.141006 51.452499 6.407800 137.897995 56.274399 5.299800 137.897995 56.274300 5.299700 136.388000 56.806900 4.968500 134.809006 52.421700 6.007200 136.141006 51.452499 6.407800 137.472000 50.483101 6.007300 139.408997 55.741901 4.968500 139.408997 55.741901 4.968500 137.897995 56.274399 5.299800 136.141006 51.452400 6.407800 140.725998 55.277401 3.974800 140.725998 55.277401 3.974800 139.408997 55.741901 4.968500 137.472000 50.483101 6.007200 138.632996 49.637501 4.805900 139.453995 49.039299 2.803400 141.658005 54.948799 2.318600 141.658005 54.948700 2.318500 142.011993 54.824200 0.000000 142.011993 54.824100 0.000000 141.658005 54.948799 2.318600 139.453995 49.039200 2.803400 133.785004 57.724602 0.000000 134.139008 57.599899 2.318600 135.966003 61.073200 1.975700 135.529999 61.119301 0.000000 137.115997 60.951401 3.387000 137.115997 60.951401 3.387000 134.139008 57.599800 2.318500 138.742004 60.779400 4.233700 138.742004 60.779301 4.233600 136.388000 56.806900 4.968500 137.897995 56.274399 5.299800 140.604996 60.582100 4.516000 140.604996 60.582001 4.515900 142.468994 60.384800 4.233700 142.468994 60.384701 4.233600 140.604996 60.582100 4.516000 137.897995 56.274300 5.299700 144.095001 60.212799 3.387000 144.095001 60.212700 3.387000 140.725998 55.277401 3.974800 141.658005 54.948799 2.318600 145.244995 60.091000 1.975700 145.244995 60.090900 1.975700 141.658005 54.948799 2.318600 142.011993 54.824200 0.000000 145.681000 60.044899 0.000000 145.681000 60.044800 0.000000 145.244995 60.091000 1.975700 141.658005 54.948700 2.318500 138.830002 64.000000 1.845700 138.250000 64.000000 0.000000 140.358994 64.000000 3.164000 137.115997 60.951401 3.387000 138.742004 60.779400 4.233700 142.520996 64.000000 3.955000 142.520996 64.000000 3.954900 138.742004 60.779400 4.233700 140.604996 60.582100 4.516000 145.000000 64.000000 4.218700 145.000000 64.000000 4.218600 142.520996 64.000000 3.955000 138.742004 60.779301 4.233600 140.604996 60.582100 4.516000 142.468994 60.384800 4.233700 147.479004 64.000000 3.955000 147.479004 64.000000 3.954900 145.000000 64.000000 4.218700 140.604996 60.582001 4.515900 142.468994 60.384800 4.233700 144.095001 60.212799 3.387000 149.641006 64.000000 3.164000 149.641006 64.000000 3.164000 147.479004 64.000000 3.955000 142.468994 60.384701 4.233600 144.095001 60.212799 3.387000 145.244995 60.091000 1.975700 151.169998 64.000000 1.845700 151.169998 64.000000 1.845700 149.641006 64.000000 3.164000 144.095001 60.212700 3.387000 145.244995 60.091000 1.975700 145.681000 60.044899 0.000000 151.750000 64.000000 0.000000 151.750000 64.000000 0.000000 151.169998 64.000000 1.845700 145.244995 60.090900 1.975700 41.500000 55.562500 0.000000 41.596600 55.779999 2.214800 35.911301 55.769699 2.214800 35.911301 55.769600 2.214700 35.914501 55.552601 0.000000 41.500000 55.562500 0.000000 41.596600 55.779999 2.214800 41.851501 56.353500 3.796800 35.902802 56.341999 3.796800 35.902802 56.341900 3.796700 35.911301 55.769699 2.214800 41.596500 55.779900 2.214700 41.851501 56.353500 3.796800 42.211899 57.164299 4.746000 35.890900 57.151199 4.746000 35.890800 57.151100 4.745900 35.902802 56.341999 3.796800 41.851501 56.353500 3.796700 42.211899 57.164299 4.746000 42.625000 58.093700 5.062400 35.877102 58.078899 5.062400 35.877102 58.078800 5.062300 35.890900 57.151199 4.746000 42.211800 57.164200 4.745900 42.625000 58.093700 5.062400 43.037998 59.023102 4.746000 35.863400 59.006500 4.746000 35.863300 59.006500 4.745900 35.877102 58.078899 5.062400 42.625000 58.093700 5.062300 43.037998 59.023102 4.746000 43.398399 59.833900 3.796800 35.851501 59.815701 3.796800 35.851501 59.815701 3.796700 35.863400 59.006500 4.746000 43.037899 59.023102 4.745900 43.398399 59.833900 3.796800 43.653301 60.407398 2.214800 35.842999 60.388100 2.214800 35.842899 60.388000 2.214700 35.851501 59.815701 3.796800 43.398300 59.833900 3.796700 43.653301 60.407398 2.214800 43.750000 60.625000 0.000000 35.839802 60.605202 0.000000 35.839802 60.605202 0.000000 35.842999 60.388100 2.214800 43.653301 60.407299 2.214700 35.914501 55.552601 0.000000 35.911301 55.769699 2.214800 30.902100 55.697498 2.214800 30.902000 55.697399 2.214700 30.988199 55.483398 0.000000 35.914501 55.552601 0.000000 35.911301 55.769699 2.214800 35.902802 56.341999 3.796800 30.675100 56.262001 3.796800 30.675100 56.262001 3.796700 30.902100 55.697498 2.214800 35.911301 55.769600 2.214700 35.902802 56.341999 3.796800 35.890900 57.151199 4.746000 30.354200 57.060101 4.746000 30.354200 57.060101 4.745900 30.675100 56.262001 3.796800 35.902802 56.341900 3.796700 35.890900 57.151199 4.746000 35.877102 58.078899 5.062400 29.986300 57.974998 5.062400 29.986200 57.974899 5.062300 30.354200 57.060101 4.746000 35.890800 57.151100 4.745900 35.877102 58.078899 5.062400 35.863400 59.006500 4.746000 29.618401 58.889999 4.746000 29.618401 58.889900 4.745900 29.986300 57.974998 5.062400 35.877102 58.078800 5.062300 35.863400 59.006500 4.746000 35.851501 59.815701 3.796800 29.297400 59.688099 3.796800 29.297300 59.688000 3.796700 29.618401 58.889999 4.746000 35.863300 59.006500 4.745900 35.851501 59.815701 3.796800 35.842999 60.388100 2.214800 29.070400 60.252602 2.214800 29.070400 60.252602 2.214700 29.297400 59.688099 3.796800 35.851501 59.815701 3.796700 28.984301 60.466702 0.000000 28.984301 60.466702 0.000000 29.070400 60.252602 2.214800 35.842899 60.388000 2.214700 30.988199 55.483398 0.000000 30.902100 55.697498 2.214800 26.594400 55.501499 2.214800 26.594400 55.501400 2.214700 26.747499 55.295502 0.000000 30.988100 55.483299 0.000000 30.902100 55.697498 2.214800 30.675100 56.262001 3.796800 26.190599 56.044800 3.796800 26.190500 56.044701 3.796700 26.594400 55.501499 2.214800 30.902000 55.697399 2.214700 30.675100 56.262001 3.796800 30.354200 57.060101 4.746000 25.619801 56.812801 4.746000 25.619801 56.812801 4.745900 26.190599 56.044800 3.796800 30.675100 56.262001 3.796700 30.354200 57.060101 4.746000 29.986300 57.974998 5.062400 24.965500 57.693199 5.062400 24.965401 57.693100 5.062300 25.619801 56.812801 4.746000 30.354200 57.060101 4.745900 29.986300 57.974998 5.062400 29.618401 58.889999 4.746000 24.311199 58.573700 4.746000 24.311100 58.573601 4.745900 24.965500 57.693199 5.062400 29.986200 57.974899 5.062300 29.618401 58.889999 4.746000 29.297400 59.688099 3.796800 23.740400 59.341702 3.796800 23.740400 59.341702 3.796700 24.311199 58.573700 4.746000 29.618401 58.889900 4.745900 29.297400 59.688099 3.796800 29.070400 60.252602 2.214800 23.336700 59.884998 2.214800 23.336700 59.884899 2.214700 23.740400 59.341702 3.796800 29.297300 59.688000 3.796700 23.183500 60.091000 0.000000 23.183500 60.090900 0.000000 23.336700 59.884998 2.214800 29.070400 60.252602 2.214700 26.747499 55.295502 0.000000 26.594400 55.501499 2.214800 23.013300 55.119999 2.214800 23.013201 55.119900 2.214700 23.218700 54.929600 0.000000 26.747400 55.295502 0.000000 26.594400 55.501499 2.214800 26.190599 56.044800 3.796800 22.471600 55.621799 3.796800 22.471500 55.621700 3.796700 23.013300 55.119999 2.214800 26.594400 55.501400 2.214700 26.190599 56.044800 3.796800 25.619801 56.812801 4.746000 21.705900 56.331200 4.746000 21.705900 56.331100 4.745900 22.471600 55.621799 3.796800 26.190500 56.044701 3.796700 25.619801 56.812801 4.746000 24.965500 57.693199 5.062400 20.828100 57.144501 5.062400 20.828100 57.144501 5.062300 21.705900 56.331200 4.746000 25.619801 56.812801 4.745900 24.965500 57.693199 5.062400 24.311199 58.573700 4.746000 19.950300 57.957699 4.746000 19.950300 57.957600 4.745900 20.828100 57.144501 5.062400 24.965401 57.693100 5.062300 24.311199 58.573700 4.746000 23.740400 59.341702 3.796800 19.184500 58.667198 3.796800 19.184401 58.667099 3.796700 19.950300 57.957699 4.746000 24.311100 58.573601 4.745900 23.740400 59.341702 3.796800 23.336700 59.884998 2.214800 18.642900 59.168999 2.214800 18.642900 59.168900 2.214700 19.184500 58.667198 3.796800 23.740400 59.341702 3.796700 23.336700 59.884998 2.214800 23.183500 60.091000 0.000000 18.437500 59.359299 0.000000 18.437500 59.359200 0.000000 18.642900 59.168999 2.214800 23.336700 59.884899 2.214700 23.218700 54.929600 0.000000 23.013300 55.119999 2.214800 20.184000 54.490898 2.214800 20.184000 54.490799 2.214700 20.428200 54.326500 0.000000 23.218700 54.929501 0.000000 23.013300 55.119999 2.214800 22.471600 55.621799 3.796800 19.540300 54.924400 3.796800 19.540300 54.924400 3.796700 20.184000 54.490898 2.214800 23.013201 55.119900 2.214700 22.471600 55.621799 3.796800 21.705900 56.331200 4.746000 18.630301 55.537201 4.746000 18.630301 55.537201 4.745900 19.540300 54.924400 3.796800 22.471500 55.621700 3.796700 21.705900 56.331200 4.746000 20.828100 57.144501 5.062400 17.587099 56.239799 5.062400 17.587000 56.239700 5.062300 18.630301 55.537201 4.746000 21.705900 56.331100 4.745900 20.828100 57.144501 5.062400 19.950300 57.957699 4.746000 16.543900 56.942299 4.746000 16.543800 56.942200 4.745900 17.587099 56.239799 5.062400 20.828100 57.144501 5.062300 19.950300 57.957699 4.746000 19.184500 58.667198 3.796800 15.633900 57.555099 3.796800 15.633800 57.555000 3.796700 16.543900 56.942299 4.746000 19.950300 57.957600 4.745900 19.184500 58.667198 3.796800 18.642900 59.168999 2.214800 14.990200 57.988602 2.214800 14.990200 57.988602 2.214700 15.633900 57.555099 3.796800 19.184401 58.667099 3.796700 18.642900 59.168999 2.214800 18.437500 59.359299 0.000000 14.746000 58.153000 0.000000 14.746000 58.152901 0.000000 14.990200 57.988602 2.214800 18.642900 59.168900 2.214700 20.428200 54.326500 0.000000 20.184000 54.490898 2.214800 18.131901 53.552502 2.214800 18.131901 53.552502 2.214700 18.402300 53.426701 0.000000 20.428101 54.326401 0.000000 20.184000 54.490898 2.214800 19.540300 54.924400 3.796800 17.419001 53.883999 3.796800 17.419001 53.883900 3.796700 18.131901 53.552502 2.214800 20.184000 54.490799 2.214700 19.540300 54.924400 3.796800 18.630301 55.537201 4.746000 16.411200 54.352798 4.746000 16.411100 54.352699 4.745900 17.419001 53.883999 3.796800 19.540300 54.924400 3.796700 18.630301 55.537201 4.746000 17.587099 56.239799 5.062400 15.255800 54.890099 5.062400 15.255800 54.889999 5.062300 16.411200 54.352798 4.746000 18.630301 55.537201 4.745900 17.587099 56.239799 5.062400 16.543900 56.942299 4.746000 14.100500 55.427399 4.746000 14.100500 55.427299 4.745900 15.255800 54.890099 5.062400 17.587000 56.239700 5.062300 16.543900 56.942299 4.746000 15.633900 57.555099 3.796800 13.092600 55.896198 3.796800 13.092500 55.896099 3.796700 14.100500 55.427399 4.746000 16.543800 56.942200 4.745900 15.633900 57.555099 3.796800 14.990200 57.988602 2.214800 12.379700 56.227699 2.214800 12.379600 56.227600 2.214700 13.092600 55.896198 3.796800 15.633800 57.555000 3.796700 14.990200 57.988602 2.214800 14.746000 58.153000 0.000000 12.109300 56.353500 0.000000 12.109200 56.353500 0.000000 12.379700 56.227699 2.214800 14.990200 57.988602 2.214700 18.402300 53.426701 0.000000 18.131901 53.552502 2.214800 16.882099 52.242802 2.214800 16.882000 52.242802 2.214700 17.167400 52.171001 0.000000 18.402201 53.426701 0.000000 18.131901 53.552502 2.214800 17.419001 53.883999 3.796800 16.129900 52.432098 3.796800 16.129801 52.431999 3.796700 16.882099 52.242802 2.214800 18.131901 53.552502 2.214700 17.419001 53.883999 3.796800 16.411200 54.352798 4.746000 15.066400 52.699699 4.746000 15.066300 52.699600 4.745900 16.129900 52.432098 3.796800 17.419001 53.883900 3.796700 16.411200 54.352798 4.746000 15.255800 54.890099 5.062400 13.847400 53.006500 5.062400 13.847300 53.006500 5.062300 15.066400 52.699699 4.746000 16.411100 54.352699 4.745900 15.255800 54.890099 5.062400 14.100500 55.427399 4.746000 12.628300 53.313301 4.746000 12.628200 53.313301 4.745900 13.847400 53.006500 5.062400 15.255800 54.889999 5.062300 14.100500 55.427399 4.746000 13.092600 55.896198 3.796800 11.564800 53.580898 3.796800 11.564800 53.580799 3.796700 12.628300 53.313301 4.746000 14.100500 55.427299 4.745900 13.092600 55.896198 3.796800 12.379700 56.227699 2.214800 10.812600 53.770199 2.214800 10.812600 53.770100 2.214700 11.564800 53.580898 3.796800 13.092500 55.896099 3.796700 12.379700 56.227699 2.214800 12.109300 56.353500 0.000000 10.527300 53.841999 0.000000 10.527200 53.841900 0.000000 10.812600 53.770199 2.214800 12.379600 56.227600 2.214700 17.167400 52.171001 0.000000 16.882099 52.242802 2.214800 16.459900 50.500000 2.214800 16.459801 50.500000 2.214700 16.750000 50.500000 0.000000 17.167400 52.171001 0.000000 16.882099 52.242802 2.214800 16.129900 52.432098 3.796800 15.695300 50.500000 3.796800 15.695300 50.500000 3.796700 16.459900 50.500000 2.214800 16.882000 52.242802 2.214700 16.129900 52.432098 3.796800 15.066400 52.699699 4.746000 14.614200 50.500000 4.746000 14.614100 50.500000 4.745900 15.695300 50.500000 3.796800 16.129801 52.431999 3.796700 15.066400 52.699699 4.746000 13.847400 53.006500 5.062400 13.375000 50.500000 5.062400 13.375000 50.500000 5.062300 14.614200 50.500000 4.746000 15.066300 52.699600 4.745900 13.847400 53.006500 5.062400 12.628300 53.313301 4.746000 12.135700 50.500000 4.746000 12.135700 50.500000 4.745900 13.375000 50.500000 5.062400 13.847300 53.006500 5.062300 12.628300 53.313301 4.746000 11.564800 53.580898 3.796800 11.054600 50.500000 3.796800 11.054500 50.500000 3.796700 12.135700 50.500000 4.746000 12.628200 53.313301 4.745900 11.564800 53.580898 3.796800 10.812600 53.770199 2.214800 10.290000 50.500000 2.214800 10.289900 50.500000 2.214700 11.054600 50.500000 3.796800 11.564800 53.580799 3.796700 10.812600 53.770199 2.214800 10.527300 53.841999 0.000000 10.000000 50.500000 0.000000 10.812600 53.770100 2.214700 139.697006 64.560402 1.813900 139.697006 64.560402 1.813800 139.085007 64.553703 0.000000 138.250000 64.000000 0.000000 141.311996 64.578003 3.109600 140.358994 64.000000 3.164000 142.520996 64.000000 3.955000 143.593994 64.602898 3.887000 143.593994 64.602798 3.887000 142.520996 64.000000 3.955000 145.000000 64.000000 4.218700 146.210999 64.631500 4.146200 146.210999 64.631500 4.146200 143.593994 64.602898 3.887000 142.520996 64.000000 3.954900 145.000000 64.000000 4.218700 147.479004 64.000000 3.955000 148.828003 64.660103 3.887000 148.828003 64.660103 3.887000 146.210999 64.631500 4.146200 145.000000 64.000000 4.218600 151.110992 64.685097 3.109600 151.110992 64.684998 3.109600 148.828003 64.660103 3.887000 147.479004 64.000000 3.954900 152.725006 64.702698 1.813900 152.725006 64.702599 1.813800 153.337006 64.709396 0.000000 153.337006 64.709297 0.000000 139.085007 64.553703 0.000000 139.697006 64.560402 1.813900 140.488007 64.961899 1.730300 140.488007 64.961800 1.730200 139.867004 64.949203 0.000000 139.085007 64.553703 0.000000 142.126007 64.995499 2.966300 142.126007 64.995399 2.966300 140.488007 64.961899 1.730300 139.697006 64.560402 1.813800 141.311996 64.578003 3.109600 143.593994 64.602898 3.887000 144.442001 65.042999 3.707800 144.442001 65.042900 3.707700 147.095993 65.097504 3.955000 147.095993 65.097504 3.954900 144.442001 65.042999 3.707800 143.593994 64.602798 3.887000 149.751007 65.151901 3.707800 149.751007 65.151901 3.707700 151.110992 64.685097 3.109600 152.065994 65.199501 2.966300 151.110992 64.685097 3.109600 152.725006 64.702698 1.813900 153.703995 65.233101 1.730300 153.703995 65.233101 1.730200 152.065994 65.199501 2.966300 151.110992 64.684998 3.109600 152.725006 64.702698 1.813900 153.337006 64.709396 0.000000 154.324997 65.245796 0.000000 154.324997 65.245697 0.000000 153.703995 65.233101 1.730300 152.725006 64.702599 1.813800 139.867004 64.949203 0.000000 140.488007 64.961899 1.730300 141.154007 65.204002 1.612100 140.544006 65.186501 0.000000 140.488007 64.961899 1.730300 142.126007 64.995499 2.966300 142.763000 65.250198 2.763600 142.763000 65.250099 2.763600 141.154007 65.204002 1.612100 140.488007 64.961800 1.730200 142.126007 64.995499 2.966300 144.442001 65.042999 3.707800 145.037994 65.315498 3.454500 145.037994 65.315399 3.454400 142.763000 65.250198 2.763600 142.126007 64.995399 2.966300 144.442001 65.042999 3.707800 147.095993 65.097504 3.955000 147.645004 65.390404 3.684800 147.645004 65.390404 3.684700 145.037994 65.315498 3.454500 144.442001 65.042900 3.707700 147.095993 65.097504 3.955000 149.751007 65.151901 3.707800 150.251999 65.465302 3.454500 150.251999 65.465302 3.454400 147.645004 65.390404 3.684800 147.095993 65.097504 3.954900 152.526993 65.530602 2.763600 152.526993 65.530602 2.763600 150.251999 65.465302 3.454500 149.751007 65.151901 3.707700 152.065994 65.199501 2.966300 153.703995 65.233101 1.730300 154.136002 65.576797 1.612100 154.136002 65.576698 1.612100 153.703995 65.233101 1.730300 154.324997 65.245796 0.000000 154.746002 65.594299 0.000000 154.746002 65.594200 0.000000 154.136002 65.576797 1.612100 153.703995 65.233101 1.730200 141.645004 65.286003 1.476500 141.063004 65.265602 0.000000 141.154007 65.204002 1.612100 142.763000 65.250198 2.763600 143.182999 65.339699 2.531200 143.182999 65.339600 2.531100 142.763000 65.250198 2.763600 145.037994 65.315498 3.454500 145.356003 65.415703 3.164000 145.356003 65.415703 3.164000 143.182999 65.339699 2.531200 142.763000 65.250099 2.763600 145.037994 65.315498 3.454500 147.645004 65.390404 3.684800 147.848007 65.502899 3.374900 147.848007 65.502800 3.374900 145.356003 65.415703 3.164000 145.037994 65.315399 3.454400 147.645004 65.390404 3.684800 150.251999 65.465302 3.454500 150.339005 65.589996 3.164000 150.339005 65.589897 3.164000 147.848007 65.502899 3.374900 147.645004 65.390404 3.684700 152.511993 65.666000 2.531200 152.511993 65.666000 2.531100 150.339005 65.589996 3.164000 150.251999 65.465302 3.454400 154.136002 65.576797 1.612100 154.050003 65.719803 1.476500 154.136002 65.576797 1.612100 154.746002 65.594299 0.000000 154.632996 65.740196 0.000000 154.632996 65.740097 0.000000 154.050003 65.719803 1.476500 154.136002 65.576698 1.612100 141.914001 65.207199 1.341000 141.914001 65.207100 1.340900 141.369995 65.186501 0.000000 141.063004 65.265602 0.000000 141.645004 65.286003 1.476500 143.182999 65.339699 2.531200 143.345993 65.261803 2.298800 143.345993 65.261803 2.298700 145.371994 65.338997 2.873600 145.371994 65.338898 2.873600 143.345993 65.261803 2.298800 143.182999 65.339600 2.531100 145.356003 65.415703 3.164000 147.848007 65.502899 3.374900 147.694000 65.427498 3.065100 147.694000 65.427399 3.065000 147.848007 65.502899 3.374900 150.339005 65.589996 3.164000 150.016006 65.515999 2.873600 150.016006 65.515900 2.873600 147.694000 65.427498 3.065100 147.848007 65.502800 3.374900 150.339005 65.589996 3.164000 152.511993 65.666000 2.531200 152.042007 65.593201 2.298800 152.042007 65.593201 2.298700 150.016006 65.515999 2.873600 150.339005 65.589897 3.164000 153.475006 65.647797 1.341000 153.475006 65.647697 1.340900 152.042007 65.593201 2.298800 152.511993 65.666000 2.531100 154.632996 65.740196 0.000000 154.018997 65.668503 0.000000 141.369995 65.186501 0.000000 141.914001 65.207199 1.341000 141.908997 64.967003 1.222700 141.908997 64.967003 1.222600 141.414001 64.949203 0.000000 141.369995 65.186501 0.000000 141.914001 65.207199 1.341000 143.345993 65.261803 2.298800 143.214005 65.014099 2.096100 143.214005 65.014000 2.096100 141.908997 64.967003 1.222700 141.914001 65.207100 1.340900 143.345993 65.261803 2.298800 145.371994 65.338997 2.873600 145.059998 65.080597 2.620200 145.059998 65.080498 2.620100 143.214005 65.014099 2.096100 143.345993 65.261803 2.298700 145.371994 65.338997 2.873600 147.694000 65.427498 3.065100 147.175003 65.156799 2.794900 147.175003 65.156700 2.794800 145.059998 65.080597 2.620200 145.371994 65.338898 2.873600 147.694000 65.427498 3.065100 150.016006 65.515999 2.873600 149.291000 65.233101 2.620200 149.291000 65.233101 2.620100 147.175003 65.156799 2.794900 147.694000 65.427399 3.065000 150.016006 65.515999 2.873600 152.042007 65.593201 2.298800 151.136002 65.299599 2.096100 151.136002 65.299500 2.096100 149.291000 65.233101 2.620200 150.016006 65.515900 2.873600 152.042007 65.593201 2.298800 153.475006 65.647797 1.341000 152.440994 65.346603 1.222700 152.440994 65.346603 1.222600 151.136002 65.299599 2.096100 152.042007 65.593201 2.298700 152.936005 65.364502 0.000000 152.936005 65.364502 0.000000 152.440994 65.346603 1.222700 153.475006 65.647697 1.340900 141.414001 64.949203 0.000000 141.908997 64.967003 1.222700 141.582993 64.564796 1.139100 141.582993 64.564697 1.139000 141.141998 64.553703 0.000000 141.414001 64.949203 0.000000 141.908997 64.967003 1.222700 143.214005 65.014099 2.096100 142.746994 64.594200 1.952800 142.746994 64.594200 1.952800 141.582993 64.564796 1.139100 141.908997 64.967003 1.222600 143.214005 65.014099 2.096100 145.059998 65.080597 2.620200 144.393005 64.635803 2.441000 144.393005 64.635803 2.440900 142.746994 64.594200 1.952800 143.214005 65.014000 2.096100 145.059998 65.080597 2.620200 147.175003 65.156799 2.794900 146.279999 64.683403 2.603700 146.279999 64.683403 2.603600 144.393005 64.635803 2.441000 145.059998 65.080498 2.620100 147.175003 65.156799 2.794900 149.291000 65.233101 2.620200 148.167007 64.731102 2.441000 148.167007 64.731102 2.440900 146.279999 64.683403 2.603700 147.175003 65.156700 2.794800 149.291000 65.233101 2.620200 151.136002 65.299599 2.096100 149.813004 64.772697 1.952800 149.813004 64.772598 1.952800 148.167007 64.731102 2.441000 149.291000 65.233101 2.620100 151.136002 65.299599 2.096100 152.440994 65.346603 1.222700 150.977997 64.802101 1.139100 150.977997 64.802101 1.139000 149.813004 64.772697 1.952800 151.136002 65.299500 2.096100 151.419006 64.813202 0.000000 151.419006 64.813202 0.000000 150.977997 64.802101 1.139100 152.440994 65.346603 1.222600 141.141998 64.553703 0.000000 141.582993 64.564796 1.139100 140.886993 64.000000 1.107400 140.886993 64.000000 1.107300 140.500000 64.000000 0.000000 141.141998 64.553703 0.000000 141.906006 64.000000 1.898400 141.906006 64.000000 1.898300 140.886993 64.000000 1.107400 141.582993 64.564697 1.139000 142.746994 64.594200 1.952800 144.393005 64.635803 2.441000 143.348007 64.000000 2.373000 143.348007 64.000000 2.372900 144.393005 64.635803 2.441000 146.279999 64.683403 2.603700 145.000000 64.000000 2.531200 145.000000 64.000000 2.531100 143.348007 64.000000 2.373000 144.393005 64.635803 2.440900 146.279999 64.683403 2.603700 148.167007 64.731102 2.441000 146.651993 64.000000 2.373000 146.651993 64.000000 2.372900 145.000000 64.000000 2.531200 146.279999 64.683403 2.603600 148.167007 64.731102 2.441000 149.813004 64.772697 1.952800 148.093994 64.000000 1.898400 148.093994 64.000000 1.898300 146.651993 64.000000 2.373000 148.167007 64.731102 2.440900 149.813004 64.772697 1.952800 150.977997 64.802101 1.139100 149.113007 64.000000 1.107400 149.113007 64.000000 1.107300 148.093994 64.000000 1.898400 149.813004 64.772598 1.952800 149.500000 64.000000 0.000000 149.500000 64.000000 0.000000 149.113007 64.000000 1.107400 150.977997 64.802101 1.139000 82.573601 80.578300 1.058400 82.676697 80.578300 0.000000 77.500000 80.875000 0.000000 82.277199 80.578300 2.037300 82.573700 80.578300 1.058400 77.500000 80.875000 0.000000 81.806503 80.578300 2.917500 82.277298 80.578300 2.037300 77.500000 80.875000 0.000000 81.180199 80.578300 3.680200 81.806503 80.578300 2.917600 77.500000 80.875000 0.000000 80.417603 80.578300 4.306400 81.180298 80.578300 3.680300 77.500000 80.875000 0.000000 79.537300 80.578300 4.777200 80.417603 80.578300 4.306500 77.500000 80.875000 0.000000 78.558403 80.578300 5.073600 79.537300 80.578300 4.777300 77.500000 80.875000 0.000000 77.500000 80.578300 5.176700 78.558403 80.578300 5.073700 77.500000 80.875000 0.000000 82.676697 80.578300 0.000000 82.573700 80.578300 1.058400 85.011497 79.767502 1.567000 85.011398 79.767502 1.567000 85.164001 79.767502 0.000000 82.676598 80.578300 0.000000 82.573700 80.578300 1.058400 82.277298 80.578300 2.037300 84.572701 79.767502 3.016100 84.572701 79.767502 3.016000 85.011497 79.767502 1.567000 82.573601 80.578300 1.058400 82.277298 80.578300 2.037300 81.806503 80.578300 2.917600 83.875702 79.767502 4.319300 83.875702 79.767502 4.319300 84.572701 79.767502 3.016100 82.277199 80.578300 2.037300 81.806503 80.578300 2.917600 81.180298 80.578300 3.680300 82.948601 79.767502 5.448600 82.948601 79.767502 5.448500 83.875702 79.767502 4.319300 81.806503 80.578300 2.917500 81.180298 80.578300 3.680300 80.417603 80.578300 4.306500 81.819298 79.767502 6.375700 81.819199 79.767502 6.375600 82.948601 79.767502 5.448600 81.180199 80.578300 3.680200 80.417603 80.578300 4.306500 79.537300 80.578300 4.777300 80.516098 79.767502 7.072700 80.515999 79.767502 7.072700 81.819298 79.767502 6.375700 80.417603 80.578300 4.306400 79.537300 80.578300 4.777300 78.558403 80.578300 5.073700 79.067001 79.767502 7.511500 79.067001 79.767502 7.511400 80.516098 79.767502 7.072700 79.537300 80.578300 4.777200 77.500000 79.767502 7.664000 77.500000 79.767502 7.664000 79.067001 79.767502 7.511500 78.558403 80.578300 5.073600 85.164001 79.767502 0.000000 85.011497 79.767502 1.567000 85.485298 78.561203 1.665700 85.485199 78.561203 1.665600 85.647400 78.561203 0.000000 85.164001 79.767502 0.000000 85.011497 79.767502 1.567000 84.572701 79.767502 3.016100 85.018799 78.561203 3.206200 85.018700 78.561203 3.206100 85.485298 78.561203 1.665700 85.011398 79.767502 1.567000 84.277702 78.561203 4.591600 84.277702 78.561203 4.591500 85.018799 78.561203 3.206200 84.572701 79.767502 3.016000 83.875702 79.767502 4.319300 82.948601 79.767502 5.448600 83.292099 78.561203 5.792100 83.292000 78.561203 5.792000 82.948601 79.767502 5.448600 81.819298 79.767502 6.375700 82.091599 78.561203 6.777700 82.091499 78.561203 6.777600 83.292099 78.561203 5.792100 82.948601 79.767502 5.448500 81.819298 79.767502 6.375700 80.516098 79.767502 7.072700 80.706200 78.561203 7.518800 80.706100 78.561203 7.518700 82.091599 78.561203 6.777700 81.819199 79.767502 6.375600 80.516098 79.767502 7.072700 79.067001 79.767502 7.511500 79.165703 78.561203 7.985300 79.165703 78.561203 7.985300 80.706200 78.561203 7.518800 80.515999 79.767502 7.072700 77.500000 78.561203 8.147400 77.500000 78.561203 8.147300 79.165703 78.561203 7.985300 79.067001 79.767502 7.511400 85.647400 78.561203 0.000000 85.485298 78.561203 1.665700 84.666901 77.078102 1.494700 84.666901 77.078102 1.494600 84.812500 77.078102 0.000000 85.647301 78.561203 0.000000 85.485298 78.561203 1.665700 85.018799 78.561203 3.206200 84.248100 77.078102 2.877200 84.248100 77.078102 2.877100 84.666901 77.078102 1.494700 85.485199 78.561203 1.665600 85.018799 78.561203 3.206200 84.277702 78.561203 4.591600 83.582901 77.078102 4.120600 83.582901 77.078102 4.120600 84.248100 77.078102 2.877200 85.018700 78.561203 3.206100 84.277702 78.561203 4.591600 83.292099 78.561203 5.792100 82.698196 77.078102 5.198200 82.698097 77.078102 5.198200 83.582901 77.078102 4.120600 84.277702 78.561203 4.591500 83.292099 78.561203 5.792100 82.091599 78.561203 6.777700 81.620598 77.078102 6.082900 81.620499 77.078102 6.082900 82.698196 77.078102 5.198200 83.292000 78.561203 5.792000 82.091599 78.561203 6.777700 80.706200 78.561203 7.518800 80.377197 77.078102 6.748100 80.377098 77.078102 6.748000 81.620598 77.078102 6.082900 82.091499 78.561203 6.777600 78.994698 77.078102 7.166900 78.994598 77.078102 7.166900 80.377197 77.078102 6.748100 80.706100 78.561203 7.518700 79.165703 78.561203 7.985300 77.500000 78.561203 8.147400 77.500000 77.078102 7.312400 77.500000 77.078102 7.312300 84.812500 77.078102 0.000000 84.666901 77.078102 1.494700 83.228302 75.436699 1.194200 83.228302 75.436600 1.194200 83.344704 75.436699 0.000000 84.812500 77.078102 0.000000 84.666901 77.078102 1.494700 84.248100 77.078102 2.877200 82.893402 75.436699 2.299000 82.893402 75.436600 2.299000 83.228302 75.436699 1.194200 84.666901 77.078102 1.494600 82.361504 75.436699 3.292800 82.361504 75.436600 3.292700 82.893402 75.436699 2.299000 84.248100 77.078102 2.877100 83.582901 77.078102 4.120600 82.698196 77.078102 5.198200 81.654198 75.436699 4.154200 81.654099 75.436600 4.154200 82.698196 77.078102 5.198200 81.620598 77.078102 6.082900 80.792801 75.436699 4.861500 80.792801 75.436600 4.861400 81.654198 75.436699 4.154200 82.698097 77.078102 5.198200 81.620598 77.078102 6.082900 80.377197 77.078102 6.748100 79.799004 75.436699 5.393400 79.799004 75.436600 5.393400 80.792801 75.436699 4.861500 81.620499 77.078102 6.082900 80.377197 77.078102 6.748100 78.994698 77.078102 7.166900 78.694199 75.436699 5.728300 78.694099 75.436600 5.728300 79.799004 75.436699 5.393400 80.377098 77.078102 6.748000 78.994698 77.078102 7.166900 77.500000 77.078102 7.312400 77.500000 75.436699 5.844700 77.500000 75.436600 5.844600 78.694199 75.436699 5.728300 78.994598 77.078102 7.166900 83.344704 75.436699 0.000000 83.228302 75.436699 1.194200 81.841301 73.755798 0.904300 81.841301 73.755699 0.904200 81.929604 73.755798 0.000000 83.344704 75.436600 0.000000 83.228302 75.436699 1.194200 82.893402 75.436699 2.299000 81.587196 73.755798 1.741300 81.587097 73.755699 1.741200 81.841301 73.755798 0.904300 83.228302 75.436600 1.194200 82.893402 75.436699 2.299000 82.361504 75.436699 3.292800 81.183800 73.755798 2.494400 81.183701 73.755699 2.494400 81.587196 73.755798 1.741300 82.893402 75.436600 2.299000 82.361504 75.436699 3.292800 81.654198 75.436699 4.154200 80.647400 73.755798 3.147400 80.647301 73.755699 3.147300 81.183800 73.755798 2.494400 82.361504 75.436600 3.292700 81.654198 75.436699 4.154200 80.792801 75.436699 4.861500 79.994400 73.755798 3.683800 79.994400 73.755699 3.683700 80.647400 73.755798 3.147400 81.654099 75.436600 4.154200 80.792801 75.436699 4.861500 79.799004 75.436699 5.393400 79.241302 73.755798 4.087200 79.241302 73.755699 4.087200 79.994400 73.755798 3.683800 80.792801 75.436600 4.861400 79.799004 75.436699 5.393400 78.694199 75.436699 5.728300 78.404297 73.755798 4.341300 78.404198 73.755699 4.341300 79.241302 73.755798 4.087200 79.799004 75.436600 5.393400 78.694199 75.436699 5.728300 77.500000 75.436699 5.844700 77.500000 73.755798 4.429600 77.500000 73.755699 4.429500 78.404297 73.755798 4.341300 78.694099 75.436600 5.728300 81.929604 73.755798 0.000000 81.841301 73.755798 0.904300 81.177902 72.153999 0.765100 81.177902 72.153900 0.765100 81.252899 72.153999 0.000000 81.929604 73.755699 0.000000 81.841301 73.755798 0.904300 81.587196 73.755798 1.741300 80.962303 72.153999 1.473800 80.962303 72.153900 1.473700 81.177902 72.153999 0.765100 81.841301 73.755699 0.904200 81.587196 73.755798 1.741300 81.183800 73.755798 2.494400 80.620102 72.153999 2.111800 80.620102 72.153900 2.111700 80.962303 72.153999 1.473800 81.587097 73.755699 1.741200 81.183800 73.755798 2.494400 80.647400 73.755798 3.147400 80.165199 72.153999 2.665200 80.165100 72.153900 2.665100 80.620102 72.153999 2.111800 81.183701 73.755699 2.494400 80.647400 73.755798 3.147400 79.994400 73.755798 3.683800 79.611801 72.153999 3.120100 79.611801 72.153900 3.120100 80.165199 72.153999 2.665200 80.647301 73.755699 3.147300 79.994400 73.755798 3.683800 79.241302 73.755798 4.087200 78.973801 72.153999 3.462300 78.973801 72.153900 3.462300 79.611801 72.153999 3.120100 79.994400 73.755699 3.683700 79.241302 73.755798 4.087200 78.404297 73.755798 4.341300 78.265099 72.153999 3.677900 78.264999 72.153900 3.677900 78.973801 72.153999 3.462300 79.241302 73.755699 4.087200 78.404297 73.755798 4.341300 77.500000 73.755798 4.429600 77.500000 72.153999 3.752900 77.500000 72.153900 3.752800 78.265099 72.153999 3.677900 78.404198 73.755699 4.341300 81.252899 72.153999 0.000000 81.177902 72.153999 0.765100 81.910004 70.750000 0.916800 81.910004 70.750000 0.916800 82.000000 70.750000 0.000000 81.252800 72.153900 0.000000 81.177902 72.153999 0.765100 80.962303 72.153999 1.473800 81.651199 70.750000 1.766200 81.651100 70.750000 1.766100 81.910004 70.750000 0.916800 81.177902 72.153900 0.765100 80.962303 72.153999 1.473800 80.620102 72.153999 2.111800 81.240601 70.750000 2.531200 81.240601 70.750000 2.531100 81.651199 70.750000 1.766200 80.962303 72.153900 1.473700 80.620102 72.153999 2.111800 80.165199 72.153999 2.665200 80.694901 70.750000 3.194900 80.694901 70.750000 3.194900 81.240601 70.750000 2.531200 80.620102 72.153900 2.111700 80.165199 72.153999 2.665200 79.611801 72.153999 3.120100 80.031197 70.750000 3.740600 80.031097 70.750000 3.740600 80.694901 70.750000 3.194900 80.165100 72.153900 2.665100 79.611801 72.153999 3.120100 78.973801 72.153999 3.462300 79.266197 70.750000 4.151200 79.266098 70.750000 4.151100 80.031197 70.750000 3.740600 79.611801 72.153900 3.120100 78.973801 72.153999 3.462300 78.265099 72.153999 3.677900 78.416801 70.750000 4.409900 78.416801 70.750000 4.409900 79.266197 70.750000 4.151200 78.973801 72.153900 3.462300 78.265099 72.153999 3.677900 77.500000 72.153999 3.752900 77.500000 70.750000 4.499900 77.500000 70.750000 4.499800 78.416801 70.750000 4.409900 78.264999 72.153900 3.677900 77.500000 10.000000 0.000000 88.155602 10.075800 0.000000 87.942398 10.075700 2.171000 77.500000 10.000000 0.000000 87.942497 10.075800 2.171000 87.329803 10.075700 4.182300 77.500000 10.000000 0.000000 87.329803 10.075800 4.182300 86.357300 10.075700 5.993700 77.500000 10.000000 0.000000 86.357399 10.075800 5.993700 85.065300 10.075700 7.565400 77.500000 10.000000 0.000000 85.065399 10.075800 7.565500 83.493599 10.075700 8.857500 77.500000 10.000000 0.000000 83.493698 10.075800 8.857500 81.682198 10.075700 9.829700 77.500000 10.000000 0.000000 81.682297 10.075800 9.829800 79.670898 10.075700 10.442500 79.670998 10.075800 10.442500 95.923798 10.290000 3.830400 87.942497 10.075800 2.171000 88.155602 10.075800 0.000000 88.155602 10.075700 0.000000 96.299797 10.290000 0.000000 95.923698 10.289900 3.830300 94.842796 10.290000 7.378900 87.329803 10.075800 4.182300 87.942497 10.075800 2.171000 87.942398 10.075700 2.171000 95.923798 10.290000 3.830400 94.842697 10.289900 7.378900 93.127296 10.290000 10.574800 86.357399 10.075800 5.993700 87.329803 10.075800 4.182300 87.329803 10.075700 4.182300 94.842796 10.290000 7.378900 93.127197 10.289900 10.574700 90.847801 10.290000 13.347800 85.065399 10.075800 7.565500 86.357399 10.075800 5.993700 86.357300 10.075700 5.993700 93.127296 10.290000 10.574800 90.847801 10.289900 13.347800 88.074799 10.290000 15.627300 83.493698 10.075800 8.857500 85.065399 10.075800 7.565500 85.065300 10.075700 7.565400 90.847801 10.290000 13.347800 88.074699 10.289900 15.627300 84.878899 10.290000 17.342800 81.682297 10.075800 9.829800 83.493698 10.075800 8.857500 83.493599 10.075700 8.857500 88.074799 10.290000 15.627300 84.878799 10.289900 17.342800 81.330399 10.290000 18.423800 79.670998 10.075800 10.442500 81.682297 10.075800 9.829800 81.682198 10.075700 9.829700 84.878899 10.290000 17.342800 81.330299 10.289900 18.423700 79.670998 10.075800 10.442500 79.670898 10.075700 10.442500 81.330399 10.290000 18.423800 77.500000 10.289900 18.799801 101.773003 10.622900 5.046600 95.923798 10.290000 3.830400 96.299797 10.290000 0.000000 96.299698 10.289900 0.000000 102.268997 10.622900 0.000000 101.773003 10.622900 5.046500 100.348999 10.622900 9.721700 94.842796 10.290000 7.378900 95.923798 10.290000 3.830400 95.923698 10.289900 3.830300 101.773003 10.622900 5.046600 100.348999 10.622900 9.721600 98.088898 10.622900 13.932300 93.127296 10.290000 10.574800 94.842796 10.290000 7.378900 94.842697 10.289900 7.378900 100.348999 10.622900 9.721700 98.088799 10.622900 13.932200 95.085701 10.622900 17.585699 90.847801 10.290000 13.347800 93.127296 10.290000 10.574800 93.127197 10.289900 10.574700 98.088898 10.622900 13.932300 95.085701 10.622900 17.585600 91.432297 10.622900 20.588900 88.074799 10.290000 15.627300 90.847801 10.290000 13.347800 90.847801 10.289900 13.347800 95.085701 10.622900 17.585699 91.432198 10.622900 20.588800 87.221703 10.622900 22.849100 84.878899 10.290000 17.342800 88.074799 10.290000 15.627300 88.074699 10.289900 15.627300 91.432297 10.622900 20.588900 87.221703 10.622900 22.849100 82.546600 10.622900 24.273300 81.330399 10.290000 18.423800 84.878899 10.290000 17.342800 84.878799 10.289900 17.342800 87.221703 10.622900 22.849100 82.546600 10.622900 24.273300 81.330399 10.290000 18.423800 81.330299 10.289900 18.423700 82.546600 10.622900 24.273300 105.820000 11.054600 5.888000 101.773003 10.622900 5.046600 102.268997 10.622900 0.000000 102.268997 10.622900 0.000000 106.398003 11.054600 0.000000 105.820000 11.054500 5.888000 104.158997 11.054600 11.342600 100.348999 10.622900 9.721700 101.773003 10.622900 5.046600 101.773003 10.622900 5.046500 105.820000 11.054600 5.888000 104.158997 11.054500 11.342500 101.522003 11.054600 16.255301 98.088898 10.622900 13.932300 100.348999 10.622900 9.721700 100.348999 10.622900 9.721600 104.158997 11.054600 11.342600 101.522003 11.054500 16.255301 98.017799 11.054600 20.517799 95.085701 10.622900 17.585699 98.088898 10.622900 13.932300 98.088799 10.622900 13.932200 101.522003 11.054600 16.255301 98.017700 11.054500 20.517700 93.755302 11.054600 24.021799 91.432297 10.622900 20.588900 95.085701 10.622900 17.585699 95.085701 10.622900 17.585600 98.017799 11.054600 20.517799 93.755302 11.054500 24.021700 88.842598 11.054600 26.658800 91.432198 10.622900 20.588800 93.755302 11.054600 24.021799 88.842499 11.054500 26.658800 83.388000 11.054600 28.320400 83.388000 11.054500 28.320400 77.500000 11.054500 28.898300 108.394997 11.565500 6.423200 105.820000 11.054600 5.888000 106.398003 11.054600 0.000000 106.398003 11.054500 0.000000 109.025002 11.565500 0.000000 108.394997 11.565500 6.423200 106.582001 11.565500 12.373600 104.158997 11.054600 11.342600 105.820000 11.054600 5.888000 105.820000 11.054500 5.888000 106.582001 11.565500 12.373600 103.705002 11.565500 17.732901 101.522003 11.054600 16.255301 104.158997 11.054600 11.342600 104.158997 11.054500 11.342500 106.582001 11.565500 12.373600 103.705002 11.565500 17.732901 99.882896 11.565500 22.382900 98.017799 11.054600 20.517799 101.522003 11.054600 16.255301 101.522003 11.054500 16.255301 103.705002 11.565500 17.732901 99.882797 11.565500 22.382900 95.232903 11.565500 26.205299 93.755302 11.054600 24.021799 98.017799 11.054600 20.517799 98.017700 11.054500 20.517700 99.882896 11.565500 22.382900 95.232903 11.565500 26.205200 89.873596 11.565500 29.082001 88.842598 11.054600 26.658800 93.755302 11.054600 24.021799 93.755302 11.054500 24.021700 95.232903 11.565500 26.205299 89.873497 11.565500 29.082001 83.923203 11.565500 30.894699 83.388000 11.054600 28.320400 88.842598 11.054600 26.658800 88.842499 11.054500 26.658800 89.873596 11.565500 29.082001 83.923203 11.565500 30.894600 83.388000 11.054600 28.320400 83.388000 11.054500 28.320400 83.923203 11.565500 30.894699 77.500000 11.565500 31.525101 109.825996 12.135700 6.720700 109.025002 11.565500 0.000000 110.485001 12.135700 0.000000 109.825996 12.135700 6.720600 107.929001 12.135700 12.946700 104.918999 12.135700 18.554199 104.918999 12.135700 18.554100 100.919998 12.135700 23.419500 99.882896 11.565500 22.382900 96.054199 12.135700 27.419001 95.232903 11.565500 26.205299 99.882896 11.565500 22.382900 99.882797 11.565500 22.382900 100.919998 12.135700 23.419500 96.054100 12.135700 27.419001 90.446701 12.135700 30.428900 89.873596 11.565500 29.082001 95.232903 11.565500 26.205299 95.232903 11.565500 26.205200 96.054199 12.135700 27.419001 90.446701 12.135700 30.428801 84.220703 12.135700 32.325600 83.923203 11.565500 30.894699 89.873596 11.565500 29.082001 89.873497 11.565500 29.082001 90.446701 12.135700 30.428900 84.220703 12.135700 32.325500 83.923203 11.565500 30.894699 83.923203 11.565500 30.894600 84.220703 12.135700 32.325600 77.500000 12.135700 32.985199 110.442001 12.745400 6.849000 109.825996 12.135700 6.720700 110.485001 12.135700 0.000000 110.485001 12.135700 0.000000 111.114998 12.745400 0.000000 110.442001 12.745400 6.848900 108.510002 12.745400 13.193800 109.825996 12.135700 6.720600 110.442001 12.745400 6.849000 108.510002 12.745400 13.193700 105.442001 12.745400 18.908300 104.918999 12.135700 18.554199 101.366997 12.745400 23.866501 104.918999 12.135700 18.554100 101.366997 12.745400 23.866501 96.408302 12.745400 27.942301 96.054199 12.135700 27.419001 90.693802 12.745400 31.009701 90.446701 12.135700 30.428900 96.054199 12.135700 27.419001 96.054100 12.135700 27.419001 90.693802 12.745400 31.009701 84.348999 12.745400 32.942501 84.220703 12.135700 32.325600 90.446701 12.135700 30.428900 90.446701 12.135700 30.428801 90.693802 12.745400 31.009701 84.348900 12.745400 32.942501 84.220703 12.135700 32.325600 84.220703 12.135700 32.325500 84.348999 12.745400 32.942501 77.500000 12.745400 33.614700 110.574997 13.375000 6.876500 110.442001 12.745400 6.849000 111.114998 12.745400 0.000000 111.250000 13.375000 0.000000 108.634003 13.375000 13.246800 108.510002 12.745400 13.193800 110.442001 12.745400 6.849000 110.442001 12.745400 6.848900 110.574997 13.375000 6.876500 108.634003 13.375000 13.246800 105.555000 13.375000 18.984301 108.510002 12.745400 13.193700 108.634003 13.375000 13.246800 105.555000 13.375000 18.984301 101.462997 13.375000 23.962400 96.484299 13.375000 28.054600 96.484200 13.375000 28.054501 90.746803 13.375000 31.134300 84.376503 13.375000 33.075001 84.348999 12.745400 32.942501 84.348999 12.745400 32.942501 84.348900 12.745400 32.942501 84.376503 13.375000 33.075001 77.500000 13.375000 33.750000 84.218300 69.629303 1.396700 84.218201 69.629303 1.396700 84.355400 69.629303 0.000000 82.000000 70.750000 0.000000 81.910004 70.750000 0.916800 81.651199 70.750000 1.766200 83.824097 69.629303 2.690700 83.823997 69.629303 2.690700 81.651199 70.750000 1.766200 81.240601 70.750000 2.531200 83.198601 69.629402 3.856100 83.198601 69.629402 3.856100 83.824097 69.629303 2.690700 81.651100 70.750000 1.766100 82.367302 69.629303 4.867300 82.367302 69.629303 4.867300 83.198601 69.629402 3.856100 81.240601 70.750000 2.531100 80.694901 70.750000 3.194900 80.031197 70.750000 3.740600 81.356201 69.629303 5.698600 81.356201 69.629303 5.698500 80.031197 70.750000 3.740600 79.266197 70.750000 4.151200 80.190697 69.629303 6.324100 80.190598 69.629303 6.324100 81.356201 69.629303 5.698600 80.031097 70.750000 3.740600 78.896698 69.629303 6.718300 78.896599 69.629303 6.718200 80.190697 69.629303 6.324100 79.266098 70.750000 4.151100 84.355400 69.629303 0.000000 84.218300 69.629303 1.396700 87.560303 68.746002 2.091600 87.560303 68.746002 2.091500 87.765602 68.746002 0.000000 84.355400 69.629303 0.000000 84.218300 69.629303 1.396700 83.824097 69.629303 2.690700 86.970001 68.746002 4.029200 86.970001 68.746002 4.029200 87.560303 68.746002 2.091600 84.218201 69.629303 1.396700 86.033302 68.746002 5.774400 86.033302 68.746002 5.774400 86.970001 68.746002 4.029200 83.823997 69.629303 2.690700 84.788498 68.746002 7.288500 84.788399 68.746002 7.288400 82.367302 69.629303 4.867300 81.356201 69.629303 5.698600 83.274399 68.746002 8.533200 83.274300 68.746002 8.533200 81.356201 69.629303 5.698600 80.190697 69.629303 6.324100 81.529198 68.746002 9.470000 81.529099 68.746002 9.470000 83.274399 68.746002 8.533200 81.356201 69.629303 5.698500 80.190697 69.629303 6.324100 78.896698 69.629303 6.718300 79.591599 68.746002 10.060300 79.591499 68.746002 10.060200 81.529198 68.746002 9.470000 80.190598 69.629303 6.324100 79.591599 68.746002 10.060300 78.896599 69.629303 6.718200 87.765602 68.746002 0.000000 87.560303 68.746002 2.091600 91.522400 68.020897 2.915300 91.522301 68.020798 2.915200 91.808502 68.020897 0.000000 87.765602 68.746002 0.000000 90.699600 68.020897 5.616100 90.699600 68.020798 5.616000 91.522400 68.020897 2.915300 87.560303 68.746002 2.091500 89.393997 68.020897 8.048500 89.393898 68.020798 8.048500 86.033302 68.746002 5.774400 84.788498 68.746002 7.288500 87.659103 68.020897 10.159000 87.659103 68.020798 10.159000 84.788498 68.746002 7.288500 83.274399 68.746002 8.533200 85.548500 68.020897 11.894000 85.548500 68.020798 11.894000 87.659103 68.020897 10.159000 84.788399 68.746002 7.288400 83.274399 68.746002 8.533200 81.529198 68.746002 9.470000 83.116096 68.020897 13.199600 83.115997 68.020798 13.199600 85.548500 68.020897 11.894000 83.274300 68.746002 8.533200 81.529198 68.746002 9.470000 79.591599 68.746002 10.060300 80.415298 68.020897 14.022400 80.415199 68.020798 14.022300 83.116096 68.020897 13.199600 81.529099 68.746002 9.470000 77.500000 68.020798 14.308500 80.415298 68.020897 14.022400 79.591499 68.746002 10.060200 91.808502 68.020897 0.000000 91.522400 68.020897 2.915300 95.691200 67.375000 3.782100 95.691200 67.375000 3.782000 96.062500 67.375000 0.000000 91.808502 68.020798 0.000000 91.522400 68.020897 2.915300 90.699600 68.020897 5.616100 94.623901 67.375000 7.285700 94.623901 67.375000 7.285600 95.691200 67.375000 3.782100 91.522301 68.020798 2.915200 90.699600 68.020897 5.616100 89.393997 68.020897 8.048500 92.930000 67.375000 10.441400 92.930000 67.375000 10.441300 94.623901 67.375000 7.285700 90.699600 68.020798 5.616000 89.393997 68.020897 8.048500 87.659103 68.020897 10.159000 90.679298 67.375000 13.179300 90.679199 67.375000 13.179300 92.930000 67.375000 10.441400 89.393898 68.020798 8.048500 87.659103 68.020897 10.159000 85.548500 68.020897 11.894000 87.941399 67.375000 15.430000 87.941299 67.375000 15.430000 90.679298 67.375000 13.179300 87.659103 68.020798 10.159000 85.548500 68.020897 11.894000 83.116096 68.020897 13.199600 84.785698 67.375000 17.123899 84.785599 67.375000 17.123800 87.941399 67.375000 15.430000 85.548500 68.020798 11.894000 83.116096 68.020897 13.199600 80.415298 68.020897 14.022400 81.282097 67.375000 18.191200 81.281998 67.375000 18.191200 84.785698 67.375000 17.123899 83.115997 68.020798 13.199600 80.415298 68.020897 14.022400 81.282097 67.375000 18.191200 80.415199 68.020798 14.022300 96.062500 67.375000 0.000000 95.691200 67.375000 3.782100 99.653297 66.728996 4.605800 99.653198 66.728897 4.605800 100.105003 66.728996 0.000000 96.062500 67.375000 0.000000 95.691200 67.375000 3.782100 94.623901 67.375000 7.285700 98.353500 66.728996 8.872600 98.353500 66.728897 8.872500 99.653297 66.728996 4.605800 95.691200 67.375000 3.782000 94.623901 67.375000 7.285700 92.930000 67.375000 10.441400 96.290703 66.728996 12.715500 96.290703 66.728897 12.715400 98.353500 66.728996 8.872600 94.623901 67.375000 7.285600 92.930000 67.375000 10.441400 90.679298 67.375000 13.179300 93.549797 66.728996 16.049801 93.549698 66.728897 16.049801 96.290703 66.728996 12.715500 92.930000 67.375000 10.441300 90.679298 67.375000 13.179300 87.941399 67.375000 15.430000 90.215500 66.728996 18.790701 90.215401 66.728897 18.790701 93.549797 66.728996 16.049801 90.679199 67.375000 13.179300 87.941399 67.375000 15.430000 84.785698 67.375000 17.123899 86.372597 66.728996 20.853500 86.372498 66.728897 20.853500 90.215500 66.728996 18.790701 87.941299 67.375000 15.430000 84.785698 67.375000 17.123899 81.282097 67.375000 18.191200 82.105797 66.728996 22.153299 82.105698 66.728897 22.153200 86.372597 66.728996 20.853500 84.785599 67.375000 17.123800 77.500000 66.728897 22.605400 82.105797 66.728996 22.153299 81.281998 67.375000 18.191200 100.105003 66.728996 0.000000 99.653297 66.728996 4.605800 102.995003 66.003899 5.300600 102.995003 66.003799 5.300600 103.515999 66.003899 0.000000 100.105003 66.728897 0.000000 99.653297 66.728996 4.605800 98.353500 66.728996 8.872600 101.499001 66.003899 10.211100 101.499001 66.003799 10.211000 102.995003 66.003899 5.300600 99.653198 66.728897 4.605800 98.353500 66.728996 8.872600 96.290703 66.728996 12.715500 99.125397 66.003899 14.633700 99.125298 66.003799 14.633700 101.499001 66.003899 10.211100 98.353500 66.728897 8.872500 96.290703 66.728996 12.715500 93.549797 66.728996 16.049801 95.971001 66.003899 18.471001 95.971001 66.003799 18.471001 99.125397 66.003899 14.633700 96.290703 66.728897 12.715400 93.549797 66.728996 16.049801 90.215500 66.728996 18.790701 92.133698 66.003899 21.625401 92.133598 66.003799 21.625401 95.971001 66.003899 18.471001 93.549698 66.728897 16.049801 90.215500 66.728996 18.790701 86.372597 66.728996 20.853500 87.711098 66.003899 23.999399 87.710999 66.003799 23.999300 92.133698 66.003899 21.625401 90.215401 66.728897 18.790701 86.372597 66.728996 20.853500 82.105797 66.728996 22.153299 82.800598 66.003899 25.495300 82.800499 66.003799 25.495300 87.711098 66.003899 23.999399 86.372498 66.728897 20.853500 82.105797 66.728996 22.153299 77.500000 66.003799 26.015600 82.800598 66.003899 25.495300 82.105698 66.728897 22.153200 103.515999 66.003899 0.000000 102.995003 66.003899 5.300600 105.304001 65.120598 5.780600 105.303001 65.120499 5.780600 105.871002 65.120598 0.000000 103.515999 66.003799 0.000000 102.995003 66.003899 5.300600 101.499001 66.003899 10.211100 103.671997 65.120598 11.135600 103.671997 65.120499 11.135600 105.304001 65.120598 5.780600 102.995003 66.003799 5.300600 101.499001 66.003899 10.211100 99.125397 66.003899 14.633700 101.083000 65.120598 15.958700 101.083000 65.120499 15.958700 103.671997 65.120598 11.135600 101.499001 66.003799 10.211000 99.125397 66.003899 14.633700 95.971001 66.003899 18.471001 97.643402 65.120598 20.143400 97.643402 65.120499 20.143400 101.083000 65.120598 15.958700 99.125298 66.003799 14.633700 95.971001 66.003899 18.471001 92.133698 66.003899 21.625401 93.458702 65.120598 23.583401 93.458702 65.120499 23.583401 97.643402 65.120598 20.143400 95.971001 66.003799 18.471001 92.133698 66.003899 21.625401 87.711098 66.003899 23.999399 88.635597 65.120598 26.172300 88.635498 65.120499 26.172300 93.458702 65.120598 23.583401 92.133598 66.003799 21.625401 87.711098 66.003899 23.999399 82.800598 66.003899 25.495300 83.280602 65.120598 27.803600 83.280602 65.120499 27.803600 88.635597 65.120598 26.172300 87.710999 66.003799 23.999300 82.800598 66.003899 25.495300 77.500000 65.120499 28.371000 83.280602 65.120598 27.803600 82.800499 66.003799 25.495300 105.871002 65.120598 0.000000 105.304001 65.120598 5.780600 106.165001 64.000000 5.959600 106.165001 64.000000 5.959500 106.750000 64.000000 0.000000 105.871002 65.120499 0.000000 105.304001 65.120598 5.780600 103.671997 65.120598 11.135600 104.483002 64.000000 11.480600 104.483002 64.000000 11.480600 106.165001 64.000000 5.959600 105.303001 65.120499 5.780600 103.671997 65.120598 11.135600 101.083000 65.120598 15.958700 101.814003 64.000000 16.453100 101.814003 64.000000 16.453100 104.483002 64.000000 11.480600 103.671997 65.120499 11.135600 101.083000 65.120598 15.958700 97.643402 65.120598 20.143400 98.267502 64.000000 20.767401 98.267502 64.000000 20.767401 101.814003 64.000000 16.453100 101.083000 65.120499 15.958700 97.643402 65.120598 20.143400 93.458702 65.120598 23.583401 93.953102 64.000000 24.313999 93.953102 64.000000 24.313900 98.267502 64.000000 20.767401 97.643402 65.120499 20.143400 93.458702 65.120598 23.583401 88.635597 65.120598 26.172300 88.980598 64.000000 26.983101 88.980499 64.000000 26.983101 93.953102 64.000000 24.313999 93.458702 65.120499 23.583401 88.635597 65.120598 26.172300 83.280602 65.120598 27.803600 83.459602 64.000000 28.664900 83.459602 64.000000 28.664801 88.980598 64.000000 26.983101 88.635498 65.120499 26.172300 83.280602 65.120598 27.803600 77.500000 64.000000 29.249901 83.459602 64.000000 28.664900 83.280602 65.120499 27.803600 111.250000 64.000000 0.000000 110.574997 64.000000 6.876500 112.630997 59.573601 7.304100 113.348000 59.573601 0.000000 108.634003 64.000000 13.246800 110.570000 59.573601 14.070400 108.634003 64.000000 13.246800 105.555000 64.000000 18.984301 107.299004 59.573601 20.164700 107.299004 59.573601 20.164600 105.555000 64.000000 18.984301 101.462997 64.000000 23.962400 102.952003 59.573601 25.452299 102.952003 59.573601 25.452200 96.484299 64.000000 28.054600 97.664703 59.573601 29.798901 96.484299 64.000000 28.054600 90.746803 64.000000 31.134300 91.570396 59.573601 33.070099 91.570297 59.573601 33.070000 97.664703 59.573601 29.798901 96.484200 64.000000 28.054501 84.376503 64.000000 33.074902 84.804001 59.573601 35.131401 114.623001 55.166901 7.718200 115.380997 55.166901 0.000000 112.445000 55.166901 14.868200 110.570000 59.573601 14.070400 107.299004 59.573601 20.164700 108.987999 55.166901 21.307899 108.987999 55.166901 21.307800 107.299004 59.573601 20.164700 102.952003 59.573601 25.452299 104.394997 55.166901 26.895399 104.394997 55.166901 26.895300 108.987999 55.166901 21.307899 107.299004 59.573601 20.164600 98.807899 55.166901 31.488400 98.807800 55.166901 31.488300 104.394997 55.166901 26.895399 102.952003 59.573601 25.452200 97.664703 59.573601 29.798901 91.570396 59.573601 33.070099 92.368202 55.166901 34.945000 92.368202 55.166901 34.944901 85.218201 55.166901 37.123199 85.218201 55.166901 37.123100 92.368202 55.166901 34.945000 91.570297 59.573601 33.070000 77.500000 55.166901 37.880699 116.486000 50.799900 8.105400 117.280998 50.799900 0.000000 114.197998 50.799900 15.614200 114.197998 50.799900 15.614100 108.987999 55.166901 21.307899 110.568001 50.799900 22.377001 108.987999 55.166901 21.307899 104.394997 55.166901 26.895399 105.745003 50.799900 28.244801 105.745003 50.799900 28.244801 110.568001 50.799900 22.377001 108.987999 55.166901 21.307800 104.394997 55.166901 26.895399 98.807899 55.166901 31.488400 99.876999 50.799900 33.068298 99.876900 50.799900 33.068199 105.745003 50.799900 28.244801 104.394997 55.166901 26.895300 98.807899 55.166901 31.488400 92.368202 55.166901 34.945000 93.114197 50.799900 36.698399 93.114098 50.799900 36.698299 99.876999 50.799900 33.068298 98.807800 55.166901 31.488300 92.368202 55.166901 34.945000 85.218201 55.166901 37.123199 85.605400 50.799900 38.985802 85.605400 50.799900 38.985802 93.114197 50.799900 36.698399 92.368202 55.166901 34.944901 85.218201 55.166901 37.123199 77.500000 55.166901 37.880798 77.500000 50.799900 39.781399 77.500000 50.799900 39.781300 85.605400 50.799900 38.985802 85.218201 55.166901 37.123100 118.154999 46.492100 8.452400 118.154999 46.492001 8.452400 118.984001 46.492100 0.000000 117.280998 50.799900 0.000000 116.486000 50.799900 8.105400 114.197998 50.799900 15.614200 115.768997 46.492100 16.282600 115.768997 46.492001 16.282600 111.984001 46.492100 23.334900 111.984001 46.492001 23.334801 115.768997 46.492100 16.282600 114.197998 50.799900 15.614100 106.954002 46.492100 29.453899 106.954002 46.492001 29.453800 105.745003 50.799900 28.244801 99.876999 50.799900 33.068298 100.834999 46.492100 34.483799 100.834999 46.492001 34.483700 99.876999 50.799900 33.068298 93.114197 50.799900 36.698399 93.782600 46.492100 38.269299 93.782600 46.492001 38.269199 100.834999 46.492100 34.483799 99.876900 50.799900 33.068199 85.952400 46.492100 40.654598 85.952400 46.492001 40.654499 93.782600 46.492100 38.269299 93.114098 50.799900 36.698299 85.605400 50.799900 38.985802 77.500000 50.799900 39.781399 77.500000 46.492100 41.484299 77.500000 46.492001 41.484200 118.984001 46.492100 0.000000 118.154999 46.492100 8.452400 119.565002 42.263500 8.745600 119.565002 42.263500 8.745500 120.424004 42.263500 0.000000 118.984001 46.492001 0.000000 118.154999 46.492100 8.452400 115.768997 46.492100 16.282600 117.097000 42.263500 16.847500 117.097000 42.263500 16.847401 119.565002 42.263500 8.745600 118.154999 46.492001 8.452400 115.768997 46.492100 16.282600 111.984001 46.492100 23.334900 113.180000 42.263500 24.144501 113.180000 42.263500 24.144501 117.097000 42.263500 16.847500 115.768997 46.492001 16.282600 111.984001 46.492100 23.334900 106.954002 46.492100 29.453899 107.975998 42.263500 30.475700 107.975998 42.263500 30.475700 111.984001 46.492001 23.334801 106.954002 46.492100 29.453899 100.834999 46.492100 34.483799 101.644997 42.263500 35.680199 101.644997 42.263500 35.680099 107.975998 42.263500 30.475700 106.954002 46.492001 29.453800 100.834999 46.492100 34.483799 93.782600 46.492100 38.269299 94.347504 42.263500 39.597000 94.347504 42.263500 39.597000 101.644997 42.263500 35.680199 100.834999 46.492001 34.483700 93.782600 46.492100 38.269299 85.952400 46.492100 40.654598 86.245598 42.263500 42.065102 86.245499 42.263500 42.065102 94.347504 42.263500 39.597000 93.782600 46.492001 38.269199 85.952400 46.492100 40.654598 77.500000 46.492100 41.484299 77.500000 42.263500 42.923500 86.245598 42.263500 42.065102 85.952400 46.492001 40.654499 120.424004 42.263500 0.000000 119.565002 42.263500 8.745600 120.652000 38.133701 8.971700 120.652000 38.133701 8.971600 121.532997 38.133701 0.000000 120.424004 42.263500 0.000000 119.565002 42.263500 8.745600 117.097000 42.263500 16.847500 118.121002 38.133701 17.283001 118.120003 38.133701 17.283001 120.652000 38.133701 8.971700 119.565002 42.263500 8.745500 114.102997 38.133701 24.768600 118.121002 38.133701 17.283001 117.097000 42.263500 16.847401 108.763000 38.133701 31.263500 101.644997 42.263500 35.680199 102.268997 38.133701 36.602600 94.782997 38.133701 40.620602 94.782898 38.133701 40.620602 102.268997 38.133701 36.602600 101.644997 42.263500 35.680099 86.245598 42.263500 42.065102 86.471703 38.133701 43.152500 77.500000 38.133701 44.033100 86.471703 38.133701 43.152500 86.245499 42.263500 42.065102 121.532997 38.133701 0.000000 120.652000 38.133701 8.971700 121.351997 34.122601 9.117200 121.351997 34.122601 9.117100 122.247002 34.122601 0.000000 121.532997 38.133701 0.000000 120.652000 38.133701 8.971700 118.121002 38.133701 17.283001 118.778999 34.122601 17.563299 118.778999 34.122601 17.563200 121.351997 34.122601 9.117200 120.652000 38.133701 8.971600 114.695999 34.122601 25.170300 114.695999 34.122601 25.170200 118.778999 34.122601 17.563299 118.120003 38.133701 17.283001 109.271004 34.122601 31.770500 102.669998 34.122601 37.196201 94.782997 38.133701 40.620602 95.063301 34.122601 41.279301 86.617203 34.122601 43.852299 86.617203 34.122601 43.852200 95.063301 34.122601 41.279301 94.782898 38.133701 40.620602 86.471703 38.133701 43.152500 77.500000 38.133701 44.033199 77.500000 34.122601 44.747299 77.500000 34.122601 44.747200 122.247002 34.122601 0.000000 121.351997 34.122601 9.117200 121.599998 30.250000 9.168700 122.500000 30.250000 0.000000 121.351997 34.122601 9.117200 118.778999 34.122601 17.563299 119.012001 30.250000 17.662399 119.012001 30.250000 17.662300 121.599998 30.250000 9.168700 121.351997 34.122601 9.117100 118.778999 34.122601 17.563299 114.695999 34.122601 25.170300 114.905998 30.250000 25.312500 114.905998 30.250000 25.312500 119.012001 30.250000 17.662399 118.778999 34.122601 17.563200 109.449997 30.250000 31.950001 109.449997 30.250000 31.950001 114.905998 30.250000 25.312500 114.695999 34.122601 25.170200 102.813004 30.250000 37.406200 95.162399 30.250000 41.512501 95.162300 30.250000 41.512501 95.063301 34.122601 41.279301 86.617203 34.122601 43.852299 86.668701 30.250000 44.099899 86.668701 30.250000 44.099800 86.617203 34.122601 43.852299 77.500000 34.122601 44.747299 77.500000 30.250000 45.000000 86.668701 30.250000 44.099899 86.617203 34.122601 43.852200 121.125999 26.687099 9.070200 121.125999 26.687000 9.070100 122.016998 26.687099 0.000000 122.500000 30.250000 0.000000 121.599998 30.250000 9.168700 119.012001 30.250000 17.662399 118.566002 26.687099 17.472700 118.566002 26.687000 17.472700 114.503998 26.687099 25.040501 114.503998 26.687000 25.040501 118.566002 26.687099 17.472700 119.012001 30.250000 17.662300 109.107002 26.687099 31.606701 109.107002 26.687000 31.606701 102.540001 26.687099 37.004398 102.540001 26.687000 37.004299 102.813004 30.250000 37.406200 95.162399 30.250000 41.512501 94.972702 26.687099 41.066502 94.972702 26.687000 41.066502 95.162399 30.250000 41.512501 86.668701 30.250000 44.099899 86.570198 26.687099 43.626202 86.570099 26.687000 43.626202 94.972702 26.687099 41.066502 95.162300 30.250000 41.512501 77.500000 26.687000 44.516602 86.570198 26.687099 43.626202 86.668701 30.250000 44.099800 122.016998 26.687099 0.000000 121.125999 26.687099 9.070200 119.876999 23.579100 8.810500 119.876999 23.579000 8.810500 120.741997 23.579100 0.000000 122.016998 26.687000 0.000000 121.125999 26.687099 9.070200 118.566002 26.687099 17.472700 117.390999 23.579100 16.972500 117.390999 23.579000 16.972401 119.876999 23.579100 8.810500 121.125999 26.687000 9.070100 118.566002 26.687099 17.472700 114.503998 26.687099 25.040501 113.445000 23.579100 24.323700 113.445000 23.579000 24.323601 117.390999 23.579100 16.972500 118.566002 26.687000 17.472700 114.503998 26.687099 25.040501 109.107002 26.687099 31.606701 108.202003 23.579100 30.701900 108.202003 23.579000 30.701900 113.445000 23.579100 24.323700 114.503998 26.687000 25.040501 109.107002 26.687099 31.606701 102.540001 26.687099 37.004398 101.823997 23.579100 35.945000 101.823997 23.579000 35.944901 108.202003 23.579100 30.701900 109.107002 26.687000 31.606701 102.540001 26.687099 37.004398 94.972702 26.687099 41.066502 94.472504 23.579100 39.890900 94.472504 23.579000 39.890800 101.823997 23.579100 35.945000 102.540001 26.687000 37.004299 94.972702 26.687099 41.066502 86.570198 26.687099 43.626202 86.310501 23.579100 42.377300 86.310501 23.579000 42.377300 94.472504 23.579100 39.890900 94.972702 26.687000 41.066502 86.570198 26.687099 43.626202 77.500000 23.579000 43.242001 86.310501 23.579100 42.377300 86.570099 26.687000 43.626202 120.741997 23.579100 0.000000 119.876999 23.579100 8.810500 118.112000 20.906099 8.443400 118.112000 20.906000 8.443400 118.940002 20.906099 0.000000 120.741997 23.579000 0.000000 119.876999 23.579100 8.810500 117.390999 23.579100 16.972500 115.728996 20.906099 16.265301 115.728996 20.906000 16.265301 118.112000 20.906099 8.443400 119.876999 23.579000 8.810500 117.390999 23.579100 16.972500 113.445000 23.579100 24.323700 111.946999 20.906099 23.310200 111.946999 20.906000 23.310101 115.728996 20.906099 16.265301 117.390999 23.579000 16.972401 113.445000 23.579100 24.323700 108.202003 23.579100 30.701900 106.922997 20.906099 29.422701 106.922997 20.906000 29.422701 111.946999 20.906099 23.310200 113.445000 23.579000 24.323601 108.202003 23.579100 30.701900 101.823997 23.579100 35.945000 100.809998 20.906099 34.447300 100.809998 20.906000 34.447201 106.922997 20.906099 29.422701 108.202003 23.579000 30.701900 101.823997 23.579100 35.945000 94.472504 23.579100 39.890900 93.765297 20.906099 38.228699 93.765198 20.906000 38.228600 100.809998 20.906099 34.447300 101.823997 23.579000 35.944901 94.472504 23.579100 39.890900 86.310501 23.579100 42.377300 85.943398 20.906099 40.611599 85.943298 20.906000 40.611500 93.765297 20.906099 38.228699 94.472504 23.579000 39.890800 86.310501 23.579100 42.377300 77.500000 20.906000 41.440300 85.943398 20.906099 40.611599 86.310501 23.579000 42.377300 118.940002 20.906099 0.000000 118.112000 20.906099 8.443400 116.087997 18.648399 8.022600 116.087997 18.648300 8.022600 116.875000 18.648399 0.000000 118.940002 20.906000 0.000000 118.112000 20.906099 8.443400 115.728996 20.906099 16.265301 113.822998 18.648399 15.454600 113.822998 18.648300 15.454600 116.087997 18.648399 8.022600 118.112000 20.906000 8.443400 115.728996 20.906099 16.265301 111.946999 20.906099 23.310200 110.230003 18.648399 22.148399 110.230003 18.648300 22.148300 113.822998 18.648399 15.454600 115.728996 20.906000 16.265301 111.946999 20.906099 23.310200 106.922997 20.906099 29.422701 105.456001 18.648399 27.956200 105.456001 18.648300 27.956100 110.230003 18.648399 22.148399 111.946999 20.906000 23.310101 106.922997 20.906099 29.422701 100.809998 20.906099 34.447300 99.648399 18.648399 32.730400 99.648300 18.648300 32.730400 105.456001 18.648399 27.956200 106.922997 20.906000 29.422701 100.809998 20.906099 34.447300 93.765297 20.906099 38.228699 92.954597 18.648399 36.323399 92.954498 18.648300 36.323299 99.648399 18.648399 32.730400 100.809998 20.906000 34.447201 93.765297 20.906099 38.228699 85.943398 20.906099 40.611599 85.522598 18.648399 38.587502 85.522499 18.648300 38.587502 92.954597 18.648399 36.323399 93.765198 20.906000 38.228600 85.943398 20.906099 40.611599 77.500000 18.648300 39.375000 85.522598 18.648399 38.587502 85.943298 20.906000 40.611500 116.875000 18.648399 0.000000 116.087997 18.648399 8.022600 114.063004 16.786200 7.601800 114.063004 16.786100 7.601700 114.809998 16.786200 0.000000 116.875000 18.648300 0.000000 116.087997 18.648399 8.022600 113.822998 18.648399 15.454600 111.917999 16.786200 14.644000 111.917999 16.786100 14.644000 114.063004 16.786200 7.601800 116.087997 18.648300 8.022600 113.822998 18.648399 15.454600 110.230003 18.648399 22.148399 108.513000 16.786200 20.986601 108.513000 16.786100 20.986601 111.917999 16.786200 14.644000 113.822998 18.648300 15.454600 110.230003 18.648399 22.148399 105.456001 18.648399 27.956200 103.989998 16.786200 26.489700 103.989998 16.786100 26.489700 108.513000 16.786200 20.986601 110.230003 18.648300 22.148300 105.456001 18.648399 27.956200 99.648399 18.648399 32.730400 98.486603 16.786200 31.013500 98.486603 16.786100 31.013500 103.989998 16.786200 26.489700 105.456001 18.648300 27.956100 99.648399 18.648399 32.730400 92.954597 18.648399 36.323399 92.143997 16.786200 34.417999 92.143898 16.786100 34.417900 98.486603 16.786200 31.013500 99.648300 18.648300 32.730400 92.954597 18.648399 36.323399 85.522598 18.648399 38.587502 85.101799 16.786200 36.563301 85.101700 16.786100 36.563301 92.143997 16.786200 34.417999 92.954498 18.648300 36.323299 85.522598 18.648399 38.587502 77.500000 16.786100 37.309502 85.101799 16.786200 36.563301 85.522499 18.648300 38.587502 114.809998 16.786200 0.000000 114.063004 16.786200 7.601800 112.297997 15.299800 7.234700 112.297997 15.299700 7.234700 113.008003 15.299800 0.000000 114.809998 16.786100 0.000000 114.063004 16.786200 7.601800 111.917999 16.786200 14.644000 110.255997 15.299800 13.936800 110.255997 15.299700 13.936800 112.297997 15.299800 7.234700 114.063004 16.786100 7.601700 111.917999 16.786200 14.644000 108.513000 16.786200 20.986601 107.015999 15.299800 19.973101 107.015999 15.299700 19.973101 110.255997 15.299800 13.936800 111.917999 16.786100 14.644000 108.513000 16.786200 20.986601 103.989998 16.786200 26.489700 102.710999 15.299800 25.210501 102.710999 15.299700 25.210501 107.015999 15.299800 19.973101 108.513000 16.786100 20.986601 103.989998 16.786200 26.489700 98.486603 16.786200 31.013500 97.473099 15.299800 29.515800 97.473000 15.299700 29.515800 102.710999 15.299800 25.210501 103.989998 16.786100 26.489700 98.486603 16.786200 31.013500 92.143997 16.786200 34.417999 91.436798 15.299800 32.755901 91.436699 15.299700 32.755901 97.473099 15.299800 29.515800 98.486603 16.786100 31.013500 92.143997 16.786200 34.417999 85.101799 16.786200 36.563301 84.734703 15.299800 34.797600 84.734703 15.299700 34.797501 91.436798 15.299800 32.755901 92.143898 16.786100 34.417900 85.101799 16.786200 36.563301 77.500000 15.299700 35.507801 84.734703 15.299800 34.797600 85.101700 16.786100 36.563301 113.008003 15.299800 0.000000 112.297997 15.299800 7.234700 111.049004 14.169300 6.975000 111.049004 14.169300 6.974900 111.733002 14.169300 0.000000 113.008003 15.299700 0.000000 112.297997 15.299800 7.234700 110.255997 15.299800 13.936800 109.080002 14.169300 13.436600 109.080002 14.169300 13.436500 111.049004 14.169300 6.975000 112.297997 15.299700 7.234700 110.255997 15.299800 13.936800 107.015999 15.299800 19.973101 105.956001 14.169300 19.256201 105.956001 14.169300 19.256201 109.080002 14.169300 13.436600 110.255997 15.299700 13.936800 107.015999 15.299800 19.973101 102.710999 15.299800 25.210501 101.806000 14.169300 24.305700 101.806000 14.169300 24.305700 107.015999 15.299700 19.973101 102.710999 15.299800 25.210501 97.473099 15.299800 29.515800 96.756203 14.169300 28.456499 96.756203 14.169300 28.456400 101.806000 14.169300 24.305700 102.710999 15.299700 25.210501 97.473099 15.299800 29.515800 91.436798 15.299800 32.755901 90.936600 14.169300 31.580299 90.936501 14.169300 31.580200 96.756203 14.169300 28.456499 97.473000 15.299700 29.515800 91.436798 15.299800 32.755901 84.734703 15.299800 34.797600 84.474998 14.169300 33.548698 84.474899 14.169300 33.548599 90.936600 14.169300 31.580299 91.436699 15.299700 32.755901 84.734703 15.299800 34.797600 77.500000 14.169300 34.233200 84.474998 14.169300 33.548698 84.734703 15.299700 34.797501 111.733002 14.169300 0.000000 111.049004 14.169300 6.975000 111.049004 14.169300 6.975000 109.080002 14.169300 13.436600 108.634003 13.375000 13.246800 111.049004 14.169300 6.974900 109.080002 14.169300 13.436500 101.806000 14.169300 24.305700 96.756203 14.169300 28.456499 96.484299 13.375000 28.054600 96.756203 14.169300 28.456499 90.936600 14.169300 31.580299 90.746803 13.375000 31.134300 96.756203 14.169300 28.456400 90.936600 14.169300 31.580299 84.474998 14.169300 33.548698 84.376503 13.375000 33.075001 90.936501 14.169300 31.580200 84.474998 14.169300 33.548698 84.474899 14.169300 33.548599 77.500000 80.875000 0.000000 72.323196 80.578300 0.000000 72.426201 80.578300 1.058400 72.722603 80.578300 2.037300 73.193199 80.578300 2.917500 77.500000 80.875000 0.000000 73.193298 80.578300 2.917600 73.819603 80.578300 3.680200 77.500000 80.875000 0.000000 73.819603 80.578300 3.680300 74.582298 80.578300 4.306400 77.500000 80.875000 0.000000 74.582397 80.578300 4.306500 75.462601 80.578300 4.777200 77.500000 80.875000 0.000000 75.462601 80.578300 4.777300 76.441399 80.578300 5.073600 76.441498 80.578300 5.073700 69.988403 79.767502 1.567000 72.323097 80.578300 0.000000 69.835899 79.767502 0.000000 69.988403 79.767502 1.567000 70.427200 79.767502 3.016100 70.427200 79.767502 3.016000 71.124199 79.767502 4.319300 73.193298 80.578300 2.917600 72.722603 80.578300 2.037300 71.124100 79.767502 4.319300 72.051300 79.767502 5.448600 73.819603 80.578300 3.680300 73.193298 80.578300 2.917600 73.193199 80.578300 2.917500 71.124199 79.767502 4.319300 72.051300 79.767502 5.448500 73.180603 79.767502 6.375700 74.582397 80.578300 4.306500 73.819603 80.578300 3.680300 73.819603 80.578300 3.680200 72.051300 79.767502 5.448600 73.180603 79.767502 6.375600 74.483803 79.767502 7.072700 75.462601 80.578300 4.777300 74.582397 80.578300 4.306500 74.582298 80.578300 4.306400 73.180603 79.767502 6.375700 74.483803 79.767502 7.072700 75.932899 79.767502 7.511500 76.441498 80.578300 5.073700 75.462601 80.578300 4.777300 75.462601 80.578300 4.777200 74.483803 79.767502 7.072700 75.932800 79.767502 7.511400 76.441399 80.578300 5.073600 75.932899 79.767502 7.511500 69.514603 78.561203 1.665700 69.835800 79.767502 0.000000 69.352501 78.561203 0.000000 69.514603 78.561203 1.665600 69.981102 78.561203 3.206200 70.427200 79.767502 3.016100 69.988403 79.767502 1.567000 69.981102 78.561203 3.206100 70.722198 78.561203 4.591600 71.124199 79.767502 4.319300 70.427200 79.767502 3.016100 70.427200 79.767502 3.016000 69.981102 78.561203 3.206200 70.722099 78.561203 4.591500 71.707802 78.561203 5.792100 72.051300 79.767502 5.448600 71.124199 79.767502 4.319300 71.124100 79.767502 4.319300 70.722198 78.561203 4.591600 71.707802 78.561203 5.792000 72.908302 78.561203 6.777700 73.180603 79.767502 6.375700 72.051300 79.767502 5.448600 72.051300 79.767502 5.448500 71.707802 78.561203 5.792100 72.908302 78.561203 6.777600 74.293701 78.561203 7.518800 73.180603 79.767502 6.375600 72.908302 78.561203 6.777700 74.293701 78.561203 7.518700 75.834198 78.561203 7.985300 75.932899 79.767502 7.511500 74.483803 79.767502 7.072700 75.834099 78.561203 7.985300 75.932800 79.767502 7.511400 75.834198 78.561203 7.985300 77.500000 78.561203 8.147300 70.333000 77.078102 1.494700 69.514603 78.561203 1.665700 69.352501 78.561203 0.000000 69.352501 78.561203 0.000000 70.187500 77.078102 0.000000 70.333000 77.078102 1.494600 70.751801 77.078102 2.877200 69.981102 78.561203 3.206200 69.514603 78.561203 1.665700 69.514603 78.561203 1.665600 70.333000 77.078102 1.494700 70.751801 77.078102 2.877100 71.417000 77.078102 4.120600 70.722198 78.561203 4.591600 69.981102 78.561203 3.206200 69.981102 78.561203 3.206100 70.751801 77.078102 2.877200 71.416901 77.078102 4.120600 72.301697 77.078102 5.198200 71.707802 78.561203 5.792100 70.722198 78.561203 4.591600 70.722099 78.561203 4.591500 71.417000 77.078102 4.120600 72.301598 77.078102 5.198200 73.379303 77.078102 6.082900 72.908302 78.561203 6.777700 71.707802 78.561203 5.792100 71.707802 78.561203 5.792000 72.301697 77.078102 5.198200 73.379303 77.078102 6.082900 74.622704 77.078102 6.748100 74.293701 78.561203 7.518800 72.908302 78.561203 6.777700 72.908302 78.561203 6.777600 73.379303 77.078102 6.082900 74.622704 77.078102 6.748000 76.005203 77.078102 7.166900 75.834198 78.561203 7.985300 74.293701 78.561203 7.518800 74.293701 78.561203 7.518700 74.622704 77.078102 6.748100 76.005203 77.078102 7.166900 75.834198 78.561203 7.985300 75.834099 78.561203 7.985300 76.005203 77.078102 7.166900 77.500000 77.078102 7.312300 71.771599 75.436699 1.194200 70.333000 77.078102 1.494700 70.187500 77.078102 0.000000 70.187500 77.078102 0.000000 71.655197 75.436699 0.000000 71.771500 75.436600 1.194200 72.106499 75.436699 2.299000 70.751801 77.078102 2.877200 70.333000 77.078102 1.494700 70.333000 77.078102 1.494600 71.771599 75.436699 1.194200 72.106400 75.436600 2.299000 72.638397 75.436699 3.292800 71.417000 77.078102 4.120600 70.751801 77.078102 2.877200 70.751801 77.078102 2.877100 72.106499 75.436699 2.299000 72.638298 75.436600 3.292700 73.345703 75.436699 4.154200 72.301697 77.078102 5.198200 71.417000 77.078102 4.120600 71.416901 77.078102 4.120600 72.638397 75.436699 3.292800 73.345703 75.436600 4.154200 74.207100 75.436699 4.861500 72.301598 77.078102 5.198200 73.345703 75.436699 4.154200 74.207001 75.436600 4.861400 75.200897 75.436699 5.393400 74.622704 77.078102 6.748100 73.379303 77.078102 6.082900 75.200798 75.436600 5.393400 76.305702 75.436699 5.728300 74.622704 77.078102 6.748000 75.200897 75.436699 5.393400 76.305702 75.436600 5.728300 76.005203 77.078102 7.166900 77.500000 75.436600 5.844600 73.158600 73.755798 0.904300 71.771599 75.436699 1.194200 71.655197 75.436699 0.000000 71.655098 75.436600 0.000000 73.070297 73.755798 0.000000 73.158501 73.755699 0.904200 73.412697 73.755798 1.741300 72.106499 75.436699 2.299000 71.771599 75.436699 1.194200 71.771500 75.436600 1.194200 73.158600 73.755798 0.904300 73.412598 73.755699 1.741200 73.816101 73.755798 2.494400 72.638397 75.436699 3.292800 72.106499 75.436699 2.299000 72.106400 75.436600 2.299000 73.412697 73.755798 1.741300 73.816101 73.755699 2.494400 74.352501 73.755798 3.147400 73.345703 75.436699 4.154200 72.638397 75.436699 3.292800 72.638298 75.436600 3.292700 73.816101 73.755798 2.494400 74.352501 73.755699 3.147300 75.005501 73.755798 3.683800 74.207100 75.436699 4.861500 73.345703 75.436699 4.154200 73.345703 75.436600 4.154200 74.352501 73.755798 3.147400 75.005501 73.755699 3.683700 75.758598 73.755798 4.087200 75.200897 75.436699 5.393400 74.207100 75.436699 4.861500 74.207001 75.436600 4.861400 75.005501 73.755798 3.683800 75.758499 73.755699 4.087200 76.595596 73.755798 4.341300 76.305702 75.436699 5.728300 75.200897 75.436699 5.393400 75.200798 75.436600 5.393400 75.758598 73.755798 4.087200 76.595497 73.755699 4.341300 76.305702 75.436699 5.728300 76.305702 75.436600 5.728300 76.595596 73.755798 4.341300 77.500000 73.755699 4.429500 73.821999 72.153999 0.765100 73.158600 73.755798 0.904300 73.070297 73.755798 0.000000 73.070198 73.755699 0.000000 73.747002 72.153999 0.000000 73.821899 72.153900 0.765100 74.037598 72.153999 1.473800 73.412697 73.755798 1.741300 73.158600 73.755798 0.904300 73.158501 73.755699 0.904200 73.821999 72.153999 0.765100 74.037498 72.153900 1.473700 74.379799 72.153999 2.111800 73.816101 73.755798 2.494400 73.412697 73.755798 1.741300 73.412598 73.755699 1.741200 74.037598 72.153999 1.473800 74.379700 72.153900 2.111700 74.834702 72.153999 2.665200 74.352501 73.755798 3.147400 73.816101 73.755798 2.494400 73.816101 73.755699 2.494400 74.379799 72.153999 2.111800 74.834702 72.153900 2.665100 75.388100 72.153999 3.120100 75.005501 73.755798 3.683800 74.352501 73.755798 3.147400 74.352501 73.755699 3.147300 74.834702 72.153999 2.665200 75.388000 72.153900 3.120100 76.026100 72.153999 3.462300 75.758598 73.755798 4.087200 75.005501 73.755798 3.683800 75.005501 73.755699 3.683700 75.388100 72.153999 3.120100 76.026100 72.153900 3.462300 76.734802 72.153999 3.677900 76.595596 73.755798 4.341300 75.758598 73.755798 4.087200 75.758499 73.755699 4.087200 76.026100 72.153999 3.462300 76.734802 72.153900 3.677900 76.595596 73.755798 4.341300 76.595497 73.755699 4.341300 76.734802 72.153999 3.677900 77.500000 72.153900 3.752800 73.089897 70.750000 0.916800 73.821999 72.153999 0.765100 73.747002 72.153999 0.000000 73.747002 72.153900 0.000000 73.000000 70.750000 0.000000 73.089798 70.750000 0.916800 73.348701 70.750000 1.766200 74.037598 72.153999 1.473800 73.821999 72.153999 0.765100 73.821899 72.153900 0.765100 73.089897 70.750000 0.916800 73.348701 70.750000 1.766100 73.759300 70.750000 2.531200 74.379799 72.153999 2.111800 74.037598 72.153999 1.473800 74.037498 72.153900 1.473700 73.348701 70.750000 1.766200 73.759300 70.750000 2.531100 74.305000 70.750000 3.194900 74.834702 72.153999 2.665200 74.379799 72.153999 2.111800 74.379700 72.153900 2.111700 74.968697 70.750000 3.740600 75.388100 72.153999 3.120100 74.834702 72.153999 2.665200 74.834702 72.153900 2.665100 74.305000 70.750000 3.194900 74.968597 70.750000 3.740600 75.733704 70.750000 4.151200 76.026100 72.153999 3.462300 75.388100 72.153999 3.120100 75.388000 72.153900 3.120100 74.968697 70.750000 3.740600 75.733704 70.750000 4.151100 76.583099 70.750000 4.409900 76.734802 72.153999 3.677900 76.026100 72.153999 3.462300 76.026100 72.153900 3.462300 75.733704 70.750000 4.151200 76.583000 70.750000 4.409900 76.734802 72.153999 3.677900 76.734802 72.153900 3.677900 76.583099 70.750000 4.409900 77.500000 70.750000 4.499800 109.000000 64.000000 0.000000 108.370003 64.000000 6.418100 108.012001 64.968903 6.343800 108.635002 64.968903 0.000000 108.370003 64.000000 6.418100 106.558998 64.000000 12.363700 106.222000 64.969002 12.220500 106.222000 64.969002 12.220400 108.012001 64.968903 6.343800 108.370003 64.000000 6.418000 106.558998 64.000000 12.363700 103.683998 64.000000 17.718700 103.380997 64.969002 17.513500 106.222000 64.969002 12.220500 106.558998 64.000000 12.363600 99.864899 64.000000 22.364901 99.606003 64.968903 22.106001 99.864899 64.000000 22.364901 95.218697 64.000000 26.184299 95.013496 64.968903 25.881100 95.013397 64.968903 25.881001 99.606003 64.968903 22.106001 99.864799 64.000000 22.364901 95.218697 64.000000 26.184299 89.863701 64.000000 29.058701 89.720497 64.968903 28.722200 89.720398 64.968903 28.722200 95.013496 64.968903 25.881100 95.218597 64.000000 26.184200 83.918098 64.000000 30.869900 83.843803 64.968903 30.512501 83.918098 64.000000 30.869900 83.843803 64.968903 30.512501 83.917999 64.000000 30.869801 107.939003 65.661102 6.328500 107.939003 65.661102 6.328400 108.560997 65.661102 0.000000 108.635002 64.968903 0.000000 106.222000 64.969002 12.220500 106.153000 65.661102 12.191200 103.319000 65.661102 17.471500 103.319000 65.661102 17.471500 106.153000 65.661102 12.191200 106.222000 64.969002 12.220400 99.552902 65.661102 22.052900 99.606003 64.968903 22.106001 95.013496 64.968903 25.881100 94.971497 65.661102 25.819000 94.971397 65.661102 25.819000 95.013496 64.968903 25.881100 89.720497 64.968903 28.722200 89.691200 65.661102 28.653299 89.691200 65.661102 28.653200 94.971497 65.661102 25.819000 95.013397 64.968903 25.881001 83.828499 65.661102 30.439301 83.828400 65.661102 30.439301 89.691200 65.661102 28.653299 89.720398 64.968903 28.722200 77.500000 65.661102 31.060400 108.560997 65.661102 0.000000 107.939003 65.661102 6.328500 108.098999 66.076401 6.361700 108.723000 66.076401 0.000000 106.303001 66.076401 12.255000 106.303001 66.076401 12.255000 108.098999 66.076401 6.361700 107.939003 65.661102 6.328400 103.454002 66.076401 17.563000 103.454002 66.076401 17.562901 99.668404 66.076401 22.168400 99.668404 66.076401 22.168301 94.971497 65.661102 25.819000 95.063004 66.076401 25.954201 94.971497 65.661102 25.819000 89.691200 65.661102 28.653299 89.754997 66.076401 28.803301 89.754898 66.076401 28.803301 95.063004 66.076401 25.954201 94.971397 65.661102 25.819000 89.691200 65.661102 28.653299 83.828499 65.661102 30.439301 83.861702 66.076401 30.598600 83.861702 66.076401 30.598600 89.754997 66.076401 28.803301 89.691200 65.661102 28.653200 83.828499 65.661102 30.439301 83.861702 66.076401 30.598600 83.828400 65.661102 30.439301 108.439003 66.214798 6.432400 108.439003 66.214699 6.432400 109.070000 66.214798 0.000000 108.723000 66.076401 0.000000 106.624001 66.214798 12.391300 106.623001 66.214699 12.391300 106.303001 66.076401 12.255000 103.454002 66.076401 17.563000 103.742996 66.214798 17.758200 103.742996 66.214699 17.758101 103.454002 66.076401 17.563000 99.668404 66.076401 22.168400 99.914902 66.214798 22.414900 99.914902 66.214699 22.414801 103.742996 66.214798 17.758200 103.454002 66.076401 17.562901 95.258301 66.214798 26.242800 95.258301 66.214699 26.242701 99.914902 66.214798 22.414900 99.668404 66.076401 22.168301 95.063004 66.076401 25.954201 89.754997 66.076401 28.803301 89.891296 66.214798 29.123600 89.891197 66.214699 29.123600 83.932404 66.214798 30.938900 83.932404 66.214699 30.938801 89.891296 66.214798 29.123600 89.754898 66.076401 28.803301 77.500000 66.214699 31.570200 109.070000 66.214798 0.000000 108.439003 66.214798 6.432400 108.907997 66.076401 6.530000 108.907997 66.076401 6.530000 109.549004 66.076401 0.000000 109.070000 66.214699 0.000000 108.439003 66.214798 6.432400 106.624001 66.214798 12.391300 107.065002 66.076401 12.579300 107.065002 66.076401 12.579200 108.907997 66.076401 6.530000 108.439003 66.214699 6.432400 106.624001 66.214798 12.391300 103.742996 66.214798 17.758200 104.140999 66.076401 18.027700 104.140999 66.076401 18.027700 107.065002 66.076401 12.579300 106.623001 66.214699 12.391300 103.742996 66.214798 17.758200 99.914902 66.214798 22.414900 100.254997 66.076401 22.754999 100.254997 66.076401 22.754900 104.140999 66.076401 18.027700 103.742996 66.214699 17.758101 99.914902 66.214798 22.414900 95.258301 66.214798 26.242800 95.527702 66.076401 26.640900 95.527702 66.076401 26.640800 100.254997 66.076401 22.754999 99.914902 66.214699 22.414801 95.258301 66.214798 26.242800 89.891296 66.214798 29.123600 90.079300 66.076401 29.565399 90.079201 66.076401 29.565300 95.527702 66.076401 26.640900 95.258301 66.214699 26.242701 89.891296 66.214798 29.123600 83.932404 66.214798 30.938900 84.029999 66.076401 31.408300 84.029900 66.076401 31.408300 90.079300 66.076401 29.565399 89.891197 66.214699 29.123600 83.932404 66.214798 30.938900 84.029999 66.076401 31.408300 83.932404 66.214699 30.938801 109.455002 65.661102 6.643700 110.107002 65.661102 0.000000 108.907997 66.076401 6.530000 107.065002 66.076401 12.579300 107.580002 65.661102 12.798400 107.580002 65.661102 12.798300 104.605003 65.661102 18.341600 104.605003 65.661102 18.341600 107.580002 65.661102 12.798400 107.065002 66.076401 12.579200 104.140999 66.076401 18.027700 100.254997 66.076401 22.754999 100.651001 65.661102 23.151199 100.651001 65.661102 23.151100 100.254997 66.076401 22.754999 95.527702 66.076401 26.640900 95.841599 65.661102 27.104900 95.841499 65.661102 27.104900 100.651001 65.661102 23.151199 100.254997 66.076401 22.754900 95.527702 66.076401 26.640900 90.079300 66.076401 29.565399 90.298401 65.661102 30.080299 90.298401 65.661102 30.080200 95.841599 65.661102 27.104900 95.527702 66.076401 26.640800 90.079300 66.076401 29.565399 84.029999 66.076401 31.408300 84.143700 65.661102 31.955200 84.143600 65.661102 31.955200 90.298401 65.661102 30.080299 90.079201 66.076401 29.565300 77.500000 65.661102 32.607300 84.143700 65.661102 31.955200 84.029900 66.076401 31.408300 110.028000 64.968903 6.762800 110.692001 64.968903 0.000000 109.455002 65.661102 6.643700 107.580002 65.661102 12.798400 108.119003 64.968903 13.027800 108.119003 64.968903 13.027700 105.091003 64.968903 18.670401 108.119003 64.968903 13.027800 107.580002 65.661102 12.798300 100.651001 65.661102 23.151199 101.066002 64.968903 23.566200 100.651001 65.661102 23.151199 95.841599 65.661102 27.104900 96.170403 64.968903 27.590700 96.170403 64.968903 27.590700 101.066002 64.968903 23.566200 100.651001 65.661102 23.151100 95.841599 65.661102 27.104900 90.298401 65.661102 30.080299 90.527802 64.968903 30.619499 90.527802 64.968903 30.619400 96.170403 64.968903 27.590700 95.841499 65.661102 27.104900 90.298401 65.661102 30.080299 84.143700 65.661102 31.955200 84.262802 64.968903 32.528000 84.262802 64.968903 32.527901 90.527802 64.968903 30.619499 90.298401 65.661102 30.080200 84.143700 65.661102 31.955200 77.500000 64.968903 33.191700 84.262802 64.968903 32.528000 84.143600 65.661102 31.955200 108.119003 64.968903 13.027800 108.119003 64.968903 13.027700 96.484200 64.000000 28.054501 90.527802 64.968903 30.619499 90.527802 64.968903 30.619499 84.262802 64.968903 32.528000 84.376503 64.000000 33.074902 90.527802 64.968903 30.619400 84.262802 64.968903 32.528000 84.262802 64.968903 32.527901 3 0 1 2 3 3 4 5 3 6 7 8 3 9 10 11 3 12 13 14 3 15 16 17 3 18 19 20 3 21 22 23 3 24 25 26 3 27 28 29 3 30 31 32 3 33 34 35 3 36 37 38 3 39 40 41 3 42 43 44 3 45 46 47 3 48 49 50 3 51 52 53 3 54 55 56 3 57 58 59 3 60 61 62 3 63 64 65 3 66 67 68 3 69 70 71 3 72 73 74 3 75 76 77 3 78 79 80 3 81 82 83 3 84 85 86 3 87 88 89 3 90 91 92 3 93 94 95 3 96 97 98 3 99 100 101 3 102 103 104 3 105 106 107 3 108 109 110 3 111 112 113 3 114 115 116 3 117 118 119 3 120 121 122 3 123 124 125 3 126 127 128 3 129 130 131 3 132 133 134 3 135 136 137 3 138 139 140 3 141 142 143 3 144 145 146 3 147 148 149 3 150 151 152 3 153 154 155 3 156 157 158 3 159 160 161 3 162 163 164 3 165 166 167 3 168 169 170 3 171 172 173 3 174 175 176 3 177 178 179 3 180 181 182 3 183 184 185 3 186 187 188 3 189 190 191 3 192 193 194 3 195 196 197 3 198 199 200 3 201 202 203 3 204 205 206 3 207 208 209 3 210 211 212 3 213 214 215 3 216 217 218 3 219 220 221 3 222 223 224 3 225 226 227 3 228 229 230 3 231 232 233 3 190 189 234 3 235 236 237 3 238 239 240 3 241 242 243 3 244 245 246 3 247 248 249 3 250 251 252 3 253 254 255 3 256 257 258 3 259 260 261 3 262 263 264 3 265 266 267 3 268 269 270 3 271 272 273 3 274 275 276 3 277 278 279 3 280 281 282 3 283 284 285 3 286 287 288 3 289 290 291 3 292 293 294 3 295 296 297 3 298 299 300 3 301 302 303 3 304 305 306 3 307 308 309 3 310 311 312 3 313 314 315 3 316 317 318 3 319 320 321 3 322 323 324 3 325 326 327 3 328 329 330 3 331 332 333 3 334 335 336 3 337 338 339 3 340 341 342 3 343 344 345 3 346 347 348 3 349 350 351 3 352 353 354 3 355 356 357 3 358 359 360 3 361 362 363 3 364 365 366 3 367 368 369 3 370 371 372 3 373 374 375 3 376 377 378 3 379 380 381 3 382 383 384 3 384 385 382 3 386 387 388 3 389 390 391 3 392 393 394 3 395 396 397 3 398 399 400 3 401 402 403 3 404 405 406 3 407 408 409 3 410 411 412 3 413 406 405 3 414 415 416 3 417 412 411 3 415 418 419 3 420 416 415 3 385 384 421 3 422 423 385 3 424 425 426 3 427 421 384 3 396 395 428 3 429 430 431 3 432 433 434 3 435 428 395 3 436 437 438 3 439 440 441 3 442 443 444 3 445 446 447 3 448 449 450 3 451 452 453 3 454 455 456 3 457 458 459 3 460 461 462 3 463 464 465 3 466 467 468 3 469 470 471 3 472 473 474 3 475 476 477 3 478 479 480 3 481 482 483 3 484 485 486 3 487 488 489 3 446 445 490 3 491 492 493 3 452 451 494 3 495 490 445 3 496 497 498 3 499 494 451 3 464 463 500 3 501 502 503 3 504 505 506 3 507 500 463 3 508 509 510 3 511 512 513 3 482 481 514 3 515 516 517 3 518 519 520 3 521 514 481 3 522 523 524 3 525 526 527 3 528 529 530 3 531 532 533 3 534 535 536 3 537 538 539 3 540 541 542 3 543 544 545 3 546 547 548 3 549 550 551 3 552 553 554 3 555 556 557 3 558 559 560 3 561 562 563 3 564 565 566 3 567 568 569 3 570 571 572 3 573 574 575 3 576 577 578 3 579 580 581 3 582 583 584 3 585 586 587 3 544 543 588 3 589 590 591 3 550 549 592 3 593 588 543 3 549 594 595 3 595 592 549 3 562 561 596 3 597 595 598 3 561 599 600 3 600 596 561 3 601 602 603 3 604 605 606 3 607 608 609 3 610 611 612 3 586 585 613 3 614 615 616 3 617 618 619 3 620 621 622 3 623 624 625 3 626 627 628 3 592 595 629 3 630 631 632 3 595 597 633 3 633 629 595 3 596 600 634 3 635 633 597 3 636 637 638 3 639 634 600 3 640 641 642 3 643 644 645 3 615 614 646 3 647 648 649 3 650 651 652 3 653 654 655 3 656 657 658 3 659 660 661 3 631 630 662 3 663 664 665 3 629 633 666 3 667 662 630 3 633 668 669 3 669 666 633 3 670 671 672 3 673 674 675 3 676 677 678 3 679 680 681 3 648 647 682 3 683 684 685 3 686 687 688 3 689 690 691 3 692 693 694 3 695 696 697 3 698 699 700 3 701 702 703 3 704 705 706 3 707 708 709 3 710 711 712 3 713 714 715 3 716 717 718 3 719 720 721 3 722 723 724 3 725 726 727 3 728 729 730 3 731 732 733 3 734 735 736 3 737 738 739 3 740 741 742 3 743 744 745 3 746 747 748 3 749 750 751 3 752 753 754 3 755 756 757 3 758 759 760 3 761 762 763 3 764 765 766 3 767 768 769 3 770 771 772 3 773 774 775 3 776 777 778 3 779 780 781 3 782 783 784 3 785 786 787 3 788 789 790 3 791 792 793 3 794 795 796 3 797 798 799 3 800 801 802 3 803 804 805 3 806 807 808 3 809 810 811 3 812 813 814 3 815 816 817 3 818 819 820 3 821 822 823 3 824 825 826 3 827 828 829 3 830 831 832 3 833 834 835 3 836 837 838 3 839 840 841 3 798 797 842 3 843 844 845 3 804 803 846 3 847 842 797 3 848 849 850 3 851 852 853 3 854 855 856 3 857 858 859 3 860 861 862 3 863 864 865 3 866 867 868 3 869 870 871 3 872 873 874 3 875 876 877 3 878 879 880 3 881 882 883 3 884 885 886 3 887 888 889 3 890 891 892 3 893 894 895 3 852 851 896 3 897 898 899 3 858 857 900 3 901 896 851 3 864 863 902 3 903 900 857 3 870 869 904 3 905 902 863 3 869 906 907 3 907 904 869 3 908 909 910 3 911 912 913 3 914 915 916 3 917 918 919 3 920 921 922 3 923 924 925 3 926 927 928 3 929 930 931 3 932 933 934 3 935 936 937 3 938 939 940 3 941 942 943 3 944 945 946 3 947 948 949 3 904 907 950 3 951 952 953 3 954 955 956 3 957 950 907 3 958 959 960 3 961 962 963 3 964 965 966 3 967 968 969 3 970 971 972 3 972 973 970 3 974 975 976 3 977 978 979 3 980 981 982 3 983 984 985 3 986 987 988 3 989 990 991 3 992 993 994 3 995 996 997 3 998 999 1000 3 1001 1002 1003 3 962 961 1004 3 1005 1006 1007 3 961 1008 1009 3 1009 1004 961 3 1010 1011 1012 3 1012 1013 1010 3 1014 1015 1016 3 1017 1018 1019 3 1020 1021 1022 3 1023 1024 1025 3 1026 1027 1021 3 1021 1020 1026 3 1028 1029 1030 3 1031 1032 1033 3 1034 1035 1036 3 1037 1038 1039 3 1040 1041 1042 3 1043 1044 1045 3 1046 1047 1048 3 1049 1050 1051 3 1052 1010 1013 3 1053 1054 1055 3 1056 1057 1058 3 1010 1052 1059 3 1060 1025 1024 3 1061 1062 1063 3 1064 1026 1020 3 1025 1060 1065 3 1066 1067 1026 3 1026 1064 1066 3 1068 1039 1038 3 1069 1070 1071 3 1072 1073 1039 3 1039 1068 1072 3 1074 1051 1050 3 1075 1076 1077 3 1078 1079 1080 3 1081 1082 1083 3 1084 1085 1086 3 1087 1088 1089 3 1090 1063 1062 3 1091 1092 1093 3 1094 1095 1096 3 1063 1090 1097 3 1098 1066 1064 3 1099 1100 1101 3 1102 1103 1066 3 1066 1098 1102 3 1104 1072 1068 3 1105 1106 1107 3 1108 1109 1072 3 1072 1104 1108 3 1110 1111 1112 3 1113 1114 1115 3 1116 1089 1088 3 1117 1118 1119 3 1120 1121 1122 3 1089 1116 1123 3 1124 1125 1126 3 1127 1128 1129 3 1130 1101 1100 3 1131 1132 1133 3 1134 1102 1098 3 1101 1130 1135 3 1136 1137 1138 3 1102 1134 1139 3 1140 1108 1104 3 1141 1142 1143 3 1144 1145 1146 3 1147 1148 1149 3 1150 1151 1152 3 1153 1154 1155 3 1156 1157 1158 3 1159 1160 1161 3 1162 1163 1164 3 1165 1166 1167 3 1168 1169 1170 3 1171 1172 1173 3 1174 1175 1176 3 1177 1178 1179 3 1180 1181 1182 3 1183 1184 1185 3 1186 1187 1188 3 1189 1190 1191 3 1192 1193 1194 3 1195 1196 1197 3 1198 1199 1200 3 1201 1202 1203 3 1204 1161 1160 3 1205 1206 1207 3 1208 1209 1210 3 1161 1204 1211 3 1212 1213 1214 3 1215 1216 1217 3 1218 1219 1220 3 1221 1222 1223 3 1224 1185 1184 3 1225 1226 1227 3 1228 1191 1190 3 1185 1224 1229 3 1230 1197 1196 3 1196 1231 1230 3 1232 1233 1234 3 1197 1230 1235 3 1236 1207 1206 3 1237 1238 1239 3 1240 1241 1207 3 1207 1236 1240 3 1242 1243 1244 3 1245 1246 1247 3 1248 1249 1250 3 1251 1252 1253 3 1254 1227 1226 3 1255 1256 1257 3 1258 1259 1260 3 1227 1254 1261 3 415 1230 1231 3 1231 418 415 3 414 1262 1230 3 1230 415 414 3 1263 1264 1265 3 1266 411 410 3 1267 1240 1236 3 1268 1269 1270 3 1271 1272 1273 3 1240 1267 1274 3 1275 1276 1277 3 1278 1279 1280 3 1281 1282 1283 3 1284 1285 1286 3 1287 1288 1289 3 1290 383 382 3 1291 1292 1293 3 1293 1294 1291 3 1295 1296 1297 3 1298 1299 1300 3 1301 1302 1303 3 1304 1305 1306 3 1307 1308 1309 3 1310 1311 1312 3 1313 1314 1315 3 1308 1307 1316 3 1317 1318 1319 3 1320 1321 1322 3 1323 1324 1325 3 1326 1327 1328 3 1329 1330 1331 3 1332 1333 1329 3 1334 1291 1294 3 1335 1336 1337 3 1338 1339 1340 3 1291 1334 1341 3 1342 1306 1305 3 1343 1344 1345 3 1346 1347 1348 3 1306 1342 1349 3 1350 1351 1352 3 1353 1354 1355 3 1356 1357 1358 3 1359 1360 1361 3 1362 1363 1364 3 1365 1366 1367 3 1368 1329 1333 3 1369 1370 1371 3 1372 1373 1374 3 1375 1376 1377 3 1378 1379 1380 3 1381 1382 1383 3 1384 1385 1386 3 1387 1388 1389 3 1390 1391 1392 3 1393 1394 1395 3 1396 1355 1354 3 1397 1398 1399 3 1400 1361 1360 3 1355 1396 1401 3 1402 1403 1404 3 1361 1400 1405 3 1406 1371 1370 3 1407 1408 1409 3 1410 1411 1412 3 1413 1414 1415 3 1416 1417 1418 3 1419 1420 1421 3 1422 1423 1424 3 1425 1426 1427 3 1428 1429 1430 3 1431 1432 1433 3 1434 1435 1436 3 1437 1438 1439 3 1440 1441 1442 3 1443 1444 1445 3 1446 1447 1448 3 1449 1450 1451 3 1452 1453 1454 3 1455 1456 1457 3 1458 1459 1460 3 1461 1462 1463 3 1464 1465 1466 3 1467 1468 1469 3 1470 1471 1472 3 1473 1474 1475 3 1476 1433 1432 3 1477 1478 1479 3 1480 1439 1438 3 1433 1476 1481 3 1482 1483 1484 3 1439 1480 1485 3 1486 1487 1488 3 1489 1490 1491 3 1492 1457 1456 3 1493 1494 1495 3 1496 1497 1498 3 1499 1500 1501 3 1502 1503 1504 3 1505 1506 1507 3 1508 1509 1510 3 1511 1512 1513 3 1514 1515 1516 3 1517 1518 1519 3 1520 1521 1522 3 1523 1524 1525 3 1526 1527 1528 3 1529 1530 1531 3 1532 1533 1534 3 1535 1536 1537 3 1538 1539 1540 3 1541 1542 1543 3 1544 1545 1546 3 1547 1548 1549 3 1550 1551 1552 3 1553 1554 1555 3 1556 1557 1558 3 1559 1560 1561 3 1562 1563 1564 3 1565 1566 1567 3 1568 1569 1570 3 1571 1572 1573 3 1574 1575 1576 3 1577 1578 1579 3 1580 1581 1582 3 1583 1584 1585 3 1586 1587 1588 3 1589 1590 1591 3 1592 1593 1594 3 1595 1596 1597 3 1598 1599 1600 3 1601 1602 1603 3 1604 1605 1606 3 1607 1608 1609 3 1610 1611 1612 3 1613 1614 1615 3 1616 1617 1618 3 1619 1620 1621 3 1622 1623 1624 3 1625 1626 1627 3 1628 1629 1630 3 1631 1632 1633 3 1634 1635 1636 3 1637 1638 1639 3 1640 684 683 3 1641 1642 1643 3 1644 1645 1646 3 684 1640 1647 3 1648 1649 1650 3 1651 1652 1653 3 1654 1655 1656 3 1649 1648 1657 3 1658 1659 1660 3 1661 1662 1663 3 1664 664 663 3 1659 1658 1665 3 1666 660 659 3 664 1664 1667 3 1668 1669 1670 3 1671 1672 1673 3 1674 1675 1676 3 1677 1678 1679 3 1680 1681 1682 3 1683 1684 1685 3 1686 1687 1688 3 1689 1690 1691 3 1692 1693 1694 3 1695 1696 1697 3 1698 1699 1700 3 1701 1702 1703 3 1704 1705 1706 3 1707 1708 1709 3 1710 1711 1712 3 1713 1714 1715 3 1716 1717 1718 3 1719 1720 1721 3 1722 1723 1724 3 1725 1726 1727 3 1728 1729 1730 3 1731 1732 1733 3 1734 1735 1736 3 1737 1738 1739 3 1740 1741 1742 3 1743 1744 1745 3 1746 1747 1748 3 1749 1750 1751 3 1752 1753 1754 3 1755 1756 1757 3 1758 1759 1760 3 1761 1762 1763 3 1764 1765 1766 3 1767 1768 1769 3 1770 1771 1772 3 1773 1774 1775 3 1776 1777 1778 3 1779 1780 1781 3 1782 1783 1784 3 1785 1786 1787 3 1788 1789 1790 3 1791 1792 1793 3 1794 1795 1796 3 1797 1798 1799 3 1800 1801 1802 3 1803 1804 1805 3 1806 1807 1808 3 1809 1810 1811 3 1812 1813 1814 3 1815 1816 1817 3 1818 1819 1820 3 1821 1822 1823 3 1824 1825 1826 3 1827 1828 1829 3 1830 1831 1832 3 1833 1834 1835 3 1836 1837 1838 3 1839 1840 1841 3 1842 1843 1844 3 1845 1846 1847 3 1848 1849 1850 3 1851 1852 1853 3 1854 1855 1856 3 1857 1858 1859 3 1860 1861 1862 3 1863 1864 1865 3 1866 1867 1868 3 1869 1870 1871 3 1872 1873 1874 3 1875 1876 1877 3 1878 1879 1880 3 1881 1882 1883 3 1884 1885 1886 3 1887 1888 1889 3 1890 1891 1892 3 1893 1894 1895 3 1896 1897 1898 3 1899 1900 1901 3 1902 1903 1904 3 1905 1906 1907 3 1908 1909 1910 3 1911 1912 1913 3 1914 1915 1916 3 1917 1918 1919 3 1920 1921 1922 3 1923 1924 1925 3 1926 1927 1928 3 1929 1930 1931 3 1932 1933 1934 3 1935 1936 1937 3 1938 1939 1940 3 1941 1942 1943 3 1944 1945 1946 3 1947 1948 1949 3 1950 1951 1952 3 1953 1954 1955 3 1956 1957 1958 3 1959 1960 1961 3 972 1962 1963 3 1963 973 972 3 1964 1965 1966 3 1967 978 977 3 1968 1969 1970 3 1971 984 983 3 1972 1973 1974 3 1975 990 989 3 1976 1977 1978 3 1979 996 995 3 1980 1981 1982 3 1983 1002 1001 3 1984 1985 1986 3 1987 1006 1005 3 1988 1989 1990 3 1991 1004 1009 3 1992 1993 1994 3 1995 1996 1997 3 1993 1998 1999 3 1999 1994 1993 3 2000 2001 2002 3 2003 2004 2005 3 2006 2007 2008 3 2009 2010 2011 3 2012 2013 2014 3 2015 2016 2017 3 2018 2019 2020 3 2021 2022 2023 3 2024 2025 2026 3 2027 2028 2029 3 2030 2031 2032 3 2033 2034 2035 3 2036 2037 2038 3 2039 2040 2041 3 1994 1999 2042 3 2043 2044 2045 3 1999 2046 2047 3 2047 2042 1999 3 2048 2049 2050 3 2051 2052 2053 3 2054 2055 2056 3 2057 2058 2059 3 2022 2021 2060 3 2061 2062 2063 3 2021 2064 2065 3 2065 2060 2021 3 2034 2033 2066 3 2067 2068 2069 3 2040 2039 2070 3 2070 2071 2040 3 2072 2073 2074 3 2075 2070 2039 3 2042 2047 2076 3 2077 2078 2079 3 2047 2080 2081 3 2081 2076 2047 3 2082 2083 2084 3 2085 2086 2087 3 2088 2089 2090 3 2091 2092 2093 3 2060 2065 2094 3 2095 2096 2097 3 2098 2099 2100 3 2101 2094 2065 3 2071 2070 2102 3 2103 2104 2105 3 2070 2106 2107 3 2107 2102 2070 3 2108 2109 2110 3 2111 2112 2113 3 2076 2081 2114 3 2115 2116 2117 3 2081 2118 2119 3 2119 2114 2081 3 2092 2091 2120 3 2121 2119 2122 3 2091 2123 2124 3 2124 2120 2091 3 2125 2126 2127 3 2128 2129 2130 3 2131 2132 2133 3 2133 2134 2131 3 2102 2107 2135 3 2136 2133 2137 3 2138 2139 2140 3 2141 2135 2107 3 2142 2143 2144 3 2145 2146 2147 3 2114 2119 2148 3 2149 2150 2151 3 2119 2121 2152 3 2152 2148 2119 3 2120 2124 2153 3 2154 2152 2121 3 2124 2155 2156 3 2156 2153 2124 3 2134 2133 2157 3 2158 2159 2160 3 2133 2136 2161 3 2161 2157 2133 3 2136 2162 2163 3 2163 2161 2136 3 2164 2165 2166 3 2167 2168 2169 3 2170 2171 2172 3 2173 2174 2175 3 2148 2152 2176 3 2177 2178 2179 3 2180 2181 2182 3 2183 2176 2152 3 2153 2156 2184 3 2185 2186 2187 3 2188 2189 2190 3 2190 2191 2188 3 2157 2161 2192 3 2193 2190 2194 3 2161 2163 2195 3 2196 2192 2161 3 2197 2198 2199 3 2200 2195 2163 3 2174 2173 2201 3 2202 2203 2204 3 2178 2177 2205 3 2206 2201 2173 3 2207 2208 2209 3 2210 2205 2177 3 2211 2212 2213 3 2214 2215 2216 3 2191 2190 2217 3 2217 2218 2191 3 2190 2193 2219 3 2219 2217 2190 3 2220 2221 2222 3 2223 2219 2193 3 2224 2225 2226 3 2227 2228 2229 3 2230 2231 2232 3 2233 2234 2235 3 2236 2237 2238 3 2239 2240 2241 3 2242 2243 2244 3 2245 2246 2247 3 2248 2249 2250 3 2251 2252 2253 3 2254 2255 2256 3 2257 2258 2259 3 2260 2261 2262 3 2263 2264 2265 3 2266 2267 2268 3 2269 2270 2271 3 2272 2273 2274 3 2275 2276 2277 3 2278 2279 2280 3 2281 2282 2283 3 2284 2285 2286 3 2287 2288 2289 3 2290 2291 2292 3 2293 2294 2295 3 2296 2297 2298 3 2299 2300 2301 3 2302 2303 2304 3 2305 2306 2307 3 2308 2309 2310 3 2311 2312 2313 3 2314 2315 2316 3 2317 2318 2319 3 2320 2321 2322 3 2323 2324 2325 3 2326 2327 2328 3 2329 2330 2331 3 2332 2333 2334 3 2335 2336 2337 3 2338 2339 2340 3 2341 2342 2343 3 2300 2299 2344 3 2345 2346 2347 3 2348 2349 2350 3 2351 2352 2353 3 2354 2355 2356 3 2357 2358 2359 3 2360 2361 2362 3 2363 2364 2365 3 2366 2367 2368 3 2369 2370 2371 3 2372 2373 2374 3 2375 2376 2377 3 2378 2379 2380 3 2381 2382 2383 3 2384 2385 2386 3 2387 2388 2389 3 2346 2345 2390 3 2391 2392 2393 3 2394 2395 2396 3 2397 2398 2399 3 2400 2401 2402 3 2403 2404 2405 3 2406 2407 2408 3 2409 2410 2411 3 2412 2413 2414 3 2415 2416 2417 3 2418 2419 2420 3 2421 2422 2423 3 2424 2425 2426 3 2427 2428 2429 3 2430 2431 2432 3 2433 2434 2435 3 2436 2437 2438 3 2439 2440 2441 3 2442 2443 2444 3 2445 2446 2447 3 2448 2449 2450 3 2451 2452 2453 3 2454 2455 2456 3 2457 2458 2459 3 2460 2461 2462 3 2463 2464 2465 3 2466 2467 2468 3 2469 2470 2471 3 2472 2473 2474 3 2475 2476 2477 3 2478 2479 2480 3 2481 2482 2483 3 2484 2485 2486 3 2487 2488 2489 3 2490 2491 2492 3 2493 2494 2495 3 2496 2497 2498 3 2499 2500 2501 3 2502 2503 2504 3 2505 2506 2507 3 2508 2509 2510 3 2511 2512 2513 3 2514 2515 2516 3 2517 2518 2519 3 2520 2521 2522 3 2523 2524 2525 3 2526 2527 2528 3 2529 2530 2531 3 2532 2533 2534 3 2535 2536 2537 3 2538 2539 2540 3 2541 2542 2543 3 2544 2545 2546 3 2547 2548 2549 3 2550 2551 2552 3 2553 2554 2555 3 2556 2557 2558 3 2559 2560 2561 3 2562 2563 2564 3 2565 2566 2567 3 2568 2569 2570 3 2571 2572 2573 3 2574 2575 2576 3 2577 2578 2579 3 2580 2581 2582 3 2583 2584 2585 3 2586 2587 2588 3 2589 2590 2591 3 2592 2593 2594 3 2595 2596 2597 3 2598 2599 2600 3 2601 2602 2603 3 2604 2605 2606 3 2607 2608 2609 3 2610 2611 2612 3 2613 2614 2615 3 2616 2617 2618 3 2619 2620 2621 3 2622 2623 2624 3 2625 2626 2627 3 2628 2629 2630 3 43 42 2631 3 2218 2217 2632 3 2633 2634 2635 3 2217 2219 2636 3 2636 2632 2217 3 2637 2638 2639 3 2640 2636 2219 3 2641 2642 2643 3 2644 2645 2646 3 2647 2648 2649 3 2650 2651 2652 3 2240 2239 2653 3 2654 2655 2656 3 2246 2245 2657 3 2658 2653 2239 3 2252 2251 2659 3 2660 2657 2245 3 2661 2662 2663 3 2664 2665 2666 3 2632 2636 2667 3 2668 2669 2670 3 2671 2672 2673 3 2674 2667 2636 3 2645 2644 2675 3 2676 2677 2678 3 2651 2650 2679 3 2680 2675 2644 3 2650 2681 2682 3 2682 2679 2650 3 2683 2684 2685 3 2686 2687 2688 3 2689 2690 2691 3 2692 2693 2694 3 2695 2696 2697 3 2697 2698 2695 3 2699 2700 2701 3 2702 2703 2704 3 2705 2706 2707 3 2708 2709 2710 3 2711 2712 2713 3 2714 2715 2716 3 2717 2718 2719 3 2720 2721 2722 3 2679 2682 2723 3 2724 2725 2726 3 2727 2728 2729 3 2730 2723 2682 3 2731 2732 2733 3 2734 2735 2736 3 2698 2697 2737 3 2737 2738 2698 3 2739 2740 2741 3 2742 2737 2697 3 2743 2744 2745 3 2746 2747 2748 3 2749 2750 2751 3 2752 2753 2754 3 2755 2756 2757 3 2758 2759 2760 3 2725 2724 2761 3 2762 2763 2764 3 2724 2765 2766 3 2766 2761 2724 3 2767 2768 2769 3 2770 2771 2772 3 2738 2737 2773 3 2774 2775 2776 3 2777 2778 2779 3 2780 2773 2737 3 2747 2746 2781 3 2782 2783 2784 3 2785 2786 2787 3 2788 2781 2746 3 2789 2790 2791 3 2792 2793 2794 3 2795 2796 2797 3 2798 2799 2800 3 2761 2766 2801 3 2802 2803 2804 3 2766 2805 2806 3 2806 2801 2766 3 2807 2808 2809 3 2810 2811 2812 3 2813 2814 2815 3 2816 2817 2818 3 2819 2820 2821 3 2822 2823 2824 3 2825 2826 2827 3 2828 2829 2830 3 2831 2832 2833 3 2834 2835 2836 3 2837 2838 2839 3 2840 2841 2842 3 2843 2844 2845 3 2846 2847 2848 3 2801 2806 2849 3 2850 2851 2852 3 2853 2854 2855 3 2856 2857 2858 3 2859 2860 2861 3 2862 2863 2864 3 2865 2866 2867 3 2868 2869 2870 3 2871 2872 2873 3 2874 2875 2876 3 2877 2878 2879 3 2880 2881 2882 3 2883 2884 2885 3 2886 2887 2888 3 2889 2890 2891 3 2892 2893 2894 3 2851 2850 2895 3 2896 2897 2898 3 2899 2900 2901 3 2902 2903 2904 3 2863 2862 2905 3 2906 2907 2908 3 2909 2910 2911 3 2912 2905 2862 3 2913 2914 2915 3 2916 2917 2918 3 2919 2920 2921 3 2922 2923 2924 3 2925 2926 2927 3 2928 2929 2930 3 2931 2932 2933 3 2934 2935 2936 3 2897 2896 2937 3 2938 2939 2940 3 2941 2942 2943 3 2944 2945 2946 3 2947 2948 2949 3 2950 2951 2952 3 2953 2954 2955 3 2956 2957 2958 3 2959 2960 2961 3 2962 2963 2964 3 2965 2966 2967 3 2968 2969 2970 3 2971 2972 2973 3 2974 2975 2976 3 2977 2978 2979 3 2980 2981 2982 3 2983 2984 2985 3 2986 2987 2988 3 2989 2990 2991 3 2992 2993 2994 3 2995 2996 2997 3 2998 2999 3000 3 3001 3002 3003 3 3004 3005 3006 3 2963 2962 3007 3 3008 3009 3010 3 3011 3012 3013 3 3014 3015 3016 3 3017 3018 3019 3 3020 3021 3022 3 2981 2980 3023 3 3024 3025 3026 3 3027 3028 3029 3 3030 3023 2980 3 3031 3032 3033 3 3034 3035 3036 3 3037 3038 3039 3 3040 3041 3042 3 3043 3044 3045 3 3046 3047 3048 3 3009 3008 3049 3 3050 3051 3052 3 3053 3054 3055 3 3056 3057 3058 3 3059 3060 3061 3 3062 3063 3064 3 3065 3066 3067 3 3068 3069 3070 3 3071 3072 3073 3 3074 3075 3076 3 3077 3078 3079 3 3080 3081 3082 3 3083 3084 3085 3 3086 3087 3088 3 3047 3046 3089 3 3090 3091 3092 3 3093 3094 3095 3 3096 3089 3046 3 3097 3098 3099 3 3100 3101 3102 3 3103 3104 3105 3 3106 3107 3108 3 3069 3068 3109 3 3110 3111 3112 3 3113 3114 3115 3 3116 3109 3068 3 3117 3118 3119 3 3120 3121 3122 3 3123 3124 3125 3 3126 3127 3128 3 3129 3130 3131 3 3132 3133 3134 3 3135 3136 3137 3 3138 3139 3140 3 3141 3142 3143 3 3144 3145 3146 3 3147 3148 3149 3 3150 3151 3152 3 3153 3154 3155 3 3156 3157 3158 3 3159 3160 3161 3 3162 3163 3164 3 3165 3166 3167 3 3168 3169 3170 3 3171 3172 3173 3 3174 3175 3176 3 3177 3178 3179 3 3180 3181 3182 3 3183 3184 3185 3 3186 3187 3188 3 3189 3190 3191 3 3192 3193 3194 3 3195 3196 3197 3 3198 3199 3200 3 3201 3202 3203 3 3204 3205 3206 3 3207 3208 3209 3 3210 3211 3212 3 3213 3214 3215 3 3216 3217 3218 3 3219 3220 3221 3 3222 3223 3224 3 3225 3226 3227 3 3228 3229 3230 3 3231 3232 3233 3 3234 3235 3236 3 3237 3238 3239 3 3240 3241 3242 3 3243 3244 3245 3 3246 3247 3248 3 3249 3250 3251 3 3252 3253 3254 3 3255 3256 3257 3 3258 3259 3260 3 3261 3262 3263 3 3264 3265 3266 3 3267 3268 3269 3 3270 3271 3272 3 3273 3274 3275 3 3276 3277 3278 3 3279 3280 3281 3 3282 3283 3284 3 3285 3286 3287 3 3288 3289 3290 3 3291 3292 3293 3 3294 3295 3296 3 3297 3298 3299 3 3300 3301 3302 3 3303 3304 3305 3 709 3306 707 3 3307 3308 3309 3 3310 3311 3312 3 3313 3314 3315 3 3316 3317 3318 3 3319 3320 3321 3 3322 3323 3324 3 3325 3326 3327 3 3328 3329 3330 3 3331 3332 3333 3 3334 3335 3336 3 3337 3338 3339 3 3340 3341 3342 3 3343 3344 3345 3 3346 3347 3348 3 754 753 3349 3 3350 3351 3352 3 3353 3354 3355 3 3356 3357 3358 3 3359 3360 3361 3 3362 3363 3364 3 3365 3366 3367 3 3368 3369 3370 3 3371 3372 3373 3 3374 3375 3376 3 3377 3378 3379 3 3380 3381 3382 3 3383 3384 3385 3 3386 3387 3388 3 3389 3390 3391 3 3392 3393 3394 3 802 801 3395 3 3396 3397 803 3 3398 3399 3400 3 3401 3402 3403 3 3404 3405 3406 3 3407 3408 3409 3 3410 3411 3412 3 3413 3414 3415 3 3416 3417 3418 3 3419 3420 3421 3 3422 3423 3424 3 3425 3426 3427 3 3428 3388 3387 3 3429 3430 3431 3 3432 3394 3393 3 3388 3428 3433 3 846 803 3397 3 3394 3432 3434 3 3435 3436 3437 3 3438 3439 3440 3 3441 3442 3443 3 3444 3440 3445 3 3446 3447 3448 3 3449 3450 3451 3 3452 3453 3454 3 3455 3456 3457 3 3458 3459 3460 3 3461 3462 3463 3 3464 3465 3466 3 3467 3468 3469 3 3470 3471 3472 3 3473 3474 3475 3 892 891 3476 3 3477 3478 3479 3 3480 3440 3439 3 3481 3482 3483 3 3484 3445 3440 3 3440 3480 3484 3 3485 3451 3450 3 3445 3484 3486 3 3487 3488 3451 3 3451 3485 3487 3 3489 3490 3491 3 3492 3493 3494 3 3495 3496 3497 3 3498 3499 3500 3 3501 3502 3503 3 3504 3505 3506 3 922 921 3507 3 3508 3509 3510 3 3511 3512 3513 3 3514 3515 3516 3 3517 3484 3480 3 3518 3519 3520 3 3521 3522 3484 3 3484 3517 3521 3 3523 3487 3485 3 3524 3521 3525 3 3526 3527 3487 3 3487 3523 3526 3 3528 3529 3530 3 3531 3526 3532 3 3533 3534 3535 3 3536 3537 3538 3 966 965 3539 3 3540 3541 3542 3 3543 3544 3545 3 3545 3546 3543 3 3547 3548 3549 3 3550 3551 3552 3 3553 3521 3517 3 3554 3555 3556 3 3557 3525 3521 3 3521 3553 3557 3 3558 3526 3523 3 3525 3557 3559 3 3560 3532 3526 3 3526 3558 3560 3 3561 3562 3532 3 3532 3560 3561 3 1009 1008 3563 3 3564 3565 3566 3 3241 3240 3567 3 3568 3569 3570 3 3571 3572 3573 3 3574 3567 3240 3 3575 3576 3577 3 3578 3579 3580 3 3259 3258 3581 3 3582 3583 3584 3 3585 3586 3587 3 3588 3581 3258 3 3589 3590 3591 3 3592 3593 3594 3 3277 3276 3595 3 3596 3597 3598 3 3276 1330 1329 3 1329 3595 3276 3 3599 3600 3601 3 3602 3603 3604 3 3605 3606 3607 3 3608 3609 3610 3 3579 3578 3611 3 3612 3613 3614 3 3583 3582 3615 3 3616 3611 3578 3 3617 3618 3619 3 3620 3615 3582 3 3621 3622 3623 3 3624 3625 3626 3 3627 3628 3629 3 3630 3631 3632 3 3595 1329 1368 3 1371 3633 3634 3 3635 3636 3637 3 3638 3639 3640 3 3609 3608 3641 3 3642 3643 3644 3 3613 3612 3645 3 3646 3641 3608 3 3647 3648 3649 3 3650 3645 3612 3 3651 3652 3653 3 3654 3655 3656 3 3657 3658 3659 3 3660 3661 3662 3 3663 3664 3665 3 3666 3667 3668 3 3633 1371 1406 3 3669 3670 3671 3 3672 3673 3674 3 3675 3676 3677 3 3678 3679 3680 3 3681 3682 3683 3 3684 3685 3686 3 3687 3688 3689 3 3690 3691 3692 3 3693 3694 3695 3 3696 3697 3698 3 3699 3700 3701 3 3702 3703 3704 3 3705 3706 3707 3 3708 3709 3710 3 3711 3712 3713 3 3714 1453 1452 3 1457 3715 3716 3 3717 3718 3719 3 3720 3721 3722 3 3723 3724 3725 3 3726 3727 3728 3 3729 3730 3731 3 3732 3733 3734 3 3735 3736 3737 3 3738 3739 3740 3 3741 3742 3743 3 3744 3745 3746 3 3747 3748 3749 3 3750 3751 3752 3 3753 3754 3755 3 3756 3757 3758 3 3715 1457 1492 3 3759 3760 3761 3 3762 3763 3764 3 3765 3766 3767 3 3768 3769 3770 3 3771 3772 3773 3 3774 3775 3776 3 3777 3778 3779 3 3780 3781 3782 3 3783 3784 3785 3 3786 3787 3788 3 3789 3790 3791 3 3792 3793 3794 3 3795 3796 3797 3 3798 3799 3800 3 3801 3802 3803 3 3804 1539 1538 3 3805 3806 3807 3 3808 3809 3810 3 3811 3812 3813 3 3814 3815 3816 3 3817 3818 3819 3 3820 3821 3822 3 3823 3824 3825 3 3826 3827 3828 3 3829 3830 3831 3 3832 3833 3834 3 3835 3836 3837 3 3838 3839 3840 3 3841 3842 3843 3 3844 3845 3846 3 3847 3848 3849 3 3850 1587 1586 3 3851 3852 3853 3 3854 3855 3856 3 3857 3858 3859 3 3860 3861 3862 3 3863 3864 3865 3 3866 3867 3868 3 3869 3870 3871 3 3872 3873 3874 3 3875 3876 3877 3 3878 3879 3880 3 3881 3882 3883 3 3884 3885 3886 3 3887 3888 3889 3 3890 3891 3892 3 3893 3894 3895 3 3896 1635 1634 3 3897 3898 3899 3 3900 3901 3902 3 3902 3903 3900 3 3901 3904 3905 3 3905 3902 3901 3 3906 3907 3908 3 3909 3905 3904 3 3910 3911 3912 3 3913 3908 3907 3 3911 3914 3915 3 3915 3912 3911 3 3916 3917 3918 3 3919 3920 3921 3 3917 3922 3923 3 3923 3918 3917 3 3922 382 385 3 385 3923 3922 3 3903 3902 3924 3 3924 3925 3903 3 3902 3905 3926 3 3926 3924 3902 3 3927 3928 3929 3 3930 3926 3905 3 3931 3932 3933 3 3934 3935 3936 3 3912 3915 3937 3 3938 3939 3940 3 3941 3942 3943 3 3944 3937 3915 3 3918 3923 3945 3 3946 3947 3948 3 3923 385 423 3 3949 3945 3923 3 3925 3924 3950 3 3950 3951 3925 3 3924 3926 3952 3 3953 3950 3924 3 3926 3954 3955 3 3955 3952 3926 3 3956 3957 3958 3 3959 3960 3961 3 3962 3963 3964 3 3965 3966 3967 3 3968 3969 3970 3 3971 3972 3973 3 3974 3975 3976 3 3977 3978 3979 3 3980 3981 3982 3 3983 3984 3985 3 3951 3950 3986 3 3987 3988 3989 3 3990 3991 3992 3 3993 3986 3950 3 3952 3955 3994 3 3995 3996 3997 3 3960 3959 3998 3 3999 3994 3955 3 4000 4001 4002 3 4003 3998 3959 3 4004 4005 4006 3 4007 4008 4009 3 3978 3977 4010 3 4011 4012 4013 3 4014 4015 4016 3 4017 4010 3977 3 4018 4019 4020 3 4021 4022 4023 3 4024 4025 4026 3 4027 4028 4029 3 4030 4031 4032 3 4033 4034 4035 3 4036 4037 4038 3 4039 4033 4040 3 4041 4042 4043 3 4044 4045 4046 3 4047 4048 4049 3 4050 4051 4052 3 4053 4054 4055 3 4056 4057 4058 3 4059 4060 4061 3 591 4062 4063 3 4064 4065 4066 3 4067 4068 4069 3 4070 4071 4072 3 4073 4074 4075 3 4034 4033 4076 3 4076 4077 4078 3 4033 4039 4079 3 4079 4076 4033 3 4039 4080 4081 3 4081 4079 4039 3 4051 4050 4082 3 4083 4084 4085 3 4050 4086 4087 3 4087 4082 4050 3 4062 591 590 3 4088 4089 4090 3 4091 4092 4093 3 4094 4095 4096 3 4097 4098 4099 3 4100 4101 4102 3 4077 4076 4103 3 4104 4105 4106 3 4076 4079 4107 3 4107 4103 4076 3 4079 4081 4108 3 4108 4107 4079 3 4081 4109 4110 3 4110 4108 4081 3 4082 4087 4111 3 4112 4113 4114 3 4115 4116 4117 3 4118 4111 4087 3 4119 4120 4121 3 4121 4122 4119 3 4123 4124 4125 3 4126 4127 4128 3 4129 4130 4131 3 4132 4133 4134 3 4103 4107 4135 3 4136 4137 4138 3 4107 4108 4139 3 4139 4135 4107 3 4108 4110 4140 3 4141 4139 4108 3 4142 4143 4144 3 4145 4140 4110 3 4146 4147 4148 3 1669 4149 4150 3 4122 4121 4151 3 4152 4153 4154 3 4155 4156 4157 3 4158 4151 4121 3 4133 4132 4159 3 4160 4161 4162 3 4137 4136 4163 3 4164 4159 4132 3 4135 4139 4165 3 4166 4163 4136 3 4167 4168 4169 3 4170 4165 4139 3 4171 4172 4173 3 4174 4175 4176 3 4149 1669 1668 3 4177 4178 4179 3 4180 4181 4182 3 4183 4184 4185 3 4186 4187 4188 3 4189 4190 4191 3 4192 4193 4194 3 4195 4196 4197 3 4198 4199 4200 3 4201 4202 4203 3 4204 4205 4206 3 4207 4208 4209 3 4210 4211 4212 3 4213 4214 4215 3 4216 4217 4218 3 4219 4220 4221 3 4222 1717 1716 3 4223 4224 4225 3 4226 4227 4228 3 4229 4230 4231 3 4232 4233 4234 3 4235 4236 4237 3 4238 4239 4240 3 4241 4242 4243 3 4244 4245 4246 3 4247 4248 4249 3 4250 4251 4252 3 4253 4254 4255 3 4256 4257 4258 3 4259 4260 4261 3 4262 4263 4264 3 4265 4266 4267 3 4268 1765 1764 3 4269 4270 4271 3 4272 4273 4274 3 4275 4276 4277 3 4278 4279 4280 3 4281 4282 4283 3 4284 4285 4286 3 4287 4288 4289 3 4290 4291 4292 3 4293 4294 4295 3 4296 4297 4298 3 4299 4300 4301 3 4302 4303 4304 3 4305 4306 4307 3 4308 4309 4310 3 4311 4312 4313 3 4314 1813 1812 3 4315 4316 4317 3 4318 4319 4320 3 4321 4322 4323 3 4324 4325 4326 3 4327 4328 4329 3 4330 4331 4332 3 4333 4334 4335 3 4336 4337 4338 3 4339 4340 4341 3 4342 4343 4344 3 4345 4346 4347 3 4348 4349 4350 3 4351 4352 4353 3 4354 4355 4356 3 4357 4358 4359 3 4360 1861 1860 3 4361 4362 4363 3 4364 4365 4366 3 4367 4368 4369 3 4370 4371 4372 3 4373 4374 4375 3 4376 4377 4378 3 4379 4380 4381 3 4382 4383 4384 3 4385 4386 4387 3 4388 4389 4390 3 4391 4392 4393 3 4394 4395 4396 3 4397 4398 4399 3 4400 4401 4402 3 4403 4404 4405 3 4406 1909 1908 3 4407 4408 4409 3 4410 4411 4412 3 4413 4414 4415 3 4416 4417 4418 3 4419 4420 4421 3 4422 4423 4424 3 4425 4426 4427 3 4428 4429 4430 3 4431 4425 4432 3 4433 4434 4435 3 4436 4437 4438 3 4439 4440 4441 3 4442 4443 4444 3 4445 4446 4447 3 4448 4449 4450 3 4451 1957 1956 3 4452 4453 4454 3 4455 4456 3543 3 3543 3546 4455 3 4457 4458 4459 3 3552 3551 4460 3 4426 4425 3553 3 3556 3555 4461 3 4425 4431 3557 3 3557 3553 4425 3 4462 4463 4464 3 3559 3557 4431 3 4465 4466 4467 3 3560 3558 4468 3 4469 4470 4471 3 3561 3560 4472 3 4473 1989 1988 3 3566 3565 4474 3 4475 4476 4477 3 4475 4477 4478 3 4475 4478 4479 3 4480 4481 4482 3 4483 4484 4485 3 4486 4487 4488 3 4489 4490 4491 3 2964 4492 2962 3 4493 4477 4476 3 4494 4495 4496 3 4497 4478 4477 3 4477 4493 4498 3 4499 4500 4501 3 4478 4497 4502 3 4503 4504 4505 3 4506 4507 4508 3 4509 4510 4511 3 4512 4513 4514 3 4515 4516 4517 3 4518 4519 4520 3 4521 4522 4523 3 4524 4525 4526 3 3007 2962 4492 3 4527 4528 3008 3 4529 4496 4495 3 4530 4531 4532 3 4533 4534 4535 3 4496 4529 4536 3 4537 4538 4539 3 4540 4541 4542 3 4543 4544 4545 3 4546 4547 4548 3 4549 4550 4551 3 4552 4553 4554 3 4555 4520 4519 3 4556 4557 4558 3 4559 4560 4561 3 4520 4555 4562 3 3049 3008 4528 3 4563 4564 4565 3 4566 4567 4568 3 4569 4570 4571 3 4572 4573 4574 3 4575 4576 4577 3 4578 4579 4580 3 4581 4582 4583 3 4584 4585 4586 3 4587 4588 4589 3 4590 4591 4592 3 4593 4594 4595 3 4596 4597 4598 3 4599 4600 4601 3 4602 4603 4604 3 4605 4606 4607 3 3095 3094 4608 3 4609 4610 4611 3 4612 4613 4614 3 4615 4616 4617 3 4618 4619 4620 3 4621 4622 4623 3 4624 4625 4626 3 4627 4628 4629 3 4630 4631 4632 3 4633 4634 4635 3 4636 4595 4594 3 4637 4638 4639 3 4640 4641 4642 3 4595 4636 4643 3 4644 4607 4606 3 4645 4646 4647 3 3137 3136 4648 3 4607 4644 4649 3 4650 4651 4652 3 4653 4654 4655 3 4656 4657 4658 3 4659 4660 4661 3 4662 4663 4664 3 4665 4666 4667 3 4668 4669 4670 3 4671 4672 4673 3 4674 4675 4676 3 4677 4678 4679 3 4680 4681 4682 3 4683 4684 4685 3 4686 4687 4688 3 4689 4690 4691 3 3185 3184 4692 3 4693 4694 4695 3 4696 4697 4698 3 4699 4700 4701 3 4702 4703 4704 3 4705 4706 4707 3 4708 4709 4710 3 4711 4712 4713 3 4714 4715 4716 3 4717 4718 4719 3 4720 4721 4722 3 4723 4724 4725 3 4726 4727 4728 3 4729 4730 4731 3 4732 4733 4734 3 4735 4736 4737 3 3233 3232 4738 3 4739 4740 4741 3 4742 4743 4744 3 4745 4746 4747 3 4748 4749 4750 3 4751 4752 4753 3 4754 4755 4756 3 4757 4758 4759 3 4760 4761 4762 3 4763 1309 1308 3 4764 4765 4766 3 4767 4768 4769 3 4770 4771 4772 3 4773 4774 4775 3 4776 4777 4778 3 4779 4780 4781 3 3281 3280 4782 3 4783 4784 4785 3 4786 4787 4788 3 4788 4789 4786 3 4790 4791 4792 3 4793 4794 4795 3 4796 4797 4798 3 4798 4799 4800 3 4797 4801 4802 3 4802 4798 4797 3 4803 4804 4805 3 4806 4807 4808 3 4809 4810 4811 3 4812 4813 4814 3 4810 4815 4816 3 4816 4811 4810 3 4817 1047 1046 3 1051 4818 4819 3 4789 4788 4820 3 4821 4822 4823 3 4788 4824 4825 3 4825 4820 4788 3 4799 4798 4826 3 4827 4828 4829 3 4798 4802 4830 3 4830 4826 4798 3 4831 4832 4833 3 4834 4830 4802 3 4835 4836 4837 3 4838 4839 4840 3 4811 4816 4841 3 4842 4843 4844 3 4818 1051 1074 3 4845 4841 4816 3 4846 4847 4848 3 4848 4849 4846 3 4820 4825 4850 3 4851 4852 4853 3 4828 4827 4854 3 4855 4850 4825 3 4826 4830 4856 3 4857 4854 4827 3 4830 4858 4859 3 4859 4856 4830 3 4860 4861 4862 3 4863 4864 4865 3 4866 4867 4868 3 4869 4870 4871 3 4872 1109 1108 3 1108 4873 4874 3 4849 4848 4875 3 4876 4877 4878 3 4852 4851 4879 3 4880 4875 4848 3 4881 4882 4883 3 4884 4879 4851 3 4885 4886 4887 3 4888 4889 4890 3 4856 4859 4891 3 4892 4893 4894 3 4895 4896 4897 3 4898 4891 4859 3 4870 4869 4899 3 4900 4901 4902 3 4873 1108 1140 3 4903 4899 4869 3 4904 4905 4906 3 4907 4908 4909 3 4910 4911 4912 3 4913 4914 4915 3 4916 4917 4918 3 4919 4920 4921 3 4922 4923 4924 3 4925 4926 4927 3 4928 4929 4930 3 4931 4932 4933 3 4934 4935 4936 3 4937 4938 4939 3 4940 4941 4942 3 4943 4944 4945 3 4946 1187 1186 3 1191 4947 4948 3 4908 4907 4949 3 4949 4950 4908 3 4951 4952 4953 3 4954 4949 4907 3 4920 4919 4955 3 4956 4957 4958 3 4959 4960 4961 3 4962 4955 4919 3 4963 4964 4965 3 4966 4967 4968 3 4969 4970 4971 3 4972 4973 4974 3 4975 4976 4977 3 4978 4979 4980 3 4947 1191 1228 3 4981 4982 4983 3 4950 4949 4984 3 4984 4985 4950 3 4986 4987 4988 3 4989 4984 4949 3 4957 4956 4990 3 4990 4991 4992 3 4956 4993 4994 3 4994 4990 4956 3 4995 4996 4997 3 4998 4999 5000 3 5001 5002 5003 3 5004 5005 5006 3 5007 5008 5009 3 5010 5011 5012 3 5013 1259 1258 3 5014 5015 5016 3 4985 4984 3901 3 3901 3900 4985 3 4984 5017 3904 3 3904 3901 4984 3 4991 4990 3910 3 3907 3906 5018 3 4990 4994 3911 3 3911 3910 4990 3 4999 4998 3916 3 5019 3911 4994 3 4998 5020 3917 3 3917 3916 4998 3 5021 5022 5023 3 3922 3917 5024 3 5025 1288 1287 3 382 3922 5026 pyformex-0.8.6/pyformex/data/splines.pgf0000644000211500021150000107202211406164537020202 0ustar benebene00000000000000# pyFormex Geometry File (http://pyformex.org) version='1.3'; sep=' ' # objtype='BezierSpline'; ncoords=2; nparts=2; closed=True; sep=' '; name='splines-000' -30.8385925293 -3.76800489426 -1.92184400558 -30.8385925293 -3.76800489426 -1.92184400558 nan nan nan nan nan nan nan nan nan nan nan nan # objtype='BezierSpline'; ncoords=81; nparts=81; closed=True; sep=' '; name='splines-001' -28.1371479034 -4.20457267761 -0.597581267357 -28.1371479034 -4.19354724884 -0.662541329861 -28.1371479034 -4.11473274231 -1.01069200039 -28.1371479034 -4.05852222443 -1.16893792152 -28.1371479034 -3.95303153992 -1.41353070736 -28.1371479034 -3.83628940582 -1.61931502819 -28.1371479034 -3.75508069992 -1.74076390266 -28.1371479034 -3.54522418976 -2.00495815277 -28.1371479034 -3.5132086277 -2.04183268547 -28.1371479034 -3.36020803452 -2.18499851227 -28.1371479034 -3.21075439453 -2.32817602158 -28.1371479034 -3.16870188713 -2.3579018116 -28.1371479034 -2.8695538044 -2.54574251175 -28.1371479034 -2.73802947998 -2.61939883232 -28.1371479034 -2.52877044678 -2.71497654915 -28.1371479034 -2.21897864342 -2.79450464249 -28.1371479034 -2.1459145546 -2.80955410004 -28.1371479034 -1.962272048 -2.8475382328 -28.1371479034 -1.71838629246 -2.89797639847 -28.1371479034 -1.63178884983 -2.90572714806 -28.1371479034 -1.28884255886 -2.91161894798 -28.1371479034 -0.982562065125 -2.86941456795 -28.1371479034 -0.87347728014 -2.84919333458 -28.1371479034 -0.679570615292 -2.79451680183 -28.1371479034 -0.455594003201 -2.73274755478 -28.1371479034 -0.349992901087 -2.68698191643 -28.1371479034 -0.104682207108 -2.56495308876 -28.1371479034 0.173346057534 -2.38622879982 -28.1371479034 0.226340770721 -2.34840130806 -28.1371479034 0.307900607586 -2.27412104607 -28.1371479034 0.49288213253 -2.11054682732 -28.1371479034 0.583583295345 -2.00753116608 -28.1371479034 0.720096349716 -1.83967208862 -28.1371479034 0.890753448009 -1.57124137878 -28.1371479034 0.904998242855 -1.54710352421 -28.1371479034 0.929387748241 -1.49112725258 -28.1371479034 1.04966938496 -1.2233556509 -28.1371479034 1.08808946609 -1.10223960876 -28.1371479034 1.15040934086 -0.871722579002 -28.1371479034 1.18721461296 -0.622774958611 -28.1371479034 1.20049512386 -0.497334033251 -28.1371479034 1.1910392046 -0.0832434743643 -28.1371479034 1.19043636322 -0.0737293958664 -28.1371479034 1.18399429321 -0.0416897684336 -28.1371479034 1.11342978477 0.328385144472 -28.1371479034 1.08811819553 0.415507048368 -28.1371479034 0.978490114212 0.718041181564 -28.1371479034 0.898290097713 0.876046955585 -28.1371479034 0.76431196928 1.10569822788 -28.1371479034 0.635314404964 1.29329943657 -28.1371479034 0.522930681705 1.42699921131 -28.1371479034 0.340749591589 1.61264431477 -28.1371479034 0.253302305937 1.69281566143 -28.1371479034 0.0150241376832 1.87048995495 -28.1371479034 -0.0863100290298 1.93310260773 -28.1371479034 -0.378929793835 2.10518431664 -28.1371479034 -0.557533323765 2.17929840088 -28.1371479034 -0.821173965931 2.26358938217 -28.1371479034 -1.11590456963 2.32783436775 -28.1371479034 -1.31357514858 2.355645895 -28.1371479034 -1.69503676891 2.36667966843 -28.1371479034 -1.76188719273 2.36604595184 -28.1371479034 -1.85053515434 2.35264706612 -28.1371479034 -2.19905161858 2.29424953461 -28.1371479034 -2.53923821449 2.18566799164 -28.1371479034 -2.66970467567 2.13480877876 -28.1371479034 -2.90920805931 1.99741756916 -28.1371479034 -3.07422661781 1.90667593479 -28.1371479034 -3.14734649658 1.8528046608 -28.1371479034 -3.41069841385 1.63765847683 -28.1371479034 -3.53990840912 1.50236058235 -28.1371479034 -3.67103838921 1.35177540779 -28.1371479034 -3.78810596466 1.18648314476 -28.1371479034 -3.8931658268 1.01234424114 -28.1371479034 -4.01703596115 0.761537611485 -28.1371479034 -4.06470918655 0.644482851028 -28.1371479034 -4.16503238678 0.300260692835 -28.1371479034 -4.176197052 0.248739227653 -28.1371479034 -4.22226810455 -0.157589316368 -28.1371479034 -4.22488021851 -0.179438188672 -28.1371479034 -4.22488260269 -0.182540997863 -28.1371479034 -4.20101118088 -0.630273878574 -28.1371479034 -4.19994068146 -0.630235552788 -28.1371479034 -4.15891075134 -0.8375608325 -28.1371479034 -4.16430664062 -0.839561879635 -28.1371479034 -4.0914106369 -1.09120059013 -28.1371479034 -4.08920145035 -1.09082591534 -28.1371479034 -4.00985956192 -1.2928378582 -28.1371479034 -4.01226329803 -1.29446101189 -28.1371479034 -3.90042161942 -1.51928877831 -28.1371479034 -3.89835238457 -1.51870024204 -28.1371479034 -3.79796481133 -1.68144571781 -28.1371479034 -3.7981004715 -1.68180179596 -28.1371479034 -3.65573072433 -1.87693095207 -28.1371479034 -3.65298843384 -1.87521660328 -28.1371479034 -3.52962684631 -2.02373623848 -28.1371479034 -3.53012681007 -2.02427268028 -28.1371479034 -3.44061517715 -2.11717963219 -28.1371479034 -3.43628501892 -2.11296868324 -28.1371479034 -3.28506326675 -2.25614571571 -28.1371479034 -3.29036974907 -2.26251482964 -28.1371479034 -3.19094443321 -2.34451389313 -28.1371479034 -3.19011831284 -2.34362411499 -28.1371479034 -3.02180361748 -2.45583558083 -28.1371479034 -3.02138972282 -2.45563340187 -28.1371479034 -2.80475687981 -2.58419704437 -28.1371479034 -2.80519032478 -2.58532762527 -28.1371479034 -2.63553452492 -2.67139530182 -28.1371479034 -2.63679218292 -2.67678141594 -28.1371479034 -2.3785905838 -2.76807832718 -28.1371479034 -2.37474179268 -2.75849151611 -28.1371479034 -2.18264889717 -2.80290412903 -28.1371479034 -2.18244338036 -2.80201435089 -28.1371479034 -2.05408549309 -2.82850813866 -28.1371479034 -2.05409359932 -2.8285472393 -28.1371479034 -1.84032952785 -2.87275886536 -28.1371479034 -1.84137153625 -2.87981653214 -28.1371479034 -1.67545151711 -2.9043161869 -28.1371479034 -1.67517089844 -2.90341615677 -28.1371479034 -1.46064448357 -2.91484427452 -28.1371479034 -1.45952570438 -2.92185139656 -28.1371479034 -1.1349902153 -2.90239548683 -28.1371479034 -1.13513112068 -2.89405369759 -28.1371479034 -0.927814722061 -2.86057305336 -28.1371479034 -0.92744332552 -2.86177587509 -28.1371479034 -0.775477349758 -2.82634401321 -28.1371479034 -0.776601672173 -2.82157635689 -28.1371479034 -0.567671895027 -2.76331090927 -28.1371479034 -0.564882993698 -2.77128696442 -28.1371479034 -0.401456296444 -2.71365666389 -28.1371479034 -0.402154713869 -2.71123838425 -28.1371479034 -0.225816980004 -2.62923741341 -28.1371479034 -0.22362844646 -2.63249921799 -28.1371479034 0.0388063341379 -2.48347020149 -28.1371479034 0.0365851037204 -2.47891545296 -28.1371479034 0.200287282467 -2.36796998978 -28.1371479034 0.201057568192 -2.3688185215 -28.1371479034 0.269177824259 -2.31380867958 -28.1371479034 0.266850620508 -2.31096029282 -28.1371479034 0.399786829948 -2.19166040421 -28.1371479034 0.405842423439 -2.19777345657 -28.1371479034 0.541262686253 -2.06206250191 -28.1371479034 0.539257824421 -2.0599064827 -28.1371479034 0.653455734253 -1.92496919632 -28.1371479034 0.656947910786 -1.92728340626 -28.1371479034 0.812934577465 -1.7108695507 -28.1371479034 0.807673096657 -1.70683419704 -28.1371479034 0.898073911667 -1.55929386616 -28.1371479034 0.898638188839 -1.55956161022 -28.1371479034 0.918853759766 -1.51996326447 -28.1371479034 0.917035579681 -1.51904571056 -28.1371479034 0.988771855831 -1.35690665245 -28.1371479034 0.99740922451 -1.36024987698 -28.1371479034 1.07229065895 -1.16409981251 -28.1371479034 1.07019424438 -1.16318368912 -28.1371479034 1.12172031403 -0.98770660162 -28.1371479034 1.12609827518 -0.988408029079 -28.1371479034 1.17602980137 -0.748752593994 -28.1371479034 1.17138957977 -0.747575640678 -28.1371479034 1.1951469183 -0.560218334198 -28.1371479034 1.19789493084 -0.560221552849 -28.1371479034 1.2090331316 -0.290837228298 -28.1371479034 1.19995129108 -0.290108501911 -28.1371479034 1.19083404541 -0.0784822851419 -28.1371479034 1.19105684757 -0.0784444436431 -28.1371479034 1.18830919266 -0.0575656294823 -28.1371479034 1.18713510036 -0.0577252954245 -28.1371479034 1.14778721333 0.143166556954 -28.1371479034 1.15734815598 0.14542068541 -28.1371479034 1.10285365582 0.372445315123 -28.1371479034 1.10217320919 0.372402340174 -28.1371479034 1.03826713562 0.568392395973 -28.1371479034 1.0423078537 0.570673167706 -28.1371479034 0.943348109722 0.799191117287 -28.1371479034 0.940662741661 0.798282384872 -28.1371479034 0.834711074829 0.992730677128 -28.1371479034 0.835467338562 0.993515193462 -28.1371479034 0.70338088274 1.20176172256 -28.1371479034 0.704187512398 1.20282888412 -28.1371479034 0.58247834444 1.36270403862 -28.1371479034 0.58161008358 1.36240923405 -28.1371479034 0.435544639826 1.5231872797 -28.1371479034 0.434225946665 1.52228999138 -28.1371479034 0.298114150763 1.65385580063 -28.1371479034 0.298940747976 1.65504360199 -28.1371479034 0.138960555196 1.78744924068 -28.1371479034 0.137807309628 1.78701281548 -28.1371479034 -0.0341825373471 1.90394437313 -28.1371479034 -0.0353068187833 1.90235376358 -28.1371479034 -0.231662005186 2.0207324028 -28.1371479034 -0.227388814092 2.02963638306 -28.1371479034 -0.465251773596 2.14821839333 -28.1371479034 -0.466836035252 2.146048069 -28.1371479034 -0.687356114388 2.22689270973 -28.1371479034 -0.687654495239 2.22777915001 -28.1371479034 -0.966687500477 2.3026163578 -28.1371479034 -0.967544615269 2.30126619339 -28.1371479034 -1.21408164501 2.34541583061 -28.1371479034 -1.21427404881 2.34725022316 -28.1371479034 -1.50341534615 2.37169671059 -28.1371479034 -1.50427031517 2.36482548714 -28.1371479034 -1.72845578194 2.36700439453 -28.1371479034 -1.72864890099 2.36870217323 -28.1371479034 -1.80646193027 2.36248373985 -28.1371479034 -1.80626761913 2.35970067978 -28.1371479034 -2.02501583099 2.32484483719 -28.1371479034 -2.02776169777 2.33571147919 -28.1371479034 -2.37214446068 2.25235104561 -28.1371479034 -2.37101435661 2.24523806572 -28.1371479034 -2.60520458221 2.16230845451 -28.1371479034 -2.60672235489 2.16494297981 -28.1371479034 -2.79389476776 2.07538962364 -28.1371479034 -2.78884577751 2.06502604485 -28.1371479034 -2.9913008213 1.9513052702 -28.1371479034 -2.99506783485 1.95728731155 -28.1371479034 -3.11240243912 1.88226759434 -28.1371479034 -3.11148285866 1.88063752651 -28.1371479034 -3.28162956238 1.7485909462 -28.1371479034 -3.28614449501 1.75292742252 -28.1371479034 -3.47922158241 1.57424342632 -28.1371479034 -3.47689080238 1.57145726681 -28.1371479034 -3.60716772079 1.42861318588 -28.1371479034 -3.60940408707 1.43015873432 -28.1371479034 -3.73355937004 1.27226448059 -28.1371479034 -3.73268079758 1.27116394043 -28.1371479034 -3.84375739098 1.10145664215 -28.1371479034 -3.84438586235 1.10146617889 -28.1371479034 -3.96025872231 0.889764010906 -28.1371479034 -3.95969080925 0.889005839825 -28.1371479034 -4.04294633865 0.703943192959 -28.1371479034 -4.04394960403 0.704082012177 -28.1371479034 -4.1236000061 0.475412249565 -28.1371479034 -4.12096834183 0.473918914795 -28.1371479034 -4.17151117325 0.27472743392 -28.1371479034 -4.1719212532 0.274715006351 -28.1371479034 -4.20936632156 0.0472430773079 -28.1371479034 -4.19861459732 0.0455029569566 -28.1371479034 -4.22354078293 -0.168517619371 -28.1371479034 -4.2242231369 -0.168474853039 -28.1371479034 -4.22497272491 -0.18098410964 -28.1371479034 -4.22491979599 -0.180990517139 -28.1371479034 -4.21988487244 -0.390185236931 -28.1371479034 -4.22703361511 -0.391401708126 # objtype='BezierSpline'; ncoords=88; nparts=88; closed=True; sep=' '; name='splines-002' -25.4357013702 -2.6895570755 -2.53805756569 -25.4357013702 -2.88611555099 -2.4343893528 -25.4357013702 -3.10787010193 -2.26696968079 -25.4357013702 -3.17767882347 -2.21431064606 -25.4357013702 -3.25921201706 -2.13767337799 -25.4357013702 -3.41180419922 -1.99514091015 -25.4357013702 -3.50195550919 -1.89565706253 -25.4357013702 -3.61677789688 -1.75500845909 -25.4357013702 -3.77435255051 -1.50880205631 -25.4357013702 -3.78855514526 -1.48774290085 -25.4357013702 -3.79587626457 -1.47383224964 -25.4357013702 -3.96691417694 -1.10553109646 -25.4357013702 -3.99933743477 -1.00741219521 -25.4357013702 -4.08097887039 -0.67650359869 -25.4357013702 -4.09949541092 -0.53016191721 -25.4357013702 -4.11846256256 -0.196392953396 -25.4357013702 -4.10814809799 -0.0183519106358 -25.4357013702 -4.05678319931 0.320813268423 -25.4357013702 -4.01046466827 0.502772688866 -25.4357013702 -3.91624999046 0.764686405659 -25.4357013702 -3.83366274834 0.938323915005 -25.4357013702 -3.69653630257 1.17386066914 -25.4357013702 -3.58971500397 1.32930266857 -25.4357013702 -3.39492964745 1.55772161484 -25.4357013702 -3.26655006409 1.68314445019 -25.4357013702 -2.99184894562 1.90018773079 -25.4357013702 -2.85304760933 1.99003005028 -25.4357013702 -2.48113441467 2.17657709122 -25.4357013702 -2.40724039078 2.20615148544 -25.4357013702 -2.05950546265 2.31104516983 -25.4357013702 -1.96133685112 2.32966685295 -25.4357013702 -1.62588655949 2.36898684502 -25.4357013702 -1.48312997818 2.37320542336 -25.4357013702 -1.16474449635 2.3576233387 -25.4357013702 -0.947366476059 2.32595777512 -25.4357013702 -0.758104622364 2.28476285934 -25.4357013702 -0.402146309614 2.16867399216 -25.4357013702 -0.373013705015 2.15686011314 -25.4357013702 -0.239598855376 2.08479976654 -25.4357013702 -0.00704544456676 1.96045255661 -25.4357013702 0.0359852649271 1.9337117672 -25.4357013702 0.341134667397 1.71841454506 -25.4357013702 0.428221106529 1.64244866371 -25.4357013702 0.619195461273 1.44546377659 -25.4357013702 0.750170946121 1.28971004486 -25.4357013702 0.863648831844 1.13745307922 -25.4357013702 1.02127814293 0.884128928185 -25.4357013702 1.06644749641 0.794420897961 -25.4357013702 1.19943273067 0.478516876698 -25.4357013702 1.20732915401 0.4564678967 -25.4357013702 1.23101019859 0.362078130245 -25.4357013702 1.29294610023 0.121762916446 -25.4357013702 1.30277991295 0.0655668750405 -25.4357013702 1.33657050133 -0.234185799956 -25.4357013702 1.3383654356 -0.386116534472 -25.4357013702 1.32699596882 -0.610283911228 -25.4357013702 1.29379820824 -0.847305238247 -25.4357013702 1.26531851292 -0.977698802948 -25.4357013702 1.19080364704 -1.22743737698 -25.4357013702 1.16960334778 -1.28051805496 -25.4357013702 1.03920185566 -1.55963790417 -25.4357013702 1.03228330612 -1.57146310806 -25.4357013702 0.978888452053 -1.65389668941 -25.4357013702 0.865633368492 -1.83022749424 -25.4357013702 0.8459815979 -1.8550041914 -25.4357013702 0.667403578758 -2.06464719772 -25.4357013702 0.568142235279 -2.15854287148 -25.4357013702 0.450560629368 -2.266289711 -25.4357013702 0.331078648567 -2.3519346714 -25.4357013702 0.225291416049 -2.42815995216 -25.4357013702 0.108475372195 -2.49786067009 -25.4357013702 0.000857936451212 -2.56216430664 -25.4357013702 -0.102533981204 -2.60921311378 -25.4357013702 -0.238387301564 -2.67265486717 -25.4357013702 -0.479323893785 -2.75116562843 -25.4357013702 -0.554002046585 -2.77546834946 -25.4357013702 -0.603156387806 -2.78542089462 -25.4357013702 -0.885342240334 -2.84079313278 -25.4357013702 -1.14061284065 -2.87056350708 -25.4357013702 -1.21373856068 -2.87881660461 -25.4357013702 -1.30156421661 -2.87691235542 -25.4357013702 -1.56504499912 -2.87204289436 -25.4357013702 -1.73003482819 -2.84361338615 -25.4357013702 -1.91637063026 -2.80606579781 -25.4357013702 -2.21955919266 -2.74111294746 -25.4357013702 -2.26479387283 -2.73097991943 -25.4357013702 -2.3680794239 -2.68618702888 -25.4357013702 -2.58052229881 -2.59489417076 -25.4357013702 -2.78796076775 -2.48646068573 -25.4357013702 -2.79263782501 -2.49378061295 -25.4357013702 -3.0029964447 -2.36012887955 -25.4357013702 -2.99697566032 -2.35065674782 -25.4357013702 -3.14276909828 -2.24063301086 -25.4357013702 -3.14429807663 -2.24244761467 -25.4357013702 -3.2203950882 -2.17830491066 -25.4357013702 -3.21838545799 -2.17592811584 -25.4357013702 -3.33539628983 -2.06628799438 -25.4357013702 -3.33860301971 -2.06945586205 -25.4357013702 -3.45886993408 -1.94735908508 -25.4357013702 -3.45819211006 -1.94652783871 -25.4357013702 -3.56114125252 -1.82685935497 -25.4357013702 -3.5636036396 -1.82840240002 -25.4357013702 -3.70238637924 -1.63684725761 -25.4357013702 -3.69409775734 -1.63094103336 -25.4357013702 -3.78132629395 -1.49818873405 -25.4357013702 -3.78204703331 -1.49862718582 -25.4357013702 -3.79258275032 -1.48100709915 -25.4357013702 -3.79239082336 -1.48087418079 -25.4357013702 -3.8859167099 -1.29191970825 -25.4357013702 -3.89230179787 -1.29399943352 -25.4357013702 -3.98590135574 -1.05757045746 -25.4357013702 -3.98504328728 -1.05702412128 -25.4357013702 -4.04648256302 -0.843780100346 -25.4357013702 -4.04987239838 -0.843764543533 -25.4357013702 -4.09444141388 -0.60411465168 -25.4357013702 -4.09277391434 -0.603565037251 -25.4357013702 -4.11472845078 -0.363803893328 -25.4357013702 -4.11855459213 -0.363272160292 -25.4357013702 -4.11841344833 -0.107369624078 -25.4357013702 -4.11740255356 -0.106944397092 -25.4357013702 -4.0903468132 0.152053996921 -25.4357013702 -4.09077978134 0.152914151549 -25.4357013702 -4.03817462921 0.412714451551 -25.4357013702 -4.03793287277 0.413113087416 -25.4357013702 -3.96974515915 0.635686516762 -25.4357013702 -3.96969223022 0.636367917061 -25.4357013702 -3.87933254242 0.85332775116 -25.4357013702 -3.87849473953 0.853372514248 -25.4357013702 -3.7701151371 1.05873918533 -25.4357013702 -3.76940822601 1.05882155895 -25.4357013702 -3.64610719681 1.25347042084 -25.4357013702 -3.64701557159 1.25456404686 -25.4357013702 -3.49851369858 1.4482588768 -25.4357013702 -3.49730801582 1.44817137718 -25.4357013702 -3.33372068405 1.62321865559 -25.4357013702 -3.33385133743 1.62397193909 -25.4357013702 -3.13526892662 1.79856932163 -25.4357013702 -3.13400006294 1.79836809635 -25.4357013702 -2.92471528053 1.94827401638 -25.4357013702 -2.92469573021 1.94903695583 -25.4357013702 -2.67274665833 2.09318852425 -25.4357013702 -2.67068433762 2.09128975868 -25.4357013702 -2.44487476349 2.19289207458 -25.4357013702 -2.44476413727 2.19301128387 -25.4357013702 -2.23600506783 2.26611471176 -25.4357013702 -2.23565101624 2.26789903641 -25.4357013702 -2.01104784012 2.32291460037 -25.4357013702 -2.01068902016 2.32210326195 -25.4357013702 -1.79451692104 2.35523319244 -25.4357013702 -1.79414904118 2.35666275024 -25.4357013702 -1.55473542213 2.37419819832 -25.4357013702 -1.55448114872 2.37389612198 -25.4357013702 -1.32387673855 2.37166380882 -25.4357013702 -1.32320022583 2.37300634384 -25.4357013702 -1.05554759502 2.34702253342 -25.4357013702 -1.05537259579 2.34555411339 -25.4357013702 -0.852133452892 2.30867886543 -25.4357013702 -0.851456999779 2.31007552147 -25.4357013702 -0.577654004097 2.23583316803 -25.4357013702 -0.577877521515 2.23287177086 -25.4357013702 -0.387391269207 2.16328382492 -25.4357013702 -0.387211859226 2.16354846954 -25.4357013702 -0.304530620575 2.12459921837 -25.4357013702 -0.306381732225 2.12068963051 -25.4357013702 -0.123453363776 2.02238202095 -25.4357013702 -0.121179878712 2.02633738518 -25.4357013702 0.0148814702407 1.94779515266 -25.4357013702 0.014878497459 1.94769871235 -25.4357013702 0.191571831703 1.8306081295 -25.4357013702 0.194489911199 1.83361196518 -25.4357013702 0.386512875557 1.68276751041 -25.4357013702 0.386339515448 1.68218302727 -25.4357013702 0.527653217316 1.54811429977 -25.4357013702 0.527306973934 1.54720640182 -25.4357013702 0.687352478504 1.36999762058 -25.4357013702 0.687024235725 1.36944103241 -25.4357013702 0.809094309807 1.21531164646 -25.4357013702 0.810198485851 1.21582448483 -25.4357013702 0.947630643845 1.01431500912 -25.4357013702 0.948325693607 1.01408183575 -25.4357013702 1.04583621025 0.840382754803 -25.4357013702 1.04541289806 0.839990437031 -25.4357013702 1.13822996616 0.638910651207 -25.4357013702 1.13729560375 0.638164103031 -25.4357013702 1.20367860794 0.467608213425 -25.4357013702 1.20393025875 0.467659205198 -25.4357013702 1.22145211697 0.409966230392 -25.4357013702 1.21901810169 0.409234493971 -25.4357013702 1.26159179211 0.241822257638 -25.4357013702 1.26676785946 0.242955118418 -25.4357013702 1.29896402359 0.0939027294517 -25.4357013702 1.29872381687 0.0937886089087 -25.4357013702 1.32422661781 -0.0836553201079 -25.4357013702 1.32723200321 -0.0838400796056 -25.4357013702 1.34127426147 -0.30991473794 -25.4357013702 1.33984076977 -0.310197293758 -25.4357013702 1.33618593216 -0.498268336058 -25.4357013702 1.33762180805 -0.498670637608 -25.4357013702 1.31566572189 -0.729296147823 -25.4357013702 1.3148651123 -0.729594230652 -25.4357013702 1.28205001354 -0.912947952747 -25.4357013702 1.28197860718 -0.913126468658 -25.4357013702 1.23278689384 -1.10378742218 -25.4357013702 1.23359870911 -1.10449564457 -25.4357013702 1.18141794205 -1.25440049171 -25.4357013702 1.18095171452 -1.25430166721 -25.4357013702 1.10843575001 -1.42182385921 -25.4357013702 1.11069619656 -1.4233802557 -25.4357013702 1.0360224247 -1.56569731236 -25.4357013702 1.03587496281 -1.56563210487 -25.4357013702 1.00653517246 -1.61326467991 -25.4357013702 1.0055065155 -1.61262869835 -25.4357013702 0.922091662884 -1.74195289612 -25.4357013702 0.926504731178 -1.74509656429 -25.4357013702 0.8564478755 -1.8430737257 -25.4357013702 0.856021225452 -1.84279155731 -25.4357013702 0.758553743362 -1.96135568619 -25.4357013702 0.762063860893 -1.96492433548 -25.4357013702 0.620437860489 -2.11412477493 -25.4357013702 0.618141770363 -2.11199116707 -25.4357013702 0.509781956673 -2.21287870407 -25.4357013702 0.512361586094 -2.21612453461 -25.4357013702 0.393594294786 -2.31253051758 -25.4357013702 0.390766590834 -2.30903840065 -25.4357013702 0.278137981892 -2.38998174667 -25.4357013702 0.27973112464 -2.39240098 -25.4357013702 0.168496370316 -2.46546602249 -25.4357013702 0.166872471571 -2.4629919529 -25.4357013702 0.0546565875411 -2.52999567986 -25.4357013702 0.0562889426947 -2.53310728073 -25.4357013702 -0.0493680685759 -2.58849287033 -25.4357013702 -0.050955042243 -2.58543491364 -25.4357013702 -0.17061509192 -2.64059901237 -25.4357013702 -0.16878426075 -2.64518094063 -25.4357013702 -0.356022357941 -2.71908807755 -25.4357013702 -0.35884809494 -2.7119333744 -25.4357013702 -0.516660630703 -2.76332402229 -25.4357013702 -0.516089737415 -2.76549649239 -25.4357013702 -0.57821315527 -2.7818365097 -25.4357013702 -0.578564465046 -2.7805185318 -25.4357013702 -0.744164824486 -2.81353068352 -25.4357013702 -0.743387937546 -2.81862235069 -25.4357013702 -1.01220774651 -2.86060738564 -25.4357013702 -1.01295030117 -2.855915308 -25.4357013702 -1.17716789246 -2.87475800514 -25.4357013702 -1.17706394196 -2.87715220451 -25.4357013702 -1.25751805305 -2.88080358505 -25.4357013702 -1.25765001774 -2.87779426575 -25.4357013702 -1.43330037594 -2.87426686287 -25.4357013702 -1.43425011635 -2.88444757462 -25.4357013702 -1.64814066887 -2.86416220665 -25.4357013702 -1.64775681496 -2.85898852348 -25.4357013702 -1.82344889641 -2.82615709305 -25.4357013702 -1.82332074642 -2.82540726662 -25.4357013702 -2.06815743446 -2.7745153904 -25.4357013702 -2.06811976433 -2.77429580688 -25.4357013702 -2.24219965935 -2.73615193367 -25.4357013702 -2.24285316467 -2.73812413216 -25.4357013702 -2.31807971001 -2.71362948418 -25.4357013702 -2.31639957428 -2.70849752426 -25.4357013702 -2.47422456741 -2.64036393166 -25.4357013702 -2.47615098953 -2.64443802834 -25.4357013702 -2.63602352142 -2.56854844093 -25.4357013702 -2.63510847092 -2.56660699844 # objtype='BezierSpline'; ncoords=95; nparts=95; closed=True; sep=' '; name='splines-003' -22.7342567444 -3.665340662 -1.52868700027 -22.7342567444 -3.52168464661 -1.73400735855 -22.7342567444 -3.38962316513 -1.88561785221 -22.7342567444 -3.30753016472 -1.97450625896 -22.7342567444 -3.14122438431 -2.11126995087 -22.7342567444 -3.05288791656 -2.18597245216 -22.7342567444 -2.99766802788 -2.22393703461 -22.7342567444 -2.80608010292 -2.34875226021 -22.7342567444 -2.60739302635 -2.45945072174 -22.7342567444 -2.55763459206 -2.48632740974 -22.7342567444 -2.46150040627 -2.52392482758 -22.7342567444 -2.26315903664 -2.60279178619 -22.7342567444 -2.1377453804 -2.64221310616 -22.7342567444 -1.9575855732 -2.69615650177 -22.7342567444 -1.64299762249 -2.75562310219 -22.7342567444 -1.63337576389 -2.75744771957 -22.7342567444 -1.62796497345 -2.75803232193 -22.7342567444 -1.30006575584 -2.79029536247 -22.7342567444 -1.08856034279 -2.7893652916 -22.7342567444 -0.968700885773 -2.7893037796 -22.7342567444 -0.793032467365 -2.77186155319 -22.7342567444 -0.653688251972 -2.75726914406 -22.7342567444 -0.561613380909 -2.73864650726 -22.7342567444 -0.346830278635 -2.69687199593 -22.7342567444 -0.0529097542167 -2.60974597931 -22.7342567444 -0.0470233932137 -2.60806679726 -22.7342567444 -0.0401837192476 -2.60531449318 -22.7342567444 0.218728363514 -2.49800109863 -22.7342567444 0.428085356951 -2.38439750671 -22.7342567444 0.471748024225 -2.36105847359 -22.7342567444 0.518982350826 -2.32942342758 -22.7342567444 0.707642376423 -2.20000481606 -22.7342567444 0.854211688042 -2.06584644318 -22.7342567444 0.90982145071 -2.01604604721 -22.7342567444 0.951926231384 -1.9690746069 -22.7342567444 1.09358370304 -1.79884672165 -22.7342567444 1.24412047863 -1.56145524979 -22.7342567444 1.25659251213 -1.53945958614 -22.7342567444 1.27107679844 -1.50364100933 -22.7342567444 1.37006056309 -1.27173578739 -22.7342567444 1.41196882725 -1.12326598167 -22.7342567444 1.44278299809 -0.99519264698 -22.7342567444 1.47996354103 -0.748889625072 -22.7342567444 1.48710238934 -0.693478882313 -22.7342567444 1.49266338348 -0.398252516985 -22.7342567444 1.49238169193 -0.39030277729 -22.7342567444 1.48898649216 -0.359590500593 -22.7342567444 1.45381987095 -0.0253495126963 -22.7342567444 1.43832921982 0.0546556152403 -22.7342567444 1.36509621143 0.340010762215 -22.7342567444 1.31681454182 0.478119343519 -22.7342567444 1.21381747723 0.720122933388 -22.7342567444 1.14250648022 0.861270666122 -22.7342567444 1.01187336445 1.07671141624 -22.7342567444 0.913246333599 1.21723031998 -22.7342567444 0.742503225803 1.41766738892 -22.7342567444 0.621692121029 1.5417932272 -22.7342567444 0.427447646856 1.71916174889 -22.7342567444 0.295939594507 1.81823289394 -22.7342567444 0.0328380651772 1.98189771175 -22.7342567444 -0.068064250052 2.03740406036 -22.7342567444 -0.396174013615 2.18459486961 -22.7342567444 -0.453085452318 2.20489215851 -22.7342567444 -0.81722933054 2.30958175659 -22.7342567444 -0.888540625572 2.32400298119 -22.7342567444 -1.25305211544 2.36773920059 -22.7342567444 -1.36624455452 2.37201285362 -22.7342567444 -1.65024697781 2.36397457123 -22.7342567444 -1.88692951202 2.33061003685 -22.7342567444 -2.02805948257 2.30277514458 -22.7342567444 -2.36925053596 2.19899868965 -22.7342567444 -2.38306808472 2.19373965263 -22.7342567444 -2.45796442032 2.15564107895 -22.7342567444 -2.75823092461 2.00572609901 -22.7342567444 -2.80289959908 1.97806441784 -22.7342567444 -3.07688212395 1.77781510353 -22.7342567444 -3.1548705101 1.71051168442 -22.7342567444 -3.35001277924 1.51563704014 -22.7342567444 -3.44042515755 1.40870594978 -22.7342567444 -3.56781578064 1.23202610016 -22.7342567444 -3.6682138443 1.07677733898 -22.7342567444 -3.76948094368 0.888093173504 -22.7342567444 -3.85273528099 0.701070129871 -22.7342567444 -3.90687823296 0.535101234913 -22.7342567444 -3.96370840073 0.331245034933 -22.7342567444 -3.99269080162 0.157969370484 -22.7342567444 -4.02326440811 -0.0780460834503 -22.7342567444 -4.02586603165 -0.259950369596 -22.7342567444 -4.01358795166 -0.498136848211 -22.7342567444 -3.98094749451 -0.70535838604 -22.7342567444 -3.95380043983 -0.853124678135 -22.7342567444 -3.85608267784 -1.14807879925 -22.7342567444 -3.84215760231 -1.18554162979 -22.7342567444 -3.79190564156 -1.28693556786 -22.7342567444 -3.70117139816 -1.47343862057 -22.7342567444 -3.59533905983 -1.63257777691 -22.7342567444 -3.59874606133 -1.63543879986 -22.7342567444 -3.4598531723 -1.81309556961 -22.7342567444 -3.45674204826 -1.81078863144 -22.7342567444 -3.34923148155 -1.93064939976 -22.7342567444 -3.35141730309 -1.93307065964 -22.7342567444 -3.22943210602 -2.04824185371 -22.7342567444 -3.22390365601 -2.04232001305 -22.7342567444 -3.09680175781 -2.14831590652 -22.7342567444 -3.09880495071 -2.15091133118 -22.7342567444 -3.02629089355 -2.20628118515 -22.7342567444 -3.02550983429 -2.20530128479 -22.7342567444 -2.90266537666 -2.28752684593 -22.7342567444 -2.90391421318 -2.28972601891 -22.7342567444 -2.70876574516 -2.40746498108 -22.7342567444 -2.70709395409 -2.40475320816 -22.7342567444 -2.58260273933 -2.47305107117 -22.7342567444 -2.58324122429 -2.47445869446 -22.7342567444 -2.51089549065 -2.50799107552 -22.7342567444 -2.50951385498 -2.50499033928 -22.7342567444 -2.36221885681 -2.5630774498 -22.7342567444 -2.36365032196 -2.56707382202 -22.7342567444 -2.2012655735 -2.62479090691 -22.7342567444 -2.20058369637 -2.62293052673 -22.7342567444 -2.04785346985 -2.6697974205 -22.7342567444 -2.04882287979 -2.67393803596 -22.7342567444 -1.802262187 -2.73398160934 -22.7342567444 -1.80028295517 -2.72584414482 -22.7342567444 -1.63818645477 -2.75653409958 -22.7342567444 -1.63821542263 -2.75672864914 -22.7342567444 -1.63068628311 -2.75784730911 -22.7342567444 -1.63067173958 -2.75775289536 -22.7342567444 -1.46409606934 -2.77494621277 -22.7342567444 -1.46441042423 -2.78259181976 -22.7342567444 -1.19456660748 -2.79524064064 -22.7342567444 -1.19431352615 -2.78962492943 -22.7342567444 -1.0286308527 -2.78921818733 -22.7342567444 -1.02848398685 -2.79227972031 -22.7342567444 -0.880650758743 -2.78492069244 -22.7342567444 -0.880842626095 -2.78081870079 -22.7342567444 -0.723341286182 -2.76475262642 -22.7342567444 -0.722855687141 -2.76786088943 -22.7342567444 -0.607312440872 -2.75016736984 -22.7342567444 -0.607684910297 -2.74778580666 -22.7342567444 -0.454301297665 -2.71735858917 -22.7342567444 -0.452972322702 -2.72286224365 -22.7342567444 -0.198119401932 -2.66045832634 -22.7342567444 -0.200090244412 -2.652551651 -22.7342567444 -0.0499709695578 -2.6088912487 -22.7342567444 -0.0499146468937 -2.60905790329 -22.7342567444 -0.0435410141945 -2.60687303543 -22.7342567444 -0.043596342206 -2.60670828819 -22.7342567444 0.0895464941859 -2.55232906342 -22.7342567444 0.0924153029919 -2.5582473278 -22.7342567444 0.326077967882 -2.44679951668 -22.7342567444 0.323229551315 -2.44087004662 -22.7342567444 0.449879825115 -2.37265968323 -22.7342567444 0.450548499823 -2.37378072739 -22.7342567444 0.496090680361 -2.34644985199 -22.7342567444 0.495453923941 -2.34537196159 -22.7342567444 0.613669455051 -2.26524090767 -22.7342567444 0.618287026882 -2.27097725868 -22.7342567444 0.785247504711 -2.13836526871 -22.7342567444 0.780564427376 -2.13252520561 -22.7342567444 0.88188034296 -2.04079580307 -22.7342567444 0.883462309837 -2.04239273071 -22.7342567444 0.932095527649 -1.9937826395 -22.7342567444 0.93131262064 -1.99293935299 -22.7342567444 1.02429544926 -1.88529133797 -22.7342567444 1.02851963043 -1.88816022873 -22.7342567444 1.17616927624 -1.6854814291 -22.7342567444 1.1718236208 -1.68193411827 -22.7342567444 1.25062382221 -1.55061781406 -22.7342567444 1.25110471249 -1.55081892014 -22.7342567444 1.26497793198 -1.52210259438 -22.7342567444 1.26366388798 -1.52147936821 -22.7342567444 1.31945443153 -1.38722538948 -22.7342567444 1.32819056511 -1.39037823677 -22.7342567444 1.39567792416 -1.19914662838 -22.7342567444 1.39246988297 -1.19788122177 -22.7342567444 1.42861843109 -1.05955398083 -22.7342567444 1.43016386032 -1.05977404118 -22.7342567444 1.46664524078 -0.8730712533 -22.7342567444 1.46271121502 -0.872228264809 -22.7342567444 1.48383307457 -0.721226215363 -22.7342567444 1.48505461216 -0.72129625082 -22.7342567444 1.49792528152 -0.546457707882 -22.7342567444 1.49388718605 -0.545832514763 -22.7342567444 1.49263036251 -0.394276738167 -22.7342567444 1.49267065525 -0.394266843796 -22.7342567444 1.49125933647 -0.374904721975 -22.7342567444 1.49064362049 -0.374951004982 -22.7342567444 1.47096264362 -0.192517533898 -22.7342567444 1.47858321667 -0.191399216652 -22.7342567444 1.44781553745 0.0149126797915 -22.7342567444 1.44726622105 0.0149210747331 -22.7342567444 1.40602064133 0.19830211997 -22.7342567444 1.40770971775 0.199147373438 -22.7342567444 1.34393358231 0.409966021776 -22.7342567444 1.34320855141 0.409937262535 -22.7342567444 1.26936650276 0.60068911314 -22.7342567444 1.26921701431 0.600934386253 -22.7342567444 1.1805075407 0.791787028313 -22.7342567444 1.18083238602 0.792178094387 -22.7342567444 1.08144450188 0.971351087093 -22.7342567444 1.08071780205 0.971294999123 -22.7342567444 0.964963674545 1.14854073524 -22.7342567444 0.965734779835 1.14942872524 -22.7342567444 0.832744181156 1.3212184906 -22.7342567444 0.831100642681 1.32038676739 -22.7342567444 0.684219777584 1.48166298866 -22.7342567444 0.683872401714 1.48156249523 -22.7342567444 0.52726495266 1.63325977325 -22.7342567444 0.528532445431 1.63525104523 -22.7342567444 0.364173978567 1.77168536186 -22.7342567444 0.363768339157 1.771723032 -22.7342567444 0.168293237686 1.90575933456 -22.7342567444 0.166485011578 1.90364539623 -22.7342567444 -0.0168340150267 2.01098155975 -22.7342567444 -0.0165703240782 2.01174354553 -22.7342567444 -0.228862926364 2.1175339222 -22.7342567444 -0.229467734694 2.11759662628 -22.7342567444 -0.42418423295 2.19585204124 -22.7342567444 -0.424340009689 2.19564414024 -22.7342567444 -0.633340537548 2.26288437843 -22.7342567444 -0.633349359035 2.26463365555 -22.7342567444 -0.852537810802 2.31821274757 -22.7342567444 -0.852653622627 2.31823086739 -22.7342567444 -1.06962883472 2.35312962532 -22.7342567444 -1.07020807266 2.35334229469 -22.7342567444 -1.30946683884 2.37218117714 -22.7342567444 -1.30963945389 2.3717455864 -22.7342567444 -1.50822365284 2.37268304825 -22.7342567444 -1.50891268253 2.37589883804 -22.7342567444 -1.76914930344 2.35394287109 -22.7342567444 -1.76913261414 2.35051393509 -22.7342567444 -1.95782208443 2.3186314106 -22.7342567444 -1.95837116241 2.32019877434 -22.7342567444 -2.20082831383 2.25957918167 -22.7342567444 -2.2006278038 2.25665664673 -22.7342567444 -2.37624120712 2.19660830498 -22.7342567444 -2.37631940842 2.19673013687 -22.7342567444 -2.42142558098 2.17674231529 -22.7342567444 -2.42044520378 2.17454957962 -22.7342567444 -2.60781431198 2.08012127876 -22.7342567444 -2.61183166504 2.08737850189 -22.7342567444 -2.7811498642 1.99294340611 -22.7342567444 -2.78112792969 1.99273049831 -22.7342567444 -2.94352507591 1.8833347559 -22.7342567444 -2.94415688515 1.88330709934 -22.7342567444 -3.11717128754 1.74579262733 -22.7342567444 -3.1171503067 1.74553561211 -22.7342567444 -3.25585246086 1.61674785614 -22.7342567444 -3.256711483 1.61700451374 -22.7342567444 -3.39738702774 1.46416711807 -22.7342567444 -3.39734768867 1.46383488178 -22.7342567444 -3.50743174553 1.3229534626 -22.7342567444 -3.50639772415 1.32192182541 -22.7342567444 -3.61994767189 1.15572226048 -22.7342567444 -3.62125658989 1.15631556511 -22.7342567444 -3.72260212898 0.984651982784 -22.7342567444 -3.72239232063 0.984172642231 -22.7342567444 -3.81449699402 0.796242535114 -22.7342567444 -3.81604909897 0.796481490135 -22.7342567444 -3.88402032852 0.619705796242 -22.7342567444 -3.88162231445 0.618634641171 -22.7342567444 -3.93749427795 0.433838605881 -22.7342567444 -3.94077277184 0.434391528368 -22.7342567444 -3.98274827003 0.245618656278 -22.7342567444 -3.97980284691 0.244845047593 -22.7342567444 -4.01014947891 0.0402838326991 -22.7342567444 -4.01477003098 0.0404485613108 -22.7342567444 -4.02975749969 -0.168626010418 -22.7342567444 -4.02755689621 -0.16905388236 -22.7342567444 -4.0236492157 -0.379116564989 -22.7342567444 -4.02593517303 -0.379690736532 -22.7342567444 -4.00272798538 -0.60231679678 -22.7342567444 -3.99858379364 -0.601972103119 -22.7342567444 -3.96831655502 -0.779402315617 -22.7342567444 -3.97239923477 -0.780529022217 -22.7342567444 -3.91533493996 -1.00326442719 -22.7342567444 -3.90757679939 -1.00152766705 -22.7342567444 -3.8494591713 -1.1669293642 -22.7342567444 -3.85007596016 -1.16722333431 -22.7342567444 -3.81973767281 -1.23740828037 -22.7342567444 -3.8168451786 -1.23614704609 -22.7342567444 -3.74619698524 -1.38001942635 -22.7342567444 -3.75206828117 -1.38330984116 -22.7342567444 -3.68501162529 -1.50205433369 -22.7342567444 -3.68373584747 -1.50138616562 # objtype='BezierSpline'; ncoords=131; nparts=131; closed=True; sep=' '; name='splines-004' -20.0328102112 0.944701313972 1.30499637127 -20.0328102112 0.907776117325 1.34837639332 -20.0328102112 0.7345687747 1.53992784023 -20.0328102112 0.671517014503 1.60352528095 -20.0328102112 0.515030026436 1.75351762772 -20.0328102112 0.407630115747 1.83899629116 -20.0328102112 0.264625608921 1.94625747204 -20.0328102112 0.0943625941873 2.04499053955 -20.0328102112 -0.011011261493 2.10142636299 -20.0328102112 -0.245708078146 2.20837306976 -20.0328102112 -0.261864930391 2.21659851074 -20.0328102112 -0.31401091814 2.23406839371 -20.0328102112 -0.508593440056 2.30445218086 -20.0328102112 -0.572800397873 2.32200312614 -20.0328102112 -0.750593185425 2.37591004372 -20.0328102112 -0.89830160141 2.41193413734 -20.0328102112 -0.993186593056 2.43027448654 -20.0328102112 -1.14113724232 2.43706917763 -20.0328102112 -1.1935005188 2.44096398354 -20.0328102112 -1.22807753086 2.44206118584 -20.0328102112 -1.39183557034 2.44740343094 -20.0328102112 -1.56353974342 2.44114589691 -20.0328102112 -1.60425817966 2.43659329414 -20.0328102112 -1.76392686367 2.40390610695 -20.0328102112 -1.85425722599 2.38601589203 -20.0328102112 -1.87065112591 2.38275933266 -20.0328102112 -2.08403658867 2.32638478279 -20.0328102112 -2.09843564034 2.32262682915 -20.0328102112 -2.09946393967 2.32223939896 -20.0328102112 -2.30337357521 2.2242500782 -20.0328102112 -2.32107448578 2.21541619301 -20.0328102112 -2.50283575058 2.12232255936 -20.0328102112 -2.53560018539 2.09934663773 -20.0328102112 -2.72456789017 1.96481525898 -20.0328102112 -2.74973702431 1.94582474232 -20.0328102112 -2.91526937485 1.80099368095 -20.0328102112 -2.92313170433 1.79329895973 -20.0328102112 -3.02804088593 1.69213938713 -20.0328102112 -3.0934715271 1.62913680077 -20.0328102112 -3.0983440876 1.62418866158 -20.0328102112 -3.24470901489 1.44637441635 -20.0328102112 -3.26001834869 1.42733585835 -20.0328102112 -3.32090449333 1.34164869785 -20.0328102112 -3.43625545502 1.18325853348 -20.0328102112 -3.47632098198 1.11179339886 -20.0328102112 -3.54941678047 0.976552963257 -20.0328102112 -3.63681054115 0.802036643028 -20.0328102112 -3.64401197433 0.787395238876 -20.0328102112 -3.6481730938 0.774877905846 -20.0328102112 -3.71355247498 0.581826448441 -20.0328102112 -3.75224757195 0.451031893492 -20.0328102112 -3.78278660774 0.347555726767 -20.0328102112 -3.80587434769 0.191188246012 -20.0328102112 -3.8203766346 0.0625077113509 -20.0328102112 -3.84219288826 -0.153353646398 -20.0328102112 -3.85038304329 -0.282373547554 -20.0328102112 -3.83762788773 -0.486231476068 -20.0328102112 -3.82079529762 -0.622537970543 -20.0328102112 -3.80200195312 -0.770540833473 -20.0328102112 -3.78920197487 -0.871313393116 -20.0328102112 -3.75374650955 -1.01964712143 -20.0328102112 -3.69484972954 -1.19052147865 -20.0328102112 -3.66317486763 -1.28153073788 -20.0328102112 -3.60521888733 -1.42206835747 -20.0328102112 -3.56332349777 -1.52881860733 -20.0328102112 -3.52525305748 -1.58834826946 -20.0328102112 -3.42386960983 -1.7473129034 -20.0328102112 -3.28124165535 -1.93117177486 -20.0328102112 -3.27021670341 -1.94438552856 -20.0328102112 -3.25601363182 -1.95851051807 -20.0328102112 -3.09388828278 -2.11906313896 -20.0328102112 -2.91931962967 -2.25487923622 -20.0328102112 -2.89669895172 -2.27139639854 -20.0328102112 -2.87326550484 -2.28774094582 -20.0328102112 -2.69776725769 -2.41098570824 -20.0328102112 -2.52454948425 -2.51818299294 -20.0328102112 -2.49057888985 -2.53816628456 -20.0328102112 -2.44909262657 -2.55995869637 -20.0328102112 -2.20578193665 -2.69314932823 -20.0328102112 -1.94139528275 -2.80852890015 -20.0328102112 -1.90833890438 -2.82267999649 -20.0328102112 -1.86390316486 -2.83769130707 -20.0328102112 -1.60170984268 -2.92165088654 -20.0328102112 -1.39261734486 -2.97120666504 -20.0328102112 -1.28913366795 -2.99525475502 -20.0328102112 -1.16613066196 -3.01787948608 -20.0328102112 -0.990492045879 -3.04467105865 -20.0328102112 -0.870650649071 -3.047301054 -20.0328102112 -0.676068127155 -3.05382966995 -20.0328102112 -0.423188239336 -3.05105352402 -20.0328102112 -0.379077672958 -3.04822969437 -20.0328102112 -0.344460427761 -3.04078054428 -20.0328102112 -0.0772589445114 -2.98441576958 -20.0328102112 0.0818115845323 -2.94929718971 -20.0328102112 0.117782920599 -2.94011211395 -20.0328102112 0.175414860249 -2.91225457191 -20.0328102112 0.302585184574 -2.85191082954 -20.0328102112 0.37444216013 -2.81612110138 -20.0328102112 0.48910728097 -2.76155996323 -20.0328102112 0.666146755219 -2.67321872711 -20.0328102112 0.679850935936 -2.66520118713 -20.0328102112 0.828257441521 -2.56572937965 -20.0328102112 0.916689991951 -2.49153804779 -20.0328102112 0.976235568523 -2.44335079193 -20.0328102112 1.08840990067 -2.35419654846 -20.0328102112 1.12952589989 -2.3213455677 -20.0328102112 1.15146887302 -2.30269646645 -20.0328102112 1.27528488636 -2.18923735619 -20.0328102112 1.37106621265 -2.05962324142 -20.0328102112 1.42226099968 -1.99065065384 -20.0328102112 1.50718700886 -1.82794094086 -20.0328102112 1.53910648823 -1.77006363869 -20.0328102112 1.55026721954 -1.74226987362 -20.0328102112 1.62507414818 -1.56034553051 -20.0328102112 1.6788932085 -1.37873291969 -20.0328102112 1.68715262413 -1.34646105766 -20.0328102112 1.7068914175 -1.24397397041 -20.0328102112 1.74503052235 -1.03823292255 -20.0328102112 1.75035607815 -0.964160680771 -20.0328102112 1.76284372807 -0.731577157974 -20.0328102112 1.75374245644 -0.542639672756 -20.0328102112 1.74963867664 -0.436441451311 -20.0328102112 1.70852184296 -0.207464188337 -20.0328102112 1.70007288456 -0.157455474138 -20.0328102112 1.69474339485 -0.136778473854 -20.0328102112 1.58679032326 0.252290517092 -20.0328102112 1.58010494709 0.269972205162 -20.0328102112 1.40221345425 0.641913533211 -20.0328102112 1.39006757736 0.664470553398 -20.0328102112 1.189473629 0.988417446613 -20.0328102112 1.1685847044 1.01897287369 -20.0328102112 0.926691710949 1.32705616951 -20.0328102112 0.926559388638 1.32696771622 -20.0328102112 0.822626233101 1.44542765617 -20.0328102112 0.823325991631 1.44619119167 -20.0328102112 0.703789710999 1.5724337101 -20.0328102112 0.703443109989 1.57213354111 -20.0328102112 0.594242215157 1.67950665951 -20.0328102112 0.596552312374 1.6822732687 -20.0328102112 0.463406383991 1.79863274097 -20.0328102112 0.461932122707 1.79703605175 -20.0328102112 0.336911946535 1.89364159107 -20.0328102112 0.339036971331 1.89702367783 -20.0328102112 0.182697087526 2.00046491623 -20.0328102112 0.180303826928 1.99707639217 -20.0328102112 0.0421674363315 2.0740904808 -20.0328102112 0.04252576828 2.07492589951 -20.0328102112 -0.126525461674 2.15860533714 -20.0328102112 -0.129572957754 2.15238308907 -20.0328102112 -0.253871798515 2.21230888367 -20.0328102112 -0.253527969122 2.2131023407 -20.0328102112 -0.287153661251 2.22720360756 -20.0328102112 -0.288045585155 2.22502446175 -20.0328102112 -0.411707282066 2.26809716225 -20.0328102112 -0.410048276186 2.27321624756 -20.0328102112 -0.540293574333 2.31450009346 -20.0328102112 -0.540823936462 2.31278705597 -20.0328102112 -0.662051260471 2.34772658348 -20.0328102112 -0.661021232605 2.35142827034 -20.0328102112 -0.823894560337 2.39594483376 -20.0328102112 -0.824055790901 2.39571475983 -20.0328102112 -0.945495188236 2.42224383354 -20.0328102112 -0.945330500603 2.42458105087 -20.0328102112 -1.06652796268 2.43899989128 -20.0328102112 -1.0672249794 2.43262410164 -20.0328102112 -1.1673412323 2.43864512444 -20.0328102112 -1.16728937626 2.43957400322 -20.0328102112 -1.21076953411 2.4418797493 -20.0328102112 -1.21078932285 2.44150495529 -20.0328102112 -1.30995774269 2.44469594955 -20.0328102112 -1.30996191502 2.44755959511 -20.0328102112 -1.47769331932 2.44723963737 -20.0328102112 -1.47792518139 2.44748306274 -20.0328102112 -1.58395564556 2.43963479996 -20.0328102112 -1.58404362202 2.43978571892 -20.0328102112 -1.68466806412 2.42389416695 -20.0328102112 -1.68404102325 2.41999387741 -20.0328102112 -1.80906295776 2.39481639862 -20.0328102112 -1.80909454823 2.39497375488 -20.0328102112 -1.86245465279 2.38438987732 -20.0328102112 -1.86251270771 2.38464069366 -20.0328102112 -1.97811675072 2.35791516304 -20.0328102112 -1.97730183601 2.35441207886 -20.0328102112 -2.09123325348 2.32449507713 -20.0328102112 -2.09135437012 2.32487797737 -20.0328102112 -2.09895849228 2.3224606514 -20.0328102112 -2.09895920753 2.32245516777 -20.0328102112 -2.20336747169 2.27780127525 -20.0328102112 -2.20179009438 2.27400302887 -20.0328102112 -2.31225657463 2.21989941597 -20.0328102112 -2.31224727631 2.21987915039 -20.0328102112 -2.41219568253 2.16934514046 -20.0328102112 -2.41559505463 2.17490816116 -20.0328102112 -2.51993131638 2.11201786995 -20.0328102112 -2.5192592144 2.11089277267 -20.0328102112 -2.63032245636 2.03241848946 -20.0328102112 -2.63103365898 2.03337645531 -20.0328102112 -2.7372815609 1.95549607277 -20.0328102112 -2.73751235008 1.95576274395 -20.0328102112 -2.83501434326 1.87649810314 -20.0328102112 -2.83458805084 1.87566196918 -20.0328102112 -2.91930484772 1.79725897312 -20.0328102112 -2.91918635368 1.7971316576 -20.0328102112 -2.97539782524 1.74252521992 -20.0328102112 -2.97556829453 1.74270057678 -20.0328102112 -3.06074500084 1.66062653065 -20.0328102112 -3.06118083 1.66106760502 -20.0328102112 -3.09594035149 1.62669551373 -20.0328102112 -3.0960226059 1.62676608562 -20.0328102112 -3.17533349991 1.53871035576 -20.0328102112 -3.17203736305 1.53569722176 -20.0328102112 -3.25241780281 1.43689918518 -20.0328102112 -3.25265336037 1.43707418442 -20.0328102112 -3.29170775414 1.38543486595 -20.0328102112 -3.29021239281 1.38431322575 -20.0328102112 -3.37811589241 1.26211977005 -20.0328102112 -3.38346266747 1.26558470726 -20.0328102112 -3.458329916 1.14883518219 -20.0328102112 -3.45656561852 1.14767861366 -20.0328102112 -3.51338934898 1.04445970058 -20.0328102112 -3.51393389702 1.04472744465 -20.0328102112 -3.59446573257 0.889998555183 -20.0328102112 -3.59342646599 0.889450132847 -20.0328102112 -3.64043736458 0.794728934765 -20.0328102112 -3.64092493057 0.794926464558 -20.0328102112 -3.64650774002 0.781306743622 -20.0328102112 -3.6460750103 0.781130671501 -20.0328102112 -3.68059206009 0.678261339664 -20.0328102112 -3.682751894 0.678951263428 -20.0328102112 -3.73416423798 0.516830086708 -20.0328102112 -3.73292160034 0.51643550396 -20.0328102112 -3.76753401756 0.399298846722 -20.0328102112 -3.77121210098 0.400107622147 -20.0328102112 -3.79974389076 0.270564287901 -20.0328102112 -3.79567694664 0.269547224045 -20.0328102112 -3.81422877312 0.126991540194 -20.0328102112 -3.81349563599 0.12688754499 -20.0328102112 -3.83190512657 -0.0453566610813 -20.0328102112 -3.83330249786 -0.0452570170164 -20.0328102112 -3.84749031067 -0.21776470542 -20.0328102112 -3.8503537178 -0.217861756682 -20.0328102112 -3.85042929649 -0.384299606085 -20.0328102112 -3.8470749855 -0.384587824345 -20.0328102112 -3.83127570152 -0.554576575756 -20.0328102112 -3.82932853699 -0.554399371147 -20.0328102112 -3.81152582169 -0.696555316448 -20.0328102112 -3.81139993668 -0.696539580822 -20.0328102112 -3.79560303688 -0.820927262306 -20.0328102112 -3.79830574989 -0.821420490742 -20.0328102112 -3.77553367615 -0.946220993996 -20.0328102112 -3.77503490448 -0.946516811848 -20.0328102112 -3.72851777077 -1.10631263256 -20.0328102112 -3.72442626953 -1.1051286459 -20.0328102112 -3.67908072472 -1.23604977131 -20.0328102112 -3.68027806282 -1.23650693893 -20.0328102112 -3.63619375229 -1.35255813599 -20.0328102112 -3.63359236717 -1.35155630112 -20.0328102112 -3.58381509781 -1.47525990009 -20.0328102112 -3.58924341202 -1.47797846794 -20.0328102112 -3.54735207558 -1.56014549732 -20.0328102112 -3.54426980019 -1.55857169628 -20.0328102112 -3.47451210022 -1.66779911518 -20.0328102112 -3.47810673714 -1.67032849789 -20.0328102112 -3.35693144798 -1.84232521057 -20.0328102112 -3.3541674614 -1.840539217 -20.0328102112 -3.27584838867 -1.93787455559 -20.0328102112 -3.27602338791 -1.93804836273 -20.0328102112 -3.26345777512 -1.95176196098 -20.0328102112 -3.26312255859 -1.95145559311 -20.0328102112 -3.17503595352 -2.03887248039 -20.0328102112 -3.17944121361 -2.04389762878 -20.0328102112 -3.01095676422 -2.19192552567 -20.0328102112 -3.00761890411 -2.18831753731 -20.0328102112 -2.90813779831 -2.26330828667 -20.0328102112 -2.90809726715 -2.26326131821 -20.0328102112 -2.88507199287 -2.27969455719 -20.0328102112 -2.88496923447 -2.27954983711 -20.0328102112 -2.7854180336 -2.34922266006 -20.0328102112 -2.78723049164 -2.3519616127 -20.0328102112 -2.61278676987 -2.46705245972 -20.0328102112 -2.61174869537 -2.46556258202 -20.0328102112 -2.50767850876 -2.5283639431 -20.0328102112 -2.50779438019 -2.5285885334 -20.0328102112 -2.47010946274 -2.54955458641 -20.0328102112 -2.4697406292 -2.54888510704 -20.0328102112 -2.32687425613 -2.62550425529 -20.0328102112 -2.33016610146 -2.63211536407 -20.0328102112 -2.07642650604 -2.75662255287 -20.0328102112 -2.07378911972 -2.75130295753 -20.0328102112 -1.92489206791 -2.81566214561 -20.0328102112 -1.92511963844 -2.8162651062 -20.0328102112 -1.88645040989 -2.83104753494 -20.0328102112 -1.88617920876 -2.83036255836 -20.0328102112 -1.73314809799 -2.88070940971 -20.0328102112 -1.73422992229 -2.88478851318 -20.0328102112 -1.49827468395 -2.95042300224 -20.0328102112 -1.49721729755 -2.94665765762 -20.0328102112 -1.34090209007 -2.98334383965 -20.0328102112 -1.34112668037 -2.9844379425 -20.0328102112 -1.22792780399 -3.00798821449 -20.0328102112 -1.22779047489 -3.00750851631 -20.0328102112 -1.07853627205 -3.03261256218 -20.0328102112 -1.07880854607 -3.03699851036 -20.0328102112 -0.930906832218 -3.04984736443 -20.0328102112 -0.930561721325 -3.04563856125 -20.0328102112 -0.773343741894 -3.05000114441 -20.0328102112 -0.77338385582 -3.0527317524 -20.0328102112 -0.549659907818 -3.05525565147 -20.0328102112 -0.549502849579 -3.05578660965 -20.0328102112 -0.401111036539 -3.05022621155 -20.0328102112 -0.400908231735 -3.05126023293 -20.0328102112 -0.361589044333 -3.04580187798 -20.0328102112 -0.361776530743 -3.04447007179 -20.0328102112 -0.210917398334 -3.01232719421 -20.0328102112 -0.210724413395 -3.01322484016 -20.0328102112 0.00235700770281 -2.96723031998 -20.0328102112 0.00258476613089 -2.96815252304 -20.0328102112 0.0998675450683 -2.94499993324 -20.0328102112 0.100433729589 -2.94644761086 -20.0328102112 0.147696301341 -2.92918848991 -20.0328102112 0.146549075842 -2.92607927322 -20.0328102112 0.238890498877 -2.88185405731 -20.0328102112 0.239293187857 -2.88268566132 -20.0328102112 0.33868086338 -2.83435988426 -20.0328102112 0.338355749846 -2.83369159698 -20.0328102112 0.431524902582 -2.78832745552 -20.0328102112 0.432034999132 -2.78937458992 -20.0328102112 0.578032553196 -2.71822142601 -20.0328102112 0.579192638397 -2.72028207779 -20.0328102112 0.673124492168 -2.66944217682 -20.0328102112 0.673127710819 -2.66941547394 -20.0328102112 0.755504369736 -2.61777877808 -20.0328102112 0.75693821907 -2.61930441856 -20.0328102112 0.874337077141 -2.53111433983 -20.0328102112 0.872148990631 -2.52823972702 -20.0328102112 0.946247279644 -2.46718287468 -20.0328102112 0.946357309818 -2.46731281281 -20.0328102112 1.03212547302 -2.39852762222 -20.0328102112 1.03238010406 -2.39884567261 -20.0328102112 1.10898900032 -2.33779740334 -20.0328102112 1.10922145844 -2.33807873726 -20.0328102112 1.14063620567 -2.31218934059 -20.0328102112 1.14067530632 -2.31222248077 -20.0328102112 1.21441435814 -2.24714255333 -20.0328102112 1.21937906742 -2.25136780739 -20.0328102112 1.32893562317 -2.12961316109 -20.0328102112 1.32310700417 -2.12437963486 -20.0328102112 1.39662706852 -2.02511000633 -20.0328102112 1.39952600002 -2.02693080902 -20.0328102112 1.47084033489 -1.91312861443 -20.0328102112 1.46379637718 -1.90879797935 -20.0328102112 1.52281272411 -1.798822999 -20.0328102112 1.52496922016 -1.79986679554 -20.0328102112 1.54551267624 -1.75655853748 -20.0328102112 1.54462945461 -1.75614345074 -20.0328102112 1.58729362488 -1.65115451813 -20.0328102112 1.59240019321 -1.65297591686 -20.0328102112 1.65653800964 -1.47114574909 -20.0328102112 1.65369725227 -1.47001218796 -20.0328102112 1.6833242178 -1.36268019676 -20.0328102112 1.68351280689 -1.36270678043 -20.0328102112 1.69855678082 -1.29556143284 -20.0328102112 1.69720089436 -1.29525125027 -20.0328102112 1.72631943226 -1.14117121696 -20.0328102112 1.73174440861 -1.14184510708 -20.0328102112 1.74974584579 -1.00145995617 -20.0328102112 1.74802935123 -1.0012178421 -20.0328102112 1.75765371323 -0.847935080528 -20.0328102112 1.76252353191 -0.847885251045 -20.0328102112 1.76310372353 -0.637121677399 -20.0328102112 1.75784373283 -0.637127935886 -20.0328102112 1.75143814087 -0.489551514387 -20.0328102112 1.75536048412 -0.489142119884 -20.0328102112 1.73711371422 -0.321080625057 -20.0328102112 1.72848987579 -0.322055697441 -20.0328102112 1.70416867733 -0.182482257485 -20.0328102112 1.70534980297 -0.182235717773 -20.0328102112 1.69785118103 -0.147022619843 -20.0328102112 1.6975029707 -0.147091582417 -20.0328102112 1.64256083965 0.0582360550761 -20.0328102112 1.64947783947 0.0606047958136 -20.0328102112 1.58385550976 0.261264741421 -20.0328102112 1.58381533623 0.261288464069 -20.0328102112 1.49917900562 0.459369599819 -20.0328102112 1.49555265903 0.45817476511 -20.0328102112 1.39641356468 0.653330743313 -20.0328102112 1.39647591114 0.653385937214 -20.0328102112 1.29475879669 0.829327821732 -20.0328102112 1.29338109493 0.828794300556 -20.0328102112 1.17937994003 1.00392341614 -20.0328102112 1.17951047421 1.00404751301 -20.0328102112 1.06136596203 1.1654419899 -20.0328102112 1.05953109264 1.16434252262 # objtype='BezierSpline'; ncoords=111; nparts=111; closed=True; sep=' '; name='splines-005' -17.3313655853 -2.44237709045 -2.61700797081 -17.3313655853 -2.25303602219 -2.71907567978 -17.3313655853 -2.15283846855 -2.77098488808 -17.3313655853 -1.97016835213 -2.85291004181 -17.3313655853 -1.83468985558 -2.91302084923 -17.3313655853 -1.77708280087 -2.93845367432 -17.3313655853 -1.55873525143 -3.00060391426 -17.3313655853 -1.37241542339 -3.04255199432 -17.3313655853 -1.27653360367 -3.05956912041 -17.3313655853 -1.04328739643 -3.08403539658 -17.3313655853 -0.992449641228 -3.08898377419 -17.3313655853 -0.963979065418 -3.08919835091 -17.3313655853 -0.667271792889 -3.09117817879 -17.3313655853 -0.440071552992 -3.08323550224 -17.3313655853 -0.357688039541 -3.0769469738 -17.3313655853 -0.049414537847 -3.03492093086 -17.3313655853 -0.0479461885989 -3.03466820717 -17.3313655853 -0.0426041036844 -3.03328633308 -17.3313655853 0.249555200338 -2.96087002754 -17.3313655853 0.337286144495 -2.93115973473 -17.3313655853 0.53419560194 -2.85548830032 -17.3313655853 0.684191286564 -2.78669047356 -17.3313655853 0.831660866737 -2.72114157677 -17.3313655853 0.998364388943 -2.62391710281 -17.3313655853 1.09083938599 -2.56394696236 -17.3313655853 1.28409564495 -2.40954732895 -17.3313655853 1.32955920696 -2.36977434158 -17.3313655853 1.54126966 -2.13830447197 -17.3313655853 1.54786527157 -2.1308259964 -17.3313655853 1.56654393673 -2.1041328907 -17.3313655853 1.71878278255 -1.88399744034 -17.3313655853 1.76005578041 -1.80516374111 -17.3313655853 1.860424757 -1.61038470268 -17.3313655853 1.91729915142 -1.4593398571 -17.3313655853 1.96880292892 -1.30890417099 -17.3313655853 2.01305150986 -1.0951987505 -17.3313655853 2.02879691124 -0.99217158556 -17.3313655853 2.04367613792 -0.708914339542 -17.3313655853 2.04449057579 -0.670162796974 -17.3313655853 2.03336930275 -0.46107852459 -17.3313655853 2.02737903595 -0.345733463764 -17.3313655853 2.02499365807 -0.325575143099 -17.3313655853 1.97400510311 0.00630079582334 -17.3313655853 1.95911335945 0.0625773221254 -17.3313655853 1.87387526035 0.344820410013 -17.3313655853 1.84052205086 0.430315434933 -17.3313655853 1.73602557182 0.685391962528 -17.3313655853 1.69629430771 0.769557654858 -17.3313655853 1.57365620136 1.01514184475 -17.3313655853 1.5248670578 1.09768497944 -17.3313655853 1.37339377403 1.33608996868 -17.3313655853 1.335916996 1.3867071867 -17.3313655853 1.14430260658 1.64110469818 -17.3313655853 1.12857973576 1.66027104855 -17.3313655853 0.924745857716 1.90110564232 -17.3313655853 0.913925051689 1.91321837902 -17.3313655853 0.689652085304 2.1464881897 -17.3313655853 0.682106971741 2.1536231041 -17.3313655853 0.442243218422 2.36120247841 -17.3313655853 0.438418328762 2.36394500732 -17.3313655853 0.179670512676 2.55387854576 -17.3313655853 0.177289247513 2.55515098572 -17.3313655853 -0.0847692042589 2.68241214752 -17.3313655853 -0.124419428408 2.70124435425 -17.3313655853 -0.358303725719 2.79919266701 -17.3313655853 -0.449374705553 2.81900548935 -17.3313655853 -0.657999813557 2.88140058517 -17.3313655853 -0.889796793461 2.9220802784 -17.3313655853 -0.952743768692 2.9240591526 -17.3313655853 -1.28403878212 2.92568349838 -17.3313655853 -1.29821205139 2.92502713203 -17.3313655853 -1.38508570194 2.91434073448 -17.3313655853 -1.62744688988 2.88351464272 -17.3313655853 -1.66486084461 2.87220954895 -17.3313655853 -1.91203546524 2.78119134903 -17.3313655853 -2.00842571259 2.74398708344 -17.3313655853 -2.24201631546 2.61897325516 -17.3313655853 -2.36926198006 2.54216814041 -17.3313655853 -2.55556941032 2.40509915352 -17.3313655853 -2.67737865448 2.29928684235 -17.3313655853 -2.82594370842 2.14954018593 -17.3313655853 -2.94403553009 2.02152848244 -17.3313655853 -3.06278586388 1.88245689869 -17.3313655853 -3.18320345879 1.72708821297 -17.3313655853 -3.283867836 1.58419024944 -17.3313655853 -3.40363168716 1.39729678631 -17.3313655853 -3.47777557373 1.25466120243 -17.3313655853 -3.57700228691 1.05097055435 -17.3313655853 -3.62722039223 0.909013688564 -17.3313655853 -3.69282317162 0.704815208912 -17.3313655853 -3.72762060165 0.564752161503 -17.3313655853 -3.76332855225 0.357399344444 -17.3313655853 -3.78139829636 0.197066128254 -17.3313655853 -3.80089259148 0.004681694787 -17.3313655853 -3.79594254494 -0.205952912569 -17.3313655853 -3.78719043732 -0.335892885923 -17.3313655853 -3.75781536102 -0.610872685909 -17.3313655853 -3.74899530411 -0.671048879623 -17.3313655853 -3.67755746841 -0.954301893711 -17.3313655853 -3.66946053505 -0.986388981342 -17.3313655853 -3.66709733009 -0.993835330009 -17.3313655853 -3.55007481575 -1.33125901222 -17.3313655853 -3.51456952095 -1.41122329235 -17.3313655853 -3.39595174789 -1.66340124607 -17.3313655853 -3.29494524002 -1.82501828671 -17.3313655853 -3.19695901871 -1.95638251305 -17.3313655853 -2.99714660645 -2.18747925758 -17.3313655853 -2.96118855476 -2.22355365753 -17.3313655853 -2.86234021187 -2.30690574646 -17.3313655853 -2.71253061295 -2.43668341637 -17.3313655853 -2.64634418488 -2.4840130806 -17.3313655853 -2.34999656677 -2.67189621925 -17.3313655853 -2.34811902046 -2.66882228851 -17.3313655853 -2.20315361023 -2.74543976784 -17.3313655853 -2.20362901688 -2.74646306038 -17.3313655853 -2.06273055077 -2.81448936462 -17.3313655853 -2.06158494949 -2.81213021278 -17.3313655853 -1.90248942375 -2.88310074806 -17.3313655853 -1.90245676041 -2.88302779198 -17.3313655853 -1.80589807034 -2.92576384544 -17.3313655853 -1.80662596226 -2.92778563499 -17.3313655853 -1.67057561874 -2.97691345215 -17.3313655853 -1.6686912775 -2.97260046005 -17.3313655853 -1.4662334919 -3.02416205406 -17.3313655853 -1.46600663662 -3.02372145653 -17.3313655853 -1.32469439507 -3.05215358734 -17.3313655853 -1.32471632957 -3.05277514458 -17.3313655853 -1.16049277782 -3.07593154907 -17.3313655853 -1.15995466709 -3.0722386837 -17.3313655853 -1.01787817478 -3.08660459518 -17.3313655853 -1.01792824268 -3.08765053749 -17.3313655853 -0.978247642517 -3.0897269249 -17.3313655853 -0.978214383125 -3.08909726143 -17.3313655853 -0.815625905991 -3.09025239944 -17.3313655853 -0.815581798553 -3.09327483177 -17.3313655853 -0.553638219833 -3.0895717144 -17.3313655853 -0.5535415411 -3.08954691887 -17.3313655853 -0.398832499981 -3.08094167709 -17.3313655853 -0.398750394583 -3.08130931854 -17.3313655853 -0.20306403935 -3.06052041054 -17.3313655853 -0.203137069941 -3.0586206913 -17.3313655853 -0.0486783795059 -3.03480744362 -17.3313655853 -0.0486738905311 -3.03482460976 -17.3313655853 -0.0452511757612 -3.03408885002 -17.3313655853 -0.0452785864472 -3.03396368027 -17.3313655853 0.103287875652 -2.99633717537 -17.3313655853 0.105241425335 -3.00311112404 -17.3313655853 0.293964087963 -2.94787144661 -17.3313655853 0.293738275766 -2.94689393044 -17.3313655853 0.436464190483 -2.89532589912 -17.3313655853 0.437032669783 -2.89639258385 -17.3313655853 0.6102039814 -2.8234899044 -17.3313655853 0.608993530273 -2.82064676285 -17.3313655853 0.757730603218 -2.75348305702 -17.3313655853 0.759942293167 -2.75785470009 -17.3313655853 0.917423665524 -2.67723917961 -17.3313655853 0.916209340096 -2.67447400093 -17.3313655853 1.04528534412 -2.59504270554 -17.3313655853 1.04619300365 -2.59613871574 -17.3313655853 1.19103837013 -2.49169969559 -17.3313655853 1.18923830986 -2.48886489868 -17.3313655853 1.30725979805 -2.39017796516 -17.3313655853 1.30800127983 -2.39086079597 -17.3313655853 1.44151008129 -2.26027131081 -17.3313655853 1.43646991253 -2.25498747826 -17.3313655853 1.54460096359 -2.13459539413 -17.3313655853 1.5447871685 -2.13473796844 -17.3313655853 1.55792236328 -2.11804413795 -17.3313655853 1.55724143982 -2.11750507355 -17.3313655853 1.64296638966 -1.99427592754 -17.3313655853 1.64968752861 -1.99831080437 -17.3313655853 1.74175453186 -1.84599208832 -17.3313655853 1.73954749107 -1.84464716911 -17.3313655853 1.81055605412 -1.70793819427 -17.3313655853 1.8160289526 -1.71034514904 -17.3313655853 1.89312577248 -1.53675603867 -17.3313655853 1.89001107216 -1.53527522087 -17.3313655853 1.94418311119 -1.38452887535 -17.3313655853 1.94786715508 -1.3854393959 -17.3313655853 1.99753725529 -1.20385956764 -17.3313655853 1.99374687672 -1.20255851746 -17.3313655853 2.02227067947 -1.0439273119 -17.3313655853 2.02349376678 -1.04394829273 -17.3313655853 2.04322957993 -0.851259231567 -17.3313655853 2.03846621513 -0.850624918938 -17.3313655853 2.0443880558 -0.689549744129 -17.3313655853 2.04480171204 -0.68952703476 -17.3313655853 2.04281020164 -0.565558314323 -17.3313655853 2.03886437416 -0.565624117851 -17.3313655853 2.03033804893 -0.403407901525 -17.3313655853 2.03226971626 -0.403244793415 -17.3313655853 2.02651953697 -0.335625976324 -17.3313655853 2.02636051178 -0.335630625486 -17.3313655853 2.00238227844 -0.159245252609 -17.3313655853 2.0082256794 -0.157817617059 -17.3313655853 1.96807217598 0.0347545221448 -17.3313655853 1.96704375744 0.0345762856305 -17.3313655853 1.91894829273 0.204393878579 -17.3313655853 1.92197346687 0.205591693521 -17.3313655853 1.85890412331 0.388157099485 -17.3313655853 1.85755765438 0.387711465359 -17.3313655853 1.78935217857 0.558284878731 -17.3313655853 1.79156756401 0.559304654598 -17.3313655853 1.71727204323 0.727964699268 -17.3313655853 1.71662247181 0.727699458599 -17.3313655853 1.63633942604 0.893012225628 -17.3313655853 1.63923490047 0.894668459892 -17.3313655853 1.55074954033 1.05722332001 -17.3313655853 1.54991936684 1.05681657791 -17.3313655853 1.4510679245 1.21807515621 -17.3313655853 1.45328080654 1.21973705292 -17.3313655853 1.35558080673 1.3620339632 -17.3313655853 1.3547590971 1.36147606373 -17.3313655853 1.24063456059 1.51429784298 -17.3313655853 1.24270522594 1.51594650745 -17.3313655853 1.13664317131 1.65084671974 -17.3313655853 1.13651430607 1.65074884892 -17.3313655853 1.02759361267 1.78146398067 -17.3313655853 1.02825462818 1.7820725441 -17.3313655853 0.919417381287 1.90723323822 -17.3313655853 0.9194445014 1.90726304054 -17.3313655853 0.803960859776 2.03186655045 -17.3313655853 0.804499745369 2.03258705139 -17.3313655853 0.685966551304 2.15014338493 -17.3313655853 0.685956299305 2.15014052391 -17.3313655853 0.564520835876 2.26000547409 -17.3313655853 0.566657304764 2.26309704781 -17.3313655853 0.440397262573 2.36265802383 -17.3313655853 0.44032305479 2.36256313324 -17.3313655853 0.308518767357 2.45818734169 -17.3313655853 0.315130829811 2.46857714653 -17.3313655853 0.178531080484 2.55459618568 -17.3313655853 0.178491741419 2.55453801155 -17.3313655853 0.0475391149521 2.62129092216 -17.3313655853 0.0465332083404 2.61935043335 -17.3313655853 -0.104553155601 2.69191384315 -17.3313655853 -0.104384846985 2.69229722023 -17.3313655853 -0.240151569247 2.75292801857 -17.3313655853 -0.237890034914 2.76122975349 -17.3313655853 -0.402563214302 2.81314635277 -17.3313655853 -0.404283642769 2.8073759079 -17.3313655853 -0.554725646973 2.84617686272 -17.3313655853 -0.552224040031 2.85639166832 -17.3313655853 -0.772316932678 2.90842890739 -17.3313655853 -0.773041725159 2.91006159782 -17.3313655853 -0.921041071415 2.92529654503 -17.3313655853 -0.921262681484 2.92348718643 -17.3313655853 -1.11835134029 2.92706775665 -17.3313655853 -1.11847889423 2.92910885811 -17.3313655853 -1.29112911224 2.92553687096 -17.3313655853 -1.29114818573 2.92562437057 -17.3313655853 -1.3417891264 2.92134332657 -17.3313655853 -1.34166002274 2.91977334023 -17.3313655853 -1.50629746914 2.89917707443 -17.3313655853 -1.50838899612 2.90888786316 -17.3313655853 -1.64649343491 2.87945556641 -17.3313655853 -1.6463381052 2.8784122467 -17.3313655853 -1.78968977928 2.83040809631 -17.3313655853 -1.78880906105 2.82765746117 -17.3313655853 -1.96037209034 2.76296448708 -17.3313655853 -1.96155428886 2.76547646523 -17.3313655853 -2.12861514091 2.68888354301 -17.3313655853 -2.12691283226 2.68445420265 -17.3313655853 -2.30658817291 2.582239151 -17.3313655853 -2.30752086639 2.58338904381 -17.3313655853 -2.46534395218 2.47801971436 -17.3313655853 -2.46533870697 2.47728729248 -17.3313655853 -2.6185131073 2.35474181175 -17.3313655853 -2.61851668358 2.35437560081 -17.3313655853 -2.75433158875 2.22726678848 -17.3313655853 -2.75304532051 2.22573757172 -17.3313655853 -2.88613247871 2.08662772179 -17.3313655853 -2.88623905182 2.08664321899 -17.3313655853 -3.00472259521 1.9531570673 -17.3313655853 -3.00509166718 1.95336043835 -17.3313655853 -3.12480139732 1.80624270439 -17.3313655853 -3.12479782104 1.80610537529 -17.3313655853 -3.23513913155 1.65682435036 -17.3313655853 -3.23512458801 1.65670728683 -17.3313655853 -3.34576749802 1.49209988117 -17.3313655853 -3.34809589386 1.49325871468 -17.3313655853 -3.44385099411 1.32780051231 -17.3313655853 -3.44163918495 1.32644987106 -17.3313655853 -3.52870750427 1.15347957611 -17.3313655853 -3.53330492973 1.15529382229 -17.3313655853 -3.60604286194 0.981638908386 -17.3313655853 -3.60315155983 0.980343103409 -17.3313655853 -3.66150331497 0.807414352894 -17.3313655853 -3.66349411011 0.807902395725 -17.3313655853 -3.71255850792 0.635448455811 -17.3313655853 -3.71279811859 0.635324776173 -17.3313655853 -3.74923038483 0.461864590645 -17.3313655853 -3.74851059914 0.461507886648 -17.3313655853 -3.77469158173 0.277564108372 -17.3313655853 -3.77281427383 0.277280986309 -17.3313655853 -3.79168581963 0.100931733847 -17.3313655853 -3.79715466499 0.101106859744 -17.3313655853 -3.80496525764 -0.100381791592 -17.3313655853 -3.80071973801 -0.100740149617 -17.3313655853 -3.79298949242 -0.270987510681 -17.3313655853 -3.79283690453 -0.271033495665 -17.3313655853 -3.77520060539 -0.473617643118 -17.3313655853 -3.7751853466 -0.47372251749 -17.3313655853 -3.75399518013 -0.641035497189 -17.3313655853 -3.75491857529 -0.641261696815 -17.3313655853 -3.72054481506 -0.814120769501 -17.3313655853 -3.71328544617 -0.812677681446 -17.3313655853 -3.67351007462 -0.970345675945 -17.3313655853 -3.67398738861 -0.970481574535 -17.3313655853 -3.66839194298 -0.990144312382 -17.3313655853 -3.6683280468 -0.990128457546 -17.3313655853 -3.61083340645 -1.16329336166 -17.3313655853 -3.61556315422 -1.16530036926 -17.3313655853 -3.53403139114 -1.37191569805 -17.3313655853 -3.53275585175 -1.37143945694 -17.3313655853 -3.45664191246 -1.53794372082 -17.3313655853 -3.46253037453 -1.54127550125 -17.3313655853 -3.35042023659 -1.7469201088 -17.3313655853 -3.34868454933 -1.74642241001 -17.3313655853 -3.2487347126 -1.89260303974 -17.3313655853 -3.24825263023 -1.89254891872 -17.3313655853 -3.10134100914 -2.07537674904 -17.3313655853 -3.10101771355 -2.07561254501 -17.3313655853 -2.97982859612 -2.20613026619 -17.3313655853 -2.97991275787 -2.20632648468 -17.3313655853 -2.91365623474 -2.26728582382 -17.3313655853 -2.91148471832 -2.26490235329 -17.3313655853 -2.78700661659 -2.37129282951 -17.3313655853 -2.79028892517 -2.37541627884 -17.3313655853 -2.68060898781 -2.46183514595 -17.3313655853 -2.67993044853 -2.46107006073 -17.3313655853 -2.54583621025 -2.55267071724 -17.3313655853 -2.54695296288 -2.55487370491 # objtype='BezierSpline'; ncoords=100; nparts=100; closed=True; sep=' '; name='splines-006' -14.6299200058 -2.96062350273 -1.87611961365 -14.6299200058 -2.79195594788 -2.03702998161 -14.6299200058 -2.66132044792 -2.15776324272 -14.6299200058 -2.38194608688 -2.35358929634 -14.6299200058 -2.33460617065 -2.38492560387 -14.6299200058 -2.1845741272 -2.46854424477 -14.6299200058 -1.92242467403 -2.61360454559 -14.6299200058 -1.80687975883 -2.66691446304 -14.6299200058 -1.50441539288 -2.79260373116 -14.6299200058 -1.17741858959 -2.86072492599 -14.6299200058 -1.0750490427 -2.87986278534 -14.6299200058 -0.897134065628 -2.88593554497 -14.6299200058 -0.669375777245 -2.89518904686 -14.6299200058 -0.587745249271 -2.89321613312 -14.6299200058 -0.221490576863 -2.85320711136 -14.6299200058 -0.189353615046 -2.8483402729 -14.6299200058 -0.0771466046572 -2.82137107849 -14.6299200058 0.228506177664 -2.74956297874 -14.6299200058 0.278952240944 -2.73025417328 -14.6299200058 0.584738135338 -2.58612537384 -14.6299200058 0.684668540955 -2.52807879448 -14.6299200058 0.917113482952 -2.38749051094 -14.6299200058 1.04963684082 -2.28481960297 -14.6299200058 1.1933375597 -2.16346406937 -14.6299200058 1.3377314806 -2.00426769257 -14.6299200058 1.43339598179 -1.89738953114 -14.6299200058 1.57631695271 -1.67657148838 -14.6299200058 1.63400864601 -1.58111059666 -14.6299200058 1.77107465267 -1.29390835762 -14.6299200058 1.799441576 -1.22650122643 -14.6299200058 1.90740704536 -0.913079440594 -14.6299200058 1.916705966 -0.884994864464 -14.6299200058 1.99224126339 -0.524861872196 -14.6299200058 1.99669313431 -0.499591588974 -14.6299200058 2.0135037899 -0.122729837894 -14.6299200058 2.01327061653 -0.0907991081476 -14.6299200058 1.96364867687 0.313934147358 -14.6299200058 1.95441782475 0.358652710915 -14.6299200058 1.86255908012 0.671287119389 -14.6299200058 1.83887648582 0.741075158119 -14.6299200058 1.72744452953 1.02952873707 -14.6299200058 1.67446625233 1.15826046467 -14.6299200058 1.56864154339 1.37558269501 -14.6299200058 1.47753572464 1.54769957066 -14.6299200058 1.38496601582 1.70453429222 -14.6299200058 1.24894499779 1.91284036636 -14.6299200058 1.18079721928 2.01108336449 -14.6299200058 0.979265749454 2.27351880074 -14.6299200058 0.978778719902 2.27411317825 -14.6299200058 0.977470695972 2.27552771568 -14.6299200058 0.746238529682 2.53104734421 -14.6299200058 0.651267230511 2.62589049339 -14.6299200058 0.504254877567 2.76109695435 -14.6299200058 0.307818591595 2.91516447067 -14.6299200058 0.243708923459 2.96198534966 -14.6299200058 0.116499297321 3.04478645325 -14.6299200058 -0.00295303016901 3.12385940552 -14.6299200058 -0.0706130638719 3.15716814995 -14.6299200058 -0.266840398312 3.25080442429 -14.6299200058 -0.439948678017 3.31202745438 -14.6299200058 -0.527748584747 3.33674478531 -14.6299200058 -0.761648237705 3.39122390747 -14.6299200058 -0.798816144466 3.39960455894 -14.6299200058 -0.812101960182 3.40209054947 -14.6299200058 -1.0973033905 3.42698764801 -14.6299200058 -1.16202640533 3.43133234978 -14.6299200058 -1.39171123505 3.4317677021 -14.6299200058 -1.4991350174 3.41221189499 -14.6299200058 -1.69709777832 3.37224888802 -14.6299200058 -1.86483037472 3.32888960838 -14.6299200058 -2.00992965698 3.26959276199 -14.6299200058 -2.18685889244 3.17939352989 -14.6299200058 -2.34874677658 3.09044694901 -14.6299200058 -2.49397015572 3.00110650063 -14.6299200058 -2.66959786415 2.86172032356 -14.6299200058 -2.76825237274 2.77523398399 -14.6299200058 -2.97862291336 2.56379127502 -14.6299200058 -3.04111647606 2.49514603615 -14.6299200058 -3.26728701591 2.23122644424 -14.6299200058 -3.29545259476 2.19233250618 -14.6299200058 -3.36624097824 2.07806491852 -14.6299200058 -3.50323581696 1.85776901245 -14.6299200058 -3.54413366318 1.7828387022 -14.6299200058 -3.66503405571 1.50616681576 -14.6299200058 -3.72825503349 1.33880603313 -14.6299200058 -3.78454375267 1.10174226761 -14.6299200058 -3.83263206482 0.883715927601 -14.6299200058 -3.85127282143 0.667529642582 -14.6299200058 -3.8637945652 0.399470597506 -14.6299200058 -3.85120916367 0.182818964124 -14.6299200058 -3.82956790924 -0.0195005722344 -14.6299200058 -3.76683545113 -0.352814018726 -14.6299200058 -3.75036478043 -0.422137349844 -14.6299200058 -3.63221406937 -0.745910227299 -14.6299200058 -3.61780381203 -0.785746693611 -14.6299200058 -3.61465024948 -0.793664216995 -14.6299200058 -3.45930767059 -1.14507830143 -14.6299200058 -3.41018009186 -1.23160862923 -14.6299200058 -3.23187708855 -1.53437101841 -14.6299200058 -3.1340367794 -1.66173183918 -14.6299200058 -2.88180613518 -1.96165764332 -14.6299200058 -2.87692165375 -1.95724773407 -14.6299200058 -2.72712039948 -2.09791016579 -14.6299200058 -2.73039484024 -2.10205459595 -14.6299200058 -2.52883839607 -2.26461005211 -14.6299200058 -2.52291226387 -2.25755357742 -14.6299200058 -2.35848903656 -2.36956977844 -14.6299200058 -2.3588385582 -2.37018203735 -14.6299200058 -2.26129198074 -2.42953205109 -14.6299200058 -2.25965380669 -2.42684936523 -14.6299200058 -2.05361032486 -2.54127407074 -14.6299200058 -2.05597400665 -2.54596018791 -14.6299200058 -1.86570322514 -2.64233469963 -14.6299200058 -1.86514306068 -2.64137935638 -14.6299200058 -1.6569108963 -2.73264145851 -14.6299200058 -1.66019546986 -2.74448132515 -14.6299200058 -1.34555482864 -2.84167766571 -14.6299200058 -1.34124994278 -2.82834935188 -14.6299200058 -1.12633764744 -2.87081933022 -14.6299200058 -1.12666201591 -2.87419009209 -14.6299200058 -0.986823558807 -2.88955926895 -14.6299200058 -0.986080765724 -2.88261079788 -14.6299200058 -0.783241093159 -2.89019298553 -14.6299200058 -0.783285260201 -2.89425253868 -14.6299200058 -0.628571391106 -2.8955245018 -14.6299200058 -0.628445744514 -2.89592623711 -14.6299200058 -0.404100120068 -2.88098835945 -14.6299200058 -0.404124200344 -2.87700104713 -14.6299200058 -0.205378547311 -2.85110807419 -14.6299200058 -0.205288693309 -2.8514559269 -14.6299200058 -0.132776468992 -2.83727812767 -14.6299200058 -0.133284315467 -2.83471179008 -14.6299200058 0.0755867138505 -2.7850754261 -14.6299200058 0.0787858441472 -2.79557418823 -14.6299200058 0.254263579845 -2.74164748192 -14.6299200058 0.254125773907 -2.7408387661 -14.6299200058 0.434327125549 -2.66401124001 -14.6299200058 0.435213208199 -2.6646065712 -14.6299200058 0.635854721069 -2.5592956543 -14.6299200058 0.634964466095 -2.55754256248 -14.6299200058 0.801504790783 -2.4588201046 -14.6299200058 0.805315673351 -2.46423053741 -14.6299200058 0.986105680466 -2.34013295174 -14.6299200058 0.984486103058 -2.33752822876 -14.6299200058 1.12273359299 -2.22568249702 -14.6299200058 1.12582147121 -2.22863221169 -14.6299200058 1.2704873085 -2.08899712563 -14.6299200058 1.26579749584 -2.08410286903 -14.6299200058 1.38573920727 -1.95098674297 -14.6299200058 1.38999533653 -1.95421338081 -14.6299200058 1.51298308372 -1.79318737984 -14.6299200058 1.50657439232 -1.7880551815 -14.6299200058 1.60589134693 -1.62929677963 -14.6299200058 1.60757541656 -1.63014173508 -14.6299200058 1.70942509174 -1.44122040272 -14.6299200058 1.70594894886 -1.43903839588 -14.6299200058 1.78604114056 -1.26055622101 -14.6299200058 1.78639519215 -1.26063930988 -14.6299200058 1.85857832432 -1.0717600584 -14.6299200058 1.85436654091 -1.07010853291 -14.6299200058 1.91214060783 -0.899065554142 -14.6299200058 1.91286301613 -0.899254500866 -14.6299200058 1.96450507641 -0.707631826401 -14.6299200058 1.95739686489 -0.705492258072 -14.6299200058 1.99467110634 -0.512266039848 -14.6299200058 1.9952943325 -0.512317657471 -14.6299200058 2.01725816727 -0.312497287989 -14.6299200058 2.00998973846 -0.311251878738 -14.6299200058 2.01380133629 -0.106772191823 -14.6299200058 2.01430034637 -0.106705352664 -14.6299200058 2.0001206398 0.112322442234 -14.6299200058 1.99666249752 0.112914681435 -14.6299200058 1.95995175838 0.336444288492 -14.6299200058 1.95994353294 0.336520701647 -14.6299200058 1.91498470306 0.516591846943 -14.6299200058 1.91170179844 0.515986740589 -14.6299200058 1.85144460201 0.706411123276 -14.6299200058 1.85143637657 0.706441760063 -14.6299200058 1.78617572784 0.886395394802 -14.6299200058 1.78472352028 0.885925352573 -14.6299200058 1.70165896416 1.09417521954 -14.6299200058 1.70294702053 1.09478831291 -14.6299200058 1.62501227856 1.26847338676 -14.6299200058 1.62336838245 1.26784324646 -14.6299200058 1.52455043793 1.4623837471 -14.6299200058 1.52505910397 1.46274340153 -14.6299200058 1.43309354782 1.62714767456 -14.6299200058 1.43300116062 1.62720417976 -14.6299200058 1.31934654713 1.81017255783 -14.6299200058 1.31839966774 1.80965936184 -14.6299200058 1.21556520462 1.96242904663 -14.6299200058 1.2160397768 1.96281516552 -14.6299200058 1.08326566219 2.14466261864 -14.6299200058 1.08207786083 2.14392447472 -14.6299200058 0.979026973248 2.27381968498 -14.6299200058 0.979030907154 2.27382349968 -14.6299200058 0.97814643383 2.27483940125 -14.6299200058 0.978120863438 2.2748169899 -14.6299200058 0.861171066761 2.40266227722 -14.6299200058 0.865007340908 2.40628886223 -14.6299200058 0.699980795383 2.5796380043 -14.6299200058 0.69970780611 2.57946538925 -14.6299200058 0.579182088375 2.69497656822 -14.6299200058 0.580298185349 2.69647955894 -14.6299200058 0.409207940102 2.84186267853 -14.6299200058 0.407329201698 2.83983802795 -14.6299200058 0.276174753904 2.93911790848 -14.6299200058 0.276369780302 2.93945336342 -14.6299200058 0.181262806058 3.00506544113 -14.6299200058 0.179943159223 3.00314068794 -14.6299200058 0.056621234864 3.08409142494 -14.6299200058 0.0590407773852 3.08827328682 -14.6299200058 -0.0355892591178 3.14259338379 -14.6299200058 -0.0366824120283 3.14072132111 -14.6299200058 -0.168436601758 3.20458483696 -14.6299200058 -0.166538253427 3.20927143097 -14.6299200058 -0.351546347141 3.28587937355 -14.6299200058 -0.352485239506 3.28428244591 -14.6299200058 -0.483396917582 3.3258099556 -14.6299200058 -0.483589887619 3.32539272308 -14.6299200058 -0.644017100334 3.36663460732 -14.6299200058 -0.644603669643 3.36439776421 -14.6299200058 -0.780217170715 3.39547991753 -14.6299200058 -0.780161380768 3.39575743675 -14.6299200058 -0.805433928967 3.40096926689 -14.6299200058 -0.805414199829 3.40117526054 -14.6299200058 -0.953752875328 3.4214785099 -14.6299200058 -0.954592227936 3.41596984863 -14.6299200058 -1.12963986397 3.42948412895 -14.6299200058 -1.12962853909 3.43021535873 -14.6299200058 -1.27673995495 3.43528699875 -14.6299200058 -1.27779722214 3.44194293022 -14.6299200058 -1.44586443901 3.42693042755 -14.6299200058 -1.44552147388 3.4225025177 -14.6299200058 -1.59829843044 3.39317846298 -14.6299200058 -1.59872484207 3.39487576485 -14.6299200058 -1.78148603439 3.35283851624 -14.6299200058 -1.78280436993 3.35611391068 -14.6299200058 -1.9390450716 3.30425786972 -14.6299200058 -1.93874287605 3.30221509933 -14.6299200058 -2.10012102127 3.22826123238 -14.6299200058 -2.09911322594 3.22585105896 -14.6299200058 -2.26847147942 3.13618326187 -14.6299200058 -2.26894330978 3.13688015938 -14.6299200058 -2.42241120338 3.0475859642 -14.6299200058 -2.42427587509 3.04994034767 -14.6299200058 -2.58562040329 2.93688869476 -14.6299200058 -2.58354043961 2.93351864815 -14.6299200058 -2.71995282173 2.81970882416 -14.6299200058 -2.72045516968 2.82010698318 -14.6299200058 -2.87691617012 2.67321801186 -14.6299200058 -2.87583208084 2.6717915535 -14.6299200058 -3.01061487198 2.53017783165 -14.6299200058 -3.01039147377 2.52992963791 -14.6299200058 -3.15615534782 2.36491179466 -14.6299200058 -3.15977954865 2.3675839901 -14.6299200058 -3.28214049339 2.21238708496 -14.6299200058 -3.28208875656 2.21226167679 -14.6299200058 -3.33285951614 2.13654828072 -14.6299200058 -3.33079791069 2.13516831398 -14.6299200058 -3.43464398384 1.96785843372 -14.6299200058 -3.43791556358 1.96976995468 -14.6299200058 -3.52473020554 1.82091367245 -14.6299200058 -3.5253636837 1.82112693787 -14.6299200058 -3.61052250862 1.64741396904 -14.6299200058 -3.60813450813 1.64594817162 -14.6299200058 -3.69874835014 1.42334282398 -14.6299200058 -3.70211720467 1.42416226864 -14.6299200058 -3.7638528347 1.22255659103 -14.6299200058 -3.75735163689 1.22049224377 -14.6299200058 -3.80946040154 0.992928922176 -14.6299200058 -3.81581497192 0.993832826614 -14.6299200058 -3.84897637367 0.776695489883 -14.6299200058 -3.84408140182 0.775764226913 -14.6299200058 -3.86016654968 0.533675074577 -14.6299200058 -3.86455464363 0.533460259438 -14.6299200058 -3.8631799221 0.291112571955 -14.6299200058 -3.86012601852 0.2909283638 -14.6299200058 -3.84284877777 0.0814562737942 -14.6299200058 -3.84438681602 0.081070035696 -14.6299200058 -3.80486655235 -0.187139347196 -14.6299200058 -3.80211877823 -0.186990752816 -14.6299200058 -3.7594230175 -0.38765078783 -14.6299200058 -3.76058888435 -0.388072609901 -14.6299200058 -3.70090961456 -0.586911201477 -14.6299200058 -3.69106173515 -0.583941042423 -14.6299200058 -3.62498092651 -0.765818297863 -14.6299200058 -3.62532520294 -0.765948653221 -14.6299200058 -3.61629056931 -0.789729654789 -14.6299200058 -3.61630010605 -0.789736151695 -14.6299200058 -3.54027152061 -0.970754146576 -14.6299200058 -3.54556775093 -0.973694145679 -14.6299200058 -3.43696808815 -1.18946301937 -14.6299200058 -3.43508553505 -1.18854105473 -14.6299200058 -3.322234869 -1.38368749619 -14.6299200058 -3.32996606827 -1.38902115822 -14.6299200058 -3.18704199791 -1.60080826283 -14.6299200058 -3.18374752998 -1.59867465496 -14.6299200058 -3.04868745804 -1.7699958086 -14.6299200058 -3.05385518074 -1.77493822575 # objtype='BezierSpline'; ncoords=113; nparts=113; closed=True; sep=' '; name='splines-007' -11.9284744263 -1.09963107109 -2.35921406746 -11.9284744263 -1.13069331646 -2.35784077644 -11.9284744263 -1.14859688282 -2.35550451279 -11.9284744263 -1.50543141365 -2.30936360359 -11.9284744263 -1.85492801666 -2.19732022285 -11.9284744263 -1.8689186573 -2.19227004051 -11.9284744263 -1.88316881657 -2.18658423424 -11.9284744263 -2.24408960342 -2.05406570435 -11.9284744263 -2.58996343613 -1.86962819099 -11.9284744263 -2.5926296711 -1.86828804016 -11.9284744263 -2.59438967705 -1.86713540554 -11.9284744263 -2.92820501328 -1.61678028107 -11.9284744263 -3.03057193756 -1.52172577381 -11.9284744263 -3.22670340538 -1.30982625484 -11.9284744263 -3.41585683823 -1.0656028986 -11.9284744263 -3.50258374214 -0.939934074879 -11.9284744263 -3.68777084351 -0.629057407379 -11.9284744263 -3.74470186234 -0.526763081551 -11.9284744263 -3.8377020359 -0.311633169651 -11.9284744263 -3.90832328796 -0.157808572054 -11.9284744263 -3.94544291496 -0.0523852407932 -11.9284744263 -4.03276348114 0.234902948141 -11.9284744263 -4.11100196838 0.63945555687 -11.9284744263 -4.1163854599 0.681802392006 -11.9284744263 -4.11454582214 0.759468138218 -11.9284744263 -4.11051607132 1.11334705353 -11.9284744263 -4.10207653046 1.21354806423 -11.9284744263 -4.05690288544 1.52176642418 -11.9284744263 -4.05122470856 1.56433081627 -11.9284744263 -4.04522418976 1.58586084843 -11.9284744263 -3.94969892502 1.95009374619 -11.9284744263 -3.84690022469 2.18099141121 -11.9284744263 -3.78733587265 2.31883311272 -11.9284744263 -3.67650914192 2.49511432648 -11.9284744263 -3.57168984413 2.65963673592 -11.9284744263 -3.49358320236 2.76130080223 -11.9284744263 -3.40432357788 2.87708711624 -11.9284744263 -3.32545995712 2.96434664726 -11.9284744263 -3.22721338272 3.07949209213 -11.9284744263 -3.07812786102 3.2154006958 -11.9284744263 -3.02670550346 3.26327991486 -11.9284744263 -2.97838234901 3.29581427574 -11.9284744263 -2.80238580704 3.42782187462 -11.9284744263 -2.55418157578 3.57789039612 -11.9284744263 -2.54600429535 3.58294582367 -11.9284744263 -2.53049516678 3.58885121346 -11.9284744263 -2.29180979729 3.67265939713 -11.9284744263 -2.15263032913 3.71289205551 -11.9284744263 -2.01763796806 3.75308203697 -11.9284744263 -1.74277377129 3.77559161186 -11.9284744263 -1.73785507679 3.77626037598 -11.9284744263 -1.73504304886 3.77643561363 -11.9284744263 -1.52998960018 3.78806686401 -11.9284744263 -1.49823462963 3.79009485245 -11.9284744263 -1.32422482967 3.78381371498 -11.9284744263 -1.2607998848 3.7761952877 -11.9284744263 -0.949061274529 3.7302942276 -11.9284744263 -0.946042001247 3.72976660728 -11.9284744263 -0.938660621643 3.72776269913 -11.9284744263 -0.637913346291 3.646920681 -11.9284744263 -0.51498991251 3.60902905464 -11.9284744263 -0.389230340719 3.57130980492 -11.9284744263 -0.257931530476 3.51590538025 -11.9284744263 -0.190619304776 3.4860291481 -11.9284744263 -0.0816688835621 3.42761492729 -11.9284744263 0.0949519574642 3.33002066612 -11.9284744263 0.217872440815 3.2408349514 -11.9284744263 0.355895102024 3.14369535446 -11.9284744263 0.542800545692 2.99444842339 -11.9284744263 0.600798726082 2.94791555405 -11.9284744263 0.638426721096 2.90950512886 -11.9284744263 0.815048277378 2.7197368145 -11.9284744263 0.960665643215 2.54352283478 -11.9284744263 1.03064787388 2.45635533333 -11.9284744263 1.18586969376 2.2325360775 -11.9284744263 1.22556817532 2.17386102676 -11.9284744263 1.24033534527 2.14897894859 -11.9284744263 1.40766191483 1.85788917542 -11.9284744263 1.51840102673 1.61975097656 -11.9284744263 1.56190621853 1.52891695499 -11.9284744263 1.66991364956 1.21717119217 -11.9284744263 1.67003810406 1.21682298183 -11.9284744263 1.67039775848 1.21529471874 -11.9284744263 1.74206840992 0.903720498085 -11.9284744263 1.74989080429 0.822305560112 -11.9284744263 1.78515231609 0.554889798164 -11.9284744263 1.78066122532 0.394644200802 -11.9284744263 1.76880145073 0.144353896379 -11.9284744263 1.74327790737 -0.0511062629521 -11.9284744263 1.7198138237 -0.183158785105 -11.9284744263 1.65028595924 -0.44066542387 -11.9284744263 1.63351166248 -0.502584517002 -11.9284744263 1.54715692997 -0.718526899815 -11.9284744263 1.51916444302 -0.790288150311 -11.9284744263 1.50758099556 -0.812030255795 -11.9284744263 1.37356114388 -1.06525230408 -11.9284744263 1.22609674931 -1.27743911743 -11.9284744263 1.17915272713 -1.34603452682 -11.9284744263 1.1530174017 -1.37774395943 -11.9284744263 0.979663848877 -1.57161307335 -11.9284744263 0.863229334354 -1.69445586205 -11.9284744263 0.776397705078 -1.7806429863 -11.9284744263 0.559054493904 -1.96053516865 -11.9284744263 0.55396771431 -1.96450340748 -11.9284744263 0.541017353535 -1.97230815887 -11.9284744263 0.308532476425 -2.1192843914 -11.9284744263 0.197796866298 -2.17415904999 -11.9284744263 -0.0913571044803 -2.28706431389 -11.9284744263 -0.144931614399 -2.30688285828 -11.9284744263 -0.249301955104 -2.32407784462 -11.9284744263 -0.480849862099 -2.36925005913 -11.9284744263 -0.611849725246 -2.37876987457 -11.9284744263 -0.809694826603 -2.39109039307 -11.9284744263 -1.11512327194 -2.35802125931 -11.9284744263 -1.11521995068 -2.35918998718 -11.9284744263 -1.13967859745 -2.35705733299 -11.9284744263 -1.13964438438 -2.35666751862 -11.9284744263 -1.32700061798 -2.33232998848 -11.9284744263 -1.33056557178 -2.34835934639 -11.9284744263 -1.68380224705 -2.26958632469 -11.9284744263 -1.68125009537 -2.25648403168 -11.9284744263 -1.86196672916 -2.1949224472 -11.9284744263 -1.861967206 -2.19491052628 -11.9284744263 -1.87608897686 -2.18954634666 -11.9284744263 -1.87600564957 -2.18932771683 -11.9284744263 -2.06267499924 -2.11783361435 -11.9284744263 -2.06904459 -2.13242292404 -11.9284744263 -2.42254757881 -1.97418069839 -11.9284744263 -2.41593885422 -1.9597465992 -11.9284744263 -2.59128832817 -1.86894214153 -11.9284744263 -2.59133911133 -1.86903178692 -11.9284744263 -2.59353971481 -1.8677636385 -11.9284744263 -2.59352898598 -1.86773908138 -11.9284744263 -2.76511073112 -1.7473949194 -11.9284744263 -2.76830863953 -1.75035107136 -11.9284744263 -2.98173570633 -1.57206296921 -11.9284744263 -2.98125767708 -1.57111918926 -11.9284744263 -3.13250112534 -1.41963326931 -11.9284744263 -3.13347005844 -1.41987013817 -11.9284744263 -3.32645010948 -1.19209468365 -11.9284744263 -3.32470393181 -1.19021904469 -11.9284744263 -3.46091270447 -1.0040063858 -11.9284744263 -3.46136641502 -1.00414609909 -11.9284744263 -3.60026335716 -0.78776037693 -11.9284744263 -3.59748148918 -0.785822868347 -11.9284744263 -3.71698188782 -0.578339636326 -11.9284744263 -3.71885561943 -0.579201221466 -11.9284744263 -3.79644560814 -0.421782702208 -11.9284744263 -3.79000520706 -0.418664813042 -11.9284744263 -3.87214827538 -0.234335705638 -11.9284744263 -3.8766143322 -0.236178174615 -11.9284744263 -3.929261446 -0.106059193611 -11.9284744263 -3.9280371666 -0.105475284159 -11.9284744263 -3.99220371246 0.0902423411608 -11.9284744263 -3.99668002129 0.0893801376224 -11.9284744263 -4.082280159 0.434601128101 -11.9284744263 -4.07845115662 0.436127722263 -11.9284744263 -4.11437416077 0.660520017147 -11.9284744263 -4.1152920723 0.660546779633 -11.9284744263 -4.11837482452 0.720485627651 -11.9284744263 -4.11522674561 0.720631062984 -11.9284744263 -4.11144351959 0.936388552189 -11.9284744263 -4.11894893646 0.936713993549 -11.9284744263 -4.1081199646 1.16353464127 -11.9284744263 -4.10783195496 1.16362464428 -11.9284744263 -4.08424711227 1.36820566654 -11.9284744263 -4.07849407196 1.36751782894 -11.9284744263 -4.0539264679 1.54302942753 -11.9284744263 -4.05552625656 1.54334843159 -11.9284744263 -4.0489859581 1.57525193691 -11.9284744263 -4.04814195633 1.57507348061 -11.9284744263 -3.9960694313 1.76760077477 -11.9284744263 -4.01186847687 1.7730358839 -11.9284744263 -3.90796971321 2.06893801689 -11.9284744263 -3.89766430855 2.06526398659 -11.9284744263 -3.81674051285 2.24974679947 -11.9284744263 -3.82220745087 2.25259160995 -11.9284744263 -3.73898005486 2.41068911552 -11.9284744263 -3.7321870327 2.40714097023 -11.9284744263 -3.62434720993 2.57753229141 -11.9284744263 -3.62760663033 2.57983279228 -11.9284744263 -3.53494143486 2.71208381653 -11.9284744263 -3.53267836571 2.71050095558 -11.9284744263 -3.44900107384 2.81923055649 -11.9284744263 -3.45114541054 2.8210246563 -11.9284744263 -3.36665511131 2.92218971252 -11.9284744263 -3.36426138878 2.92016339302 -11.9284744263 -3.27552556992 3.02120709419 -11.9284744263 -3.27973985672 3.02521252632 -11.9284744263 -3.15720629692 3.15183568001 -11.9284744263 -3.15231060982 3.14705562592 -11.9284744263 -3.05229115486 3.23920416832 -11.9284744263 -3.05413174629 3.24150013924 -11.9284744263 -3.00396609306 3.28133773804 -11.9284744263 -3.00211381912 3.27894210815 -11.9284744263 -2.88875961304 3.35953307152 -11.9284744263 -2.89345121384 3.36636281013 -11.9284744263 -2.68232750893 3.50884795189 -11.9284744263 -2.6779088974 3.50224328041 -11.9284744263 -2.5500805378 3.58039784431 -11.9284744263 -2.55029463768 3.58082675934 -11.9284744263 -2.53859806061 3.58660387993 -11.9284744263 -2.53828692436 3.5860004425 -11.9284744263 -2.4117205143 3.63230800629 -11.9284744263 -2.4122364521 3.63414478302 -11.9284744263 -2.22284078598 3.69471693039 -11.9284744263 -2.22213888168 3.69249892235 -11.9284744263 -2.08505511284 3.73271799088 -11.9284744263 -2.08648061752 3.74016046524 -11.9284744263 -1.88284218311 3.77838277817 -11.9284744263 -1.87980735302 3.7606754303 -11.9284744263 -1.74030721188 3.77586007118 -11.9284744263 -1.74032330513 3.77601599693 -11.9284744263 -1.73645412922 3.77639913559 -11.9284744263 -1.73644924164 3.77635192871 -11.9284744263 -1.63253331184 3.78253698349 -11.9284744263 -1.63249433041 3.78188657761 -11.9284744263 -1.51410865784 3.78902435303 -11.9284744263 -1.51412308216 3.7898747921 -11.9284744263 -1.41128993034 3.79129886627 -11.9284744263 -1.41094732285 3.7905755043 -11.9284744263 -1.29240882397 3.78133296967 -11.9284744263 -1.29245603085 3.78042626381 -11.9284744263 -1.10465252399 3.75532531738 -11.9284744263 -1.10459494591 3.75533008575 -11.9284744263 -0.947548389435 3.73005080223 -11.9284744263 -0.947536289692 3.7300992012 -11.9284744263 -0.942313075066 3.72893643379 -11.9284744263 -0.94235253334 3.72876000404 -11.9284744263 -0.788337647915 3.68715405464 -11.9284744263 -0.787501335144 3.69006562233 -11.9284744263 -0.576127111912 3.6291000843 -11.9284744263 -0.576522886753 3.62774038315 -11.9284744263 -0.452182859182 3.58993029594 -11.9284744263 -0.450911670923 3.59350061417 -11.9284744263 -0.322280108929 3.54722332954 -11.9284744263 -0.323320239782 3.54420995712 -11.9284744263 -0.224140688777 3.50127840042 -11.9284744263 -0.223673447967 3.50219798088 -11.9284744263 -0.135133609176 3.45888781548 -11.9284744263 -0.135957062244 3.45716571808 -11.9284744263 0.00694683846086 3.37937855721 -11.9284744263 0.00996454525739 3.38404536247 -11.9284744263 0.158913075924 3.28936195374 -11.9284744263 0.156094223261 3.28498291969 -11.9284744263 0.286530405283 3.19177055359 -11.9284744263 0.288417011499 3.19430923462 -11.9284744263 0.451520621777 3.07196855545 -11.9284744263 0.449434369802 3.06918001175 -11.9284744263 0.571826517582 2.97121548653 -11.9284744263 0.573290407658 2.9728281498 -11.9284744263 0.620690703392 2.92990064621 -11.9284744263 0.619861364365 2.92894768715 -11.9284744263 0.727936327457 2.81576561928 -11.9284744263 0.729607760906 2.81713867188 -11.9284744263 0.890387892723 2.63384985924 -11.9284744263 0.888483405113 2.63214015961 -11.9284744263 0.995963096619 2.5001885891 -11.9284744263 0.997226417065 2.501111269 -11.9284744263 1.11208355427 2.34730172157 -11.9284744263 1.10890614986 2.34488916397 -11.9284744263 1.20588731766 2.20331382751 -11.9284744263 1.20660448074 2.20376014709 -11.9284744263 1.23331344128 2.16164946556 -11.9284744263 1.23303866386 2.16147065163 -11.9284744263 1.32500684261 2.0040230751 -11.9284744263 1.33043670654 2.00677347183 -11.9284744263 1.4680672884 1.74143218994 -11.9284744263 1.46235489845 1.73850083351 -11.9284744263 1.53989422321 1.57421147823 -11.9284744263 1.54278719425 1.57541680336 -11.9284744263 1.62453699112 1.3765912056 -11.9284744263 1.61515176296 1.37277722359 -11.9284744263 1.66997504234 1.21699678898 -11.9284744263 1.66998577118 1.21700000763 -11.9284744263 1.67026007175 1.21607136726 -11.9284744263 1.67021989822 1.21605932713 -11.9284744263 1.70662510395 1.05959880352 -11.9284744263 1.71650648117 1.06117546558 -11.9284744263 1.74860775471 0.863439679146 -11.9284744263 1.745262146 0.862931311131 -11.9284744263 1.76515543461 0.68832808733 -11.9284744263 1.77822613716 0.689149916172 -11.9284744263 1.78926873207 0.47509521246 -11.9284744263 1.78368091583 0.474737823009 -11.9284744263 1.77594137192 0.269453436136 -11.9284744263 1.77987754345 0.269041895866 -11.9284744263 1.76008808613 0.0462641939521 -11.9284744263 1.75828015804 0.0462786741555 -11.9284744263 1.73307025433 -0.117367364466 -11.9284744263 1.73442018032 -0.117774620652 -11.9284744263 1.69076597691 -0.313189059496 -11.9284744263 1.68510401249 -0.311926752329 -11.9284744263 1.64191186428 -0.47162848711 -11.9284744263 1.64366018772 -0.472213536501 -11.9284744263 1.59671986103 -0.612689435482 -11.9284744263 1.58987498283 -0.61037427187 -11.9284744263 1.53300857544 -0.754347443581 -11.9284744263 1.53521716595 -0.755352437496 -11.9284744263 1.51403045654 -0.801461398602 -11.9284744263 1.51335787773 -0.801151275635 -11.9284744263 1.44039797783 -0.938549399376 -11.9284744263 1.4479418993 -0.943130612373 -11.9284744263 1.30647671223 -1.17539465427 -11.9284744263 1.29944646358 -1.1710819006 -11.9284744263 1.20250165462 -1.31165194511 -11.9284744263 1.2041053772 -1.31285023689 -11.9284744263 1.16681706905 -1.36243963242 -11.9284744263 1.16639876366 -1.36215865612 -11.9284744263 1.06832635403 -1.47638344765 -11.9284744263 1.0677293539 -1.47595703602 -11.9284744263 0.922350347042 -1.63386654854 -11.9284744263 0.922369599342 -1.63393640518 -11.9284744263 0.820480704308 -1.73820137978 -11.9284744263 0.821667671204 -1.73959422112 -11.9284744263 0.672001838684 -1.87530457973 -11.9284744263 0.669003069401 -1.8721780777 -11.9284744263 0.556540310383 -1.9625556469 -11.9284744263 0.556620836258 -1.96267879009 -11.9284744263 0.547749638557 -1.96877968311 -11.9284744263 0.547450065613 -1.96833705902 -11.9284744263 0.424002677202 -2.04454541206 -11.9284744263 0.428265601397 -2.05200886726 -11.9284744263 0.254733115435 -2.14951324463 -11.9284744263 0.254261165857 -2.14920258522 -11.9284744263 0.0559739917517 -2.23684287071 -11.9284744263 0.0537148118019 -2.23191356659 -11.9284744263 -0.118053279817 -2.29721307755 -11.9284744263 -0.117447257042 -2.29960680008 -11.9284744263 -0.19582593441 -2.32035636902 -11.9284744263 -0.197254344821 -2.31471562386 -11.9284744263 -0.365382730961 -2.34495830536 -11.9284744263 -0.364139735699 -2.35368227959 -11.9284744263 -0.545828580856 -2.37791752815 -11.9284744263 -0.546326935291 -2.37434911728 -11.9284744263 -0.710737764835 -2.38544178009 -11.9284744263 -0.710973262787 -2.39342617989 -11.9284744263 -0.954958736897 -2.38765358925 -11.9284744263 -0.954297363758 -2.37040400505 # objtype='BezierSpline'; ncoords=114; nparts=114; closed=True; sep=' '; name='splines-008' -9.22702980042 -1.69750332832 -1.83346188068 -9.22702980042 -1.84811890125 -1.82210171223 -9.22702980042 -2.12709546089 -1.78592431545 -9.22702980042 -2.31181311607 -1.75045371056 -9.22702980042 -2.5112850666 -1.69879496098 -9.22702980042 -2.79979681969 -1.5665448904 -9.22702980042 -2.88504290581 -1.52495312691 -9.22702980042 -3.08564710617 -1.38488090038 -9.22702980042 -3.20839715004 -1.30057775974 -9.22702980042 -3.25756287575 -1.26125741005 -9.22702980042 -3.4822268486 -1.05998659134 -9.22702980042 -3.70551300049 -0.820919036865 -9.22702980042 -3.73076462746 -0.788753151894 -9.22702980042 -3.76000928879 -0.741583585739 -9.22702980042 -3.95081496239 -0.454512327909 -9.22702980042 -4.0792427063 -0.204312160611 -9.22702980042 -4.12691450119 -0.101923488081 -9.22702980042 -4.22841024399 0.149419292808 -9.22702980042 -4.25737857819 0.218726724386 -9.22702980042 -4.26753854752 0.244733169675 -9.22702980042 -4.37245941162 0.539743900299 -9.22702980042 -4.42134809494 0.683890759945 -9.22702980042 -4.47455263138 0.869915485382 -9.22702980042 -4.54268455505 1.13829755783 -9.22702980042 -4.55626678467 1.20772492886 -9.22702980042 -4.58789634705 1.43396389484 -9.22702980042 -4.59143161774 1.46362757683 -9.22702980042 -4.60778141022 1.64280319214 -9.22702980042 -4.60425281525 1.83818888664 -9.22702980042 -4.60430002213 1.85230302811 -9.22702980042 -4.60315513611 1.86907613277 -9.22702980042 -4.58246994019 2.14226102829 -9.22702980042 -4.54822635651 2.31941318512 -9.22702980042 -4.51552391052 2.42958664894 -9.22702980042 -4.45709753036 2.56249809265 -9.22702980042 -4.41420459747 2.64488935471 -9.22702980042 -4.36503505707 2.72094988823 -9.22702980042 -4.28835773468 2.84001898766 -9.22702980042 -4.26545286179 2.87489271164 -9.22702980042 -4.18031406403 2.99135518074 -9.22702980042 -4.12751102448 3.06712770462 -9.22702980042 -4.10345077515 3.09623575211 -9.22702980042 -3.96565270424 3.22961187363 -9.22702980042 -3.85365986824 3.34396696091 -9.22702980042 -3.80545592308 3.390001297 -9.22702980042 -3.70710968971 3.45094847679 -9.22702980042 -3.64108324051 3.49252295494 -9.22702980042 -3.58105158806 3.52779984474 -9.22702980042 -3.40151357651 3.61919617653 -9.22702980042 -3.13814783096 3.69316101074 -9.22702980042 -3.13379049301 3.69441366196 -9.22702980042 -3.13001608849 3.69516396523 -9.22702980042 -2.87378406525 3.72350668907 -9.22702980042 -2.63016986847 3.74376630783 -9.22702980042 -2.61258292198 3.7444934845 -9.22702980042 -2.35152602196 3.74845647812 -9.22702980042 -2.32001543045 3.74840521812 -9.22702980042 -1.98680269718 3.74300861359 -9.22702980042 -1.96042203903 3.74261975288 -9.22702980042 -1.95797657967 3.74256634712 -9.22702980042 -1.60450613499 3.71986961365 -9.22702980042 -1.57295036316 3.71708321571 -9.22702980042 -1.24912631512 3.67243933678 -9.22702980042 -1.19345164299 3.66210103035 -9.22702980042 -0.838194966316 3.57031369209 -9.22702980042 -0.811802923679 3.56350946426 -9.22702980042 -0.807691454887 3.56201052666 -9.22702980042 -0.793586134911 3.55539488792 -9.22702980042 -0.354463040829 3.35345172882 -9.22702980042 -0.246514737606 3.29928827286 -9.22702980042 0.0225161928684 3.10580420494 -9.22702980042 0.199436426163 2.96796417236 -9.22702980042 0.365999400616 2.7938349247 -9.22702980042 0.573518395424 2.55187106133 -9.22702980042 0.675829648972 2.40322136879 -9.22702980042 0.878947377205 2.07629036903 -9.22702980042 0.937197685242 1.96599459648 -9.22702980042 1.03036344051 1.77804541588 -9.22702980042 1.14973211288 1.478084445 -9.22702980042 1.15370094776 1.46801590919 -9.22702980042 1.15568935871 1.46055531502 -9.22702980042 1.24050831795 1.132802248 -9.22702980042 1.28733575344 0.876419484615 -9.22702980042 1.30074691772 0.797670662403 -9.22702980042 1.30229640007 0.64330548048 -9.22702980042 1.30182695389 0.48090800643 -9.22702980042 1.29663360119 0.370179176331 -9.22702980042 1.27780914307 0.170928761363 -9.22702980042 1.21055519581 -0.0469229109585 -9.22702980042 1.19222712517 -0.111689664423 -9.22702980042 1.17420685291 -0.170234814286 -9.22702980042 1.10536313057 -0.385295212269 -9.22702980042 1.01212203503 -0.560899198055 -9.22702980042 0.951749503613 -0.669077038765 -9.22702980042 0.817547500134 -0.855712532997 -9.22702980042 0.759334683418 -0.934848725796 -9.22702980042 0.633301615715 -1.06055438519 -9.22702980042 0.547884941101 -1.14769530296 -9.22702980042 0.494064152241 -1.18857657909 -9.22702980042 0.308364212513 -1.32306396961 -9.22702980042 0.100727319717 -1.46198928356 -9.22702980042 0.082670994103 -1.47379493713 -9.22702980042 0.0614737384021 -1.48544216156 -9.22702980042 -0.154603242874 -1.590944767 -9.22702980042 -0.33976534009 -1.66850018501 -9.22702980042 -0.41298994422 -1.69733643532 -9.22702980042 -0.632481276989 -1.74721240997 -9.22702980042 -0.74859726429 -1.77475857735 -9.22702980042 -0.776281237602 -1.780184865 -9.22702980042 -0.968408286572 -1.80928146839 -9.22702980042 -1.08557677269 -1.82787156105 -9.22702980042 -1.10260272026 -1.82946503162 -9.22702980042 -1.39371657372 -1.83840382099 -9.22702980042 -1.46097493172 -1.84138727188 -9.22702980042 -1.77289688587 -1.82935726643 -9.22702980042 -1.77301776409 -1.82979786396 -9.22702980042 -1.98799192905 -1.80776786804 -9.22702980042 -1.9882850647 -1.80823123455 -9.22702980042 -2.2199075222 -1.77100944519 -9.22702980042 -2.22011232376 -1.77111041546 -9.22702980042 -2.41227006912 -1.72782468796 -9.22702980042 -2.41458916664 -1.73317492008 -9.22702980042 -2.66022372246 -1.645840168 -9.22702980042 -2.65635919571 -1.63439953327 -9.22702980042 -2.84266448021 -1.54626595974 -9.22702980042 -2.84428906441 -1.54892671108 -9.22702980042 -2.99016666412 -1.46311366558 -9.22702980042 -2.98507523537 -1.45452737808 -9.22702980042 -3.14685797691 -1.34249222279 -9.22702980042 -3.14863610268 -1.34490513802 -9.22702980042 -3.23366236687 -1.28183746338 -9.22702980042 -3.23354887962 -1.28158950806 -9.22702980042 -3.37262034416 -1.16384112835 -9.22702980042 -3.37458872795 -1.16541433334 -9.22702980042 -3.59896039963 -0.945650100708 -9.22702980042 -3.59919214249 -0.945012748241 -9.22702980042 -3.71880412102 -0.805406153202 -9.22702980042 -3.71906471252 -0.805483579636 -9.22702980042 -3.74664354324 -0.766047120094 -9.22702980042 -3.74501776695 -0.764931261539 -9.22702980042 -3.85311889648 -0.596575498581 -9.22702980042 -3.86376166344 -0.602944791317 -9.22702980042 -4.02184104919 -0.333407521248 -9.22702980042 -4.01745891571 -0.33060118556 -9.22702980042 -4.10405445099 -0.153595298529 -9.22702980042 -4.10442399979 -0.153702229261 -9.22702980042 -4.18089151382 0.0223453231156 -9.22702980042 -4.17690324783 0.0240597836673 -9.22702980042 -4.24268388748 0.184159427881 -9.22702980042 -4.24330282211 0.183907881379 -9.22702980042 -4.26261043549 0.231668576598 -9.22702980042 -4.26265954971 0.231654956937 -9.22702980042 -4.32225322723 0.391397565603 -9.22702980042 -4.32108688354 0.391860514879 -9.22702980042 -4.39743280411 0.611633598804 -9.22702980042 -4.3986620903 0.611268043518 -9.22702980042 -4.45018529892 0.776204943657 -9.22702980042 -4.44934940338 0.776525557041 -9.22702980042 -4.51062107086 1.0035661459 -9.22702980042 -4.51236104965 1.00326609612 -9.22702980042 -4.55043172836 1.17279648781 -9.22702980042 -4.55042266846 1.17285251617 -9.22702980042 -4.57513904572 1.32033205032 -9.22702980042 -4.57323074341 1.32069563866 -9.22702980042 -4.5898141861 1.44877624512 -9.22702980042 -4.58986902237 1.44877409935 -9.22702980042 -4.60084199905 1.55308544636 -9.22702980042 -4.60450601578 1.55303657055 -9.22702980042 -4.61133861542 1.74030184746 -9.22702980042 -4.60497140884 1.74048829079 -9.22702980042 -4.60420084 1.84524536133 -9.22702980042 -4.60452842712 1.84525418282 -9.22702980042 -4.60402774811 1.860699296 -9.22702980042 -4.60375881195 1.86069178581 -9.22702980042 -4.59331941605 2.00570511818 -9.22702980042 -4.60064029694 2.0067179203 -9.22702980042 -4.57050323486 2.23152828217 -9.22702980042 -4.56962299347 2.23188209534 -9.22702980042 -4.53459787369 2.37516546249 -9.22702980042 -4.53526163101 2.37574124336 -9.22702980042 -4.49058866501 2.497610569 -9.22702980042 -4.48846483231 2.49707508087 -9.22702980042 -4.43702936172 2.60435461998 -9.22702980042 -4.43753480911 2.60478973389 -9.22702980042 -4.391456604 2.68398833275 -9.22702980042 -4.38958644867 2.68289804459 -9.22702980042 -4.32664442062 2.78045082092 -9.22702980042 -4.32696390152 2.78065824509 -9.22702980042 -4.27698421478 2.85750699043 -9.22702980042 -4.27733469009 2.85775375366 -9.22702980042 -4.22436904907 2.93415379524 -9.22702980042 -4.22221899033 2.93264961243 -9.22702980042 -4.15348720551 3.02893781662 -9.22702980042 -4.15542221069 3.03038811684 -9.22702980042 -4.11609792709 3.08215069771 -9.22702980042 -4.11624956131 3.08239245415 -9.22702980042 -4.03845596313 3.16653347015 -9.22702980042 -4.03364801407 3.16201472282 -9.22702980042 -3.90890192986 3.28603053093 -9.22702980042 -3.91059684753 3.28774189949 -9.22702980042 -3.82994961739 3.36738085747 -9.22702980042 -3.83167099953 3.36971497536 -9.22702980042 -3.75995087624 3.42521500587 -9.22702980042 -3.75617313385 3.42029953003 -9.22702980042 -3.67402243614 3.47161746025 -9.22702980042 -3.67440748215 3.47224664688 -9.22702980042 -3.6113448143 3.51061749458 -9.22702980042 -3.61157250404 3.51108360291 -9.22702980042 -3.49274396896 3.57616615295 -9.22702980042 -3.49488782883 3.58272910118 -9.22702980042 -3.274725914 3.66871285439 -9.22702980042 -3.2697160244 3.65577459335 -9.22702980042 -3.13596725464 3.69378066063 -9.22702980042 -3.13599157333 3.69387960434 -9.22702980042 -3.13192224503 3.69486713409 -9.22702980042 -3.13191580772 3.69487071037 -9.22702980042 -3.00274634361 3.71481537819 -9.22702980042 -3.00206899643 3.71107983589 -9.22702980042 -2.75213718414 3.73529052734 -9.22702980042 -2.75213503838 3.7361767292 -9.22702980042 -2.62138772011 3.74431276321 -9.22702980042 -2.62137961388 3.74424481392 -9.22702980042 -2.48210263252 3.74818086624 -9.22702980042 -2.48206186295 3.74757194519 -9.22702980042 -2.33577156067 3.74856328964 -9.22702980042 -2.33576965332 3.74854564667 -9.22702980042 -2.15339827538 3.74692058563 -9.22702980042 -2.15341091156 3.74558568001 -9.22702980042 -1.97361254692 3.74280452728 -9.22702980042 -1.97361147404 3.74286103249 -9.22702980042 -1.95919919014 3.74259734154 -9.22702980042 -1.95919823647 3.74261879921 -9.22702980042 -1.78108048439 3.7349588871 -9.22702980042 -1.78108012676 3.73333239555 -9.22702980042 -1.58871388435 3.71866559982 -9.22702980042 -1.58868479729 3.71886134148 -9.22702980042 -1.41058933735 3.69873404503 -9.22702980042 -1.41043055058 3.69852018356 -9.22702980042 -1.22118365765 3.6679213047 -9.22702980042 -1.22107684612 3.66822695732 -9.22702980042 -1.01444852352 3.62240695953 -9.22702980042 -1.01583528519 3.61616110802 -9.22702980042 -0.824999809265 3.56690812111 -9.22702980042 -0.824802517891 3.5675444603 -9.22702980042 -0.809715628624 3.56286168098 -9.22702980042 -0.809709846973 3.56284976006 -9.22702980042 -0.800505816936 3.55902242661 -9.22702980042 -0.80065113306 3.55867624283 -9.22702980042 -0.574407219887 3.45359945297 -9.22702980042 -0.572244346142 3.45812726021 -9.22702980042 -0.300044029951 3.32729554176 -9.22702980042 -0.298014432192 3.33045840263 -9.22702980042 -0.105209857225 3.21376371384 -9.22702980042 -0.110093563795 3.20509147644 -9.22702980042 0.112266086042 3.0386068821 -9.22702980042 0.116449296474 3.0429418087 -9.22702980042 0.288598120213 2.88740777969 -9.22702980042 0.285140901804 2.88309454918 -9.22702980042 0.472964197397 2.67575669289 -9.22702980042 0.476456999779 2.67800712585 -9.22702980042 0.628465890884 2.48046398163 -9.22702980042 0.62644392252 2.4787042141 -9.22702980042 0.781163454056 2.24222564697 -9.22702980042 0.783231914043 2.24310851097 -9.22702980042 0.909966230392 2.02222895622 -9.22702980042 0.908785820007 2.0215075016 -9.22702980042 0.984980165958 1.87263393402 -9.22702980042 0.98768132925 1.87375950813 -9.22702980042 1.09605097771 1.63074195385 -9.22702980042 1.09029197693 1.62816166878 -9.22702980042 1.15172469616 1.47305345535 -9.22702980042 1.15201199055 1.47314739227 -9.22702980042 1.15490591526 1.46435499191 -9.22702980042 1.15470862389 1.46428918839 -9.22702980042 1.19869101048 1.29683434963 -9.22702980042 1.2040964365 1.29800069332 -9.22702980042 1.26853907108 1.00562858582 -9.22702980042 1.26469016075 1.00474643707 -9.22702980042 1.29427671432 0.837086617947 -9.22702980042 1.29719364643 0.837327539921 -9.22702980042 1.30761349201 0.721033930779 -9.22702980042 1.30202054977 0.720489859581 -9.22702980042 1.30258655548 0.562108635902 -9.22702980042 1.30384635925 0.562062323093 -9.22702980042 1.30044853687 0.425513267517 -9.22702980042 1.30053853989 0.425451159477 -9.22702980042 1.28958332539 0.270387083292 -9.22702980042 1.29727435112 0.268549442291 -9.22702980042 1.25563442707 0.0597193911672 -9.22702980042 1.24288916588 0.0623854026198 -9.22702980042 1.20100939274 -0.0791933685541 -9.22702980042 1.20175945759 -0.0794150754809 -9.22702980042 1.18355214596 -0.141061246395 -9.22702980042 1.183380723 -0.141013652086 -9.22702980042 1.14038860798 -0.277954518795 -9.22702980042 1.1490483284 -0.281670182943 -9.22702980042 1.06689882278 -0.476535648108 -9.22702980042 1.05965542793 -0.473594218493 -9.22702980042 0.982504546642 -0.615297794342 -9.22702980042 0.984923601151 -0.616887331009 -9.22702980042 0.890192627907 -0.765918850899 -9.22702980042 0.885151386261 -0.762760519981 -9.22702980042 0.788655996323 -0.895436942577 -9.22702980042 0.791277170181 -0.897720575333 -9.22702980042 0.701456904411 -1.00212264061 -9.22702980042 0.695961296558 -0.997347772121 -9.22702980042 0.590348660946 -1.10388231277 -9.22702980042 0.593531489372 -1.10745799541 -9.22702980042 0.52260196209 -1.1699821949 -9.22702980042 0.521204173565 -1.16844546795 -9.22702980042 0.401993066072 -1.25687038898 -9.22702980042 0.402430146933 -1.25756657124 -9.22702980042 0.20587067306 -1.39442944527 -9.22702980042 0.204911395907 -1.39307940006 -9.22702980042 0.0917307287455 -1.46793985367 -9.22702980042 0.0919118374586 -1.46824634075 -9.22702980042 0.0723108127713 -1.48001563549 -9.22702980042 0.072206556797 -1.47987735271 -9.22702980042 -0.0452306121588 -1.54076659679 -9.22702980042 -0.045136783272 -1.54134500027 -9.22702980042 -0.245992138982 -1.63235354424 -9.22702980042 -0.246778219938 -1.63072192669 -9.22702980042 -0.376218467951 -1.6833101511 -9.22702980042 -0.375498384237 -1.68576776981 -9.22702980042 -0.520220816135 -1.73042428493 -9.22702980042 -0.522856354713 -1.72175455093 -9.22702980042 -0.690603256226 -1.76070988178 -9.22702980042 -0.690290689468 -1.76213335991 -9.22702980042 -0.762380480766 -1.77774310112 -9.22702980042 -0.762387096882 -1.77777218819 -9.22702980042 -0.871985316277 -1.79680323601 -9.22702980042 -0.872397184372 -1.79439485073 -9.22702980042 -1.02702450752 -1.81836998463 -9.22702980042 -1.02675521374 -1.82046031952 -9.22702980042 -1.09405553341 -1.82893979549 -9.22702980042 -1.09407317638 -1.82893550396 -9.22702980042 -1.24787712097 -1.83848464489 -9.22702980042 -1.24819684029 -1.83294248581 -9.22702980042 -1.42735433578 -1.83966624737 -9.22702980042 -1.42733860016 -1.84120500088 -9.22702980042 -1.57921421528 -1.84202778339 -9.22702980042 -1.57937347889 -1.83989310265 # objtype='BezierSpline'; ncoords=153; nparts=153; closed=True; sep=' '; name='splines-009' -6.52558422089 -1.61124265194 -1.28515720367 -6.52558422089 -1.64443194866 -1.28554260731 -6.52558422089 -1.85430681705 -1.27417469025 -6.52558422089 -2.01433396339 -1.26416242123 -6.52558422089 -2.03851127625 -1.26112568378 -6.52558422089 -2.07842373848 -1.25095665455 -6.52558422089 -2.21942734718 -1.22095346451 -6.52558422089 -2.3060195446 -1.19528472424 -6.52558422089 -2.40634250641 -1.16422557831 -6.52558422089 -2.57302379608 -1.11977982521 -6.52558422089 -2.59512495995 -1.11421704292 -6.52558422089 -2.62878251076 -1.10067534447 -6.52558422089 -2.76672363281 -1.04511415958 -6.52558422089 -2.81649637222 -1.01838171482 -6.52558422089 -2.93252563477 -0.960389494896 -6.52558422089 -3.03992629051 -0.906608700752 -6.52558422089 -3.09632849693 -0.881985604763 -6.52558422089 -3.24061059952 -0.799864053726 -6.52558422089 -3.2549932003 -0.791949927807 -6.52558422089 -3.26467299461 -0.78656578064 -6.52558422089 -3.47922086716 -0.685143232346 -6.52558422089 -3.66989588737 -0.584726452827 -6.52558422089 -3.69690155983 -0.572585940361 -6.52558422089 -3.73232483864 -0.556392848492 -6.52558422089 -3.8985376358 -0.478216081858 -6.52558422089 -4.01749134064 -0.402929514647 -6.52558422089 -4.08738136292 -0.358732640743 -6.52558422089 -4.16105127335 -0.311809420586 -6.52558422089 -4.27753591537 -0.237196847796 -6.52558422089 -4.40434455872 -0.12864421308 -6.52558422089 -4.45324420929 -0.0920600369573 -6.52558422089 -4.49581861496 -0.059987090528 -6.52558422089 -4.64037132263 0.0507161766291 -6.52558422089 -4.79335784912 0.217732027173 -6.52558422089 -4.79853916168 0.223335385323 -6.52558422089 -4.80155467987 0.226942270994 -6.52558422089 -4.9311747551 0.391678661108 -6.52558422089 -4.99483203888 0.501710772514 -6.52558422089 -5.03876638412 0.576857745647 -6.52558422089 -5.1186170578 0.733002245426 -6.52558422089 -5.13451910019 0.763664662838 -6.52558422089 -5.14141845703 0.783761560917 -6.52558422089 -5.19847869873 0.959533751011 -6.52558422089 -5.23038768768 1.05828726292 -6.52558422089 -5.25756216049 1.14276564121 -6.52558422089 -5.28380918503 1.27392184734 -6.52558422089 -5.29831457138 1.34628903866 -6.52558422089 -5.31637907028 1.49133133888 -6.52558422089 -5.32352304459 1.55620920658 -6.52558422089 -5.34023237228 1.70945942402 -6.52558422089 -5.34626197815 1.76717531681 -6.52558422089 -5.34022760391 1.89550900459 -6.52558422089 -5.33440732956 1.93642878532 -6.52558422089 -5.31407880783 2.06869339943 -6.52558422089 -5.30530500412 2.13373565674 -6.52558422089 -5.28332042694 2.23980998993 -6.52558422089 -5.25959730148 2.33279800415 -6.52558422089 -5.23220825195 2.40688538551 -6.52558422089 -5.18653154373 2.5305891037 -6.52558422089 -5.17394638062 2.56641817093 -6.52558422089 -5.12711763382 2.68245768547 -6.52558422089 -5.12587690353 2.68541884422 -6.52558422089 -5.12547636032 2.68625473976 -6.52558422089 -5.05844926834 2.81127309799 -6.52558422089 -5.03010797501 2.85046052933 -6.52558422089 -4.96956396103 2.93778443336 -6.52558422089 -4.88705778122 3.00406360626 -6.52558422089 -4.85181617737 3.0296459198 -6.52558422089 -4.82389163971 3.05509114265 -6.52558422089 -4.75756502151 3.12153482437 -6.52558422089 -4.68496370316 3.18330764771 -6.52558422089 -4.66123247147 3.20588970184 -6.52558422089 -4.59480667114 3.23797273636 -6.52558422089 -4.55072164536 3.25922584534 -6.52558422089 -4.53300476074 3.26776766777 -6.52558422089 -4.44985532761 3.31052207947 -6.52558422089 -4.37641763687 3.34714818001 -6.52558422089 -4.32618093491 3.37508296967 -6.52558422089 -4.20495128632 3.40862298012 -6.52558422089 -4.19831943512 3.41013169289 -6.52558422089 -4.1817317009 3.41331338882 -6.52558422089 -4.06338071823 3.43470740318 -6.52558422089 -4.01452970505 3.44319581985 -6.52558422089 -3.92968702316 3.45797014236 -6.52558422089 -3.79076385498 3.45424509048 -6.52558422089 -3.75429344177 3.45281505585 -6.52558422089 -3.70657658577 3.451161623 -6.52558422089 -3.57718372345 3.44694375992 -6.52558422089 -3.47705340385 3.44356489182 -6.52558422089 -3.40022683144 3.43726801872 -6.52558422089 -3.29851055145 3.42734909058 -6.52558422089 -3.23737359047 3.42058849335 -6.52558422089 -3.1835668087 3.41525936127 -6.52558422089 -3.01431012154 3.39233374596 -6.52558422089 -2.88607931137 3.37547826767 -6.52558422089 -2.7941505909 3.36335515976 -6.52558422089 -2.67337369919 3.34971427917 -6.52558422089 -2.5752055645 3.33500313759 -6.52558422089 -2.49907302856 3.3241250515 -6.52558422089 -2.3559513092 3.30367517471 -6.52558422089 -2.19212818146 3.28018045425 -6.52558422089 -2.06656241417 3.26026439667 -6.52558422089 -1.81899750233 3.21275234222 -6.52558422089 -1.77910125256 3.205265522 -6.52558422089 -1.75967919827 3.20135903358 -6.52558422089 -1.49553358555 3.14186048508 -6.52558422089 -1.35357677937 3.09914755821 -6.52558422089 -1.21410286427 3.05272841454 -6.52558422089 -0.966110110283 2.96150970459 -6.52558422089 -0.952816307545 2.95598506927 -6.52558422089 -0.924709975719 2.94177436829 -6.52558422089 -0.688629865646 2.82080054283 -6.52558422089 -0.608233690262 2.77402281761 -6.52558422089 -0.427845299244 2.65096926689 -6.52558422089 -0.288245856762 2.54035043716 -6.52558422089 -0.182306647301 2.45579481125 -6.52558422089 0.0399944074452 2.23394751549 -6.52558422089 0.044351503253 2.22945070267 -6.52558422089 0.0535820312798 2.21755886078 -6.52558422089 0.209855049849 2.01486063004 -6.52558422089 0.264016538858 1.92828857899 -6.52558422089 0.347180962563 1.76938056946 -6.52558422089 0.41372898221 1.59710586071 -6.52558422089 0.449160933495 1.49501883984 -6.52558422089 0.525603294373 1.21084725857 -6.52558422089 0.531436443329 1.19025886059 -6.52558422089 0.532399833202 1.18119966984 -6.52558422089 0.550041973591 0.879499137402 -6.52558422089 0.554643213749 0.76660835743 -6.52558422089 0.541931807995 0.594120919704 -6.52558422089 0.513218045235 0.468372315168 -6.52558422089 0.481006681919 0.33037507534 -6.52558422089 0.423333257437 0.144704937935 -6.52558422089 0.391079455614 0.0520776920021 -6.52558422089 0.278955519199 -0.164202004671 -6.52558422089 0.247431159019 -0.208472236991 -6.52558422089 0.100826591253 -0.417827486992 -6.52558422089 0.0823948383331 -0.438424140215 -6.52558422089 -0.05186798051 -0.561627089977 -6.52558422089 -0.0992380455136 -0.606241703033 -6.52558422089 -0.253524392843 -0.746714353561 -6.52558422089 -0.272837996483 -0.764613747597 -6.52558422089 -0.283812314272 -0.772717237473 -6.52558422089 -0.48710373044 -0.899126410484 -6.52558422089 -0.576333045959 -0.937699735165 -6.52558422089 -0.725385308266 -1.01074337959 -6.52558422089 -0.876574575901 -1.08356690407 -6.52558422089 -0.935566663742 -1.11390006542 -6.52558422089 -1.0056937933 -1.14356195927 -6.52558422089 -1.1373821497 -1.19426095486 -6.52558422089 -1.24538469315 -1.215498209 -6.52558422089 -1.37131047249 -1.24312674999 -6.52558422089 -1.55886459351 -1.27628815174 -6.52558422089 -1.62772142887 -1.28663885593 -6.52558422089 -1.6278488636 -1.28589510918 -6.52558422089 -1.74944269657 -1.28331077099 -6.52558422089 -1.74939501286 -1.2802978754 -6.52558422089 -1.93434000015 -1.26950359344 -6.52558422089 -1.93455469608 -1.27166104317 -6.52558422089 -2.0264582634 -1.26302278042 -6.52558422089 -2.02656364441 -1.26338887215 -6.52558422089 -2.05870604515 -1.25730013847 -6.52558422089 -2.05837416649 -1.25564193726 -6.52558422089 -2.14859890938 -1.23455774784 -6.52558422089 -2.14962244034 -1.23869717121 -6.52558422089 -2.26316022873 -1.20983707905 -6.52558422089 -2.26280236244 -1.20837950706 -6.52558422089 -2.35627269745 -1.18005800247 -6.52558422089 -2.35589289665 -1.17875492573 -6.52558422089 -2.48921012878 -1.14035987854 -6.52558422089 -2.48953151703 -1.14141774178 -6.52558422089 -2.58405423164 -1.11692118645 -6.52558422089 -2.58431386948 -1.11773443222 -6.52558422089 -2.61233496666 -1.10861778259 -6.52558422089 -2.61195516586 -1.10744941235 -6.52558422089 -2.6977584362 -1.07290816307 -6.52558422089 -2.69948577881 -1.07659554482 -6.52558422089 -2.79226827621 -1.03315389156 -6.52558422089 -2.79141902924 -1.03137946129 -6.52558422089 -2.87407255173 -0.988539636135 -6.52558422089 -2.87452197075 -0.989407479763 -6.52558422089 -2.98623609543 -0.933519363403 -6.52558422089 -2.98555588722 -0.932068169117 -6.52558422089 -3.06778407097 -0.893563985825 -6.52558422089 -3.06885647774 -0.895752072334 -6.52558422089 -3.17043638229 -0.844849586487 -6.52558422089 -3.16817760468 -0.840403139591 -6.52558422089 -3.24777293205 -0.795855402946 -6.52558422089 -3.24781107903 -0.795923411846 -6.52558422089 -3.25983929634 -0.789268910885 -6.52558422089 -3.25974965096 -0.789095282555 -6.52558422089 -3.37015748024 -0.732371330261 -6.52558422089 -3.37309026718 -0.738144040108 -6.52558422089 -3.57559657097 -0.637013912201 -6.52558422089 -3.57308912277 -0.631920874119 -6.52558422089 -3.68319678307 -0.578242063522 -6.52558422089 -3.68341803551 -0.578698575497 -6.52558422089 -3.7146384716 -0.564545154572 -6.52558422089 -3.71465778351 -0.564585447311 -6.52558422089 -3.81564116478 -0.517757356167 -6.52558422089 -3.81818294525 -0.522318005562 -6.52558422089 -3.96012330055 -0.444415301085 -6.52558422089 -3.95800733566 -0.440561562777 -6.52558422089 -4.05243206024 -0.38082447648 -6.52558422089 -4.0524725914 -0.380887836218 -6.52558422089 -4.12425422668 -0.335330992937 -6.52558422089 -4.12424612045 -0.335318028927 -6.52558422089 -4.21934127808 -0.274577587843 -6.52558422089 -4.22214317322 -0.278339505196 -6.52558422089 -4.34437847137 -0.187549889088 -6.52558422089 -4.33922767639 -0.180781573057 -6.52558422089 -4.42816781998 -0.109569579363 -6.52558422089 -4.42882490158 -0.110392659903 -6.52558422089 -4.47455787659 -0.0760589390993 -6.52558422089 -4.47459506989 -0.0761076435447 -6.52558422089 -4.5683131218 -0.00492264982313 -6.52558422089 -4.5734872818 -0.0105248140171 -6.52558422089 -4.72357273102 0.126897931099 -6.52558422089 -4.7166686058 0.134404301643 -6.52558422089 -4.79594182968 0.220539778471 -6.52558422089 -4.79602003098 0.220470786095 -6.52558422089 -4.80009078979 0.225100070238 -6.52558422089 -4.80007410049 0.225116863847 -6.52558422089 -4.86757230759 0.308330833912 -6.52558422089 -4.8725271225 0.30513432622 -6.52558422089 -4.96674060822 0.444162160158 -6.52558422089 -4.9628777504 0.446767687798 -6.52558422089 -5.0167131424 0.539334237576 -6.52558422089 -5.01787424088 0.538695633411 -6.52558422089 -5.08085823059 0.65374404192 -6.52558422089 -5.07846927643 0.655044674873 -6.52558422089 -5.12652444839 0.748356044292 -6.52558422089 -5.12773990631 0.747831761837 -6.52558422089 -5.13868951797 0.773404479027 -6.52558422089 -5.13805341721 0.773684859276 -6.52558422089 -5.17068481445 0.8714017272 -6.52558422089 -5.17000865936 0.87162822485 -6.52558422089 -5.21446704865 1.00889956951 -6.52558422089 -5.21446561813 1.00890004635 -6.52558422089 -5.24400234222 1.10051751137 -6.52558422089 -5.24641513824 1.09989202023 -6.52558422089 -5.27436351776 1.20738744736 -6.52558422089 -5.27067565918 1.20834577084 -6.52558422089 -5.29105615616 1.31010651588 -6.52558422089 -5.2924079895 1.30988705158 -6.52558422089 -5.31001234055 1.41837775707 -6.52558422089 -5.30786323547 1.41874957085 -6.52558422089 -5.32018184662 1.52374315262 -6.52558422089 -5.31996822357 1.52376842499 -6.52558422089 -5.33191871643 1.63282978535 -6.52558422089 -5.33205080032 1.63281583786 -6.52558422089 -5.34331226349 1.73831045628 -6.52558422089 -5.34543609619 1.73825490475 -6.52558422089 -5.34809064865 1.83120381832 -6.52558422089 -5.34625911713 1.83162677288 -6.52558422089 -5.33828735352 1.91606044769 -6.52558422089 -5.33743190765 1.91598582268 -6.52558422089 -5.324614048 2.00261592865 -6.52558422089 -5.32363319397 2.00247311592 -6.52558422089 -5.30939292908 2.10117125511 -6.52558422089 -5.31082820892 2.10140872002 -6.52558422089 -5.29618835449 2.1870932579 -6.52558422089 -5.29551124573 2.18704986572 -6.52558422089 -5.27252054214 2.28654932976 -6.52558422089 -5.27384710312 2.28704786301 -6.52558422089 -5.2478685379 2.37045407295 -6.52558422089 -5.24589538574 2.36983895302 -6.52558422089 -5.20935773849 2.46873283386 -6.52558422089 -5.20887613297 2.46855926514 -6.52558422089 -5.18009662628 2.54845237732 -6.52558422089 -5.18064546585 2.54865694046 -6.52558422089 -5.15187168121 2.62494325638 -6.52558422089 -5.1509141922 2.62459516525 -6.52558422089 -5.12650728226 2.68394231796 -6.52558422089 -5.12653398514 2.68395471573 -6.52558422089 -5.12568712234 2.68584156036 -6.52558422089 -5.12568616867 2.68584156036 -6.52558422089 -5.09339475632 2.7494904995 -6.52558422089 -5.09598827362 2.75128293037 -6.52558422089 -5.04565095901 2.83172559738 -6.52558422089 -5.04408216476 2.83072781563 -6.52558422089 -4.99940443039 2.89381670952 -6.52558422089 -5.0054101944 2.8993165493 -6.52558422089 -4.93386220932 2.97609710693 -6.52558422089 -4.92909574509 2.97195124626 -6.52558422089 -4.86976003647 3.01727747917 -6.52558422089 -4.86867380142 3.0159175396 -6.52558422089 -4.83719205856 3.04155564308 -6.52558422089 -4.8375453949 3.04204559326 -6.52558422089 -4.78996133804 3.08751034737 -6.52558422089 -4.79202222824 3.08971428871 -6.52558422089 -4.72257852554 3.15384411812 -6.52558422089 -4.72037792206 3.15143632889 -6.52558422089 -4.67279338837 3.19426012039 -6.52558422089 -4.67453956604 3.19668245316 -6.52558422089 -4.6312661171 3.22662353516 -6.52558422089 -4.62802553177 3.22194337845 -6.52558422089 -4.57276821136 3.24860739708 -6.52558422089 -4.57276391983 3.24859905243 -6.52558422089 -4.54186296463 3.26349663734 -6.52558422089 -4.54180669785 3.26338362694 -6.52558422089 -4.49116230011 3.28860759735 -6.52558422089 -4.4915599823 3.2894012928 -6.52558422089 -4.41325044632 3.32906007767 -6.52558422089 -4.4127073288 3.32802128792 -6.52558422089 -4.35099887848 3.3605453968 -6.52558422089 -4.35259008408 3.36426734924 -6.52558422089 -4.26839065552 3.39875006676 -6.52558422089 -4.26592111588 3.39326238632 -6.52558422089 -4.2016544342 3.40945363045 -6.52558422089 -4.20164728165 3.40943431854 -6.52558422089 -4.19005537033 3.41186380386 -6.52558422089 -4.19003391266 3.4117667675 -6.52558422089 -4.12261486053 3.42432594299 -6.52558422089 -4.12259197235 3.42421150208 -6.52558422089 -4.03896999359 3.43903446198 -6.52558422089 -4.03895425797 3.43894720078 -6.52558422089 -3.97210693359 3.4505751133 -6.52558422089 -3.97241973877 3.4548535347 -6.52558422089 -3.86072802544 3.46299934387 -6.52558422089 -3.86021113396 3.45653772354 -6.52558422089 -3.77252483368 3.45364308357 -6.52558422089 -3.77253007889 3.45348858833 -6.52558422089 -3.73043704033 3.4519340992 -6.52558422089 -3.73043584824 3.45196390152 -6.52558422089 -3.64188241959 3.44898629189 -6.52558422089 -3.64187884331 3.44908976555 -6.52558422089 -3.52711772919 3.4452829361 -6.52558422089 -3.52704906464 3.44645571709 -6.52558422089 -3.43858671188 3.44134068489 -6.52558422089 -3.43861365318 3.44071269035 -6.52558422089 -3.3493335247 3.43270111084 -6.52558422089 -3.34933447838 3.43263697624 -6.52558422089 -3.26792144775 3.42416644096 -6.52558422089 -3.26796030998 3.42379426956 -6.52558422089 -3.21048617363 3.41777038574 -6.52558422089 -3.21041369438 3.41840600967 -6.52558422089 -3.0987598896 3.40531945229 -6.52558422089 -3.09896063805 3.40363001823 -6.52558422089 -2.95021152496 3.38378000259 -6.52558422089 -2.95019292831 3.38391947746 -6.52558422089 -2.84011363983 3.36942648888 -6.52558422089 -2.84016752243 3.36898756027 -6.52558422089 -2.7338309288 3.35597205162 -6.52558422089 -2.73361849785 3.35762786865 -6.52558422089 -2.62417244911 3.34325146675 -6.52558422089 -2.62431406975 3.34219098091 -6.52558422089 -2.53715825081 3.32943415642 -6.52558422089 -2.53713917732 3.32956409454 -6.52558422089 -2.42751216888 3.31390023232 -6.52558422089 -2.42750954628 3.31391882896 -6.52558422089 -2.27403664589 3.29194903374 -6.52558422089 -2.27394795418 3.29253554344 -6.52558422089 -2.12927484512 3.27068924904 -6.52558422089 -2.1291680336 3.27123403549 -6.52558422089 -1.94242858887 3.23851394653 -6.52558422089 -1.94282829762 3.2362537384 -6.52558422089 -1.79905712605 3.20896792412 -6.52558422089 -1.79902422428 3.2091383934 -6.52558422089 -1.76937794685 3.20337533951 -6.52558422089 -1.76936638355 3.2034239769 -6.52558422089 -1.62728095055 3.1731364727 -6.52558422089 -1.62639033794 3.17623877525 -6.52558422089 -1.42388939857 3.12303829193 -6.52558422089 -1.42423045635 3.12152910233 -6.52558422089 -1.28351783752 3.07695436478 -6.52558422089 -1.2834610939 3.07701945305 -6.52558422089 -1.08942556381 3.00906324387 -6.52558422089 -1.08910965919 3.00966525078 -6.52558422089 -0.95940887928 2.95888614655 -6.52558422089 -0.959351599216 2.95899009705 -6.52558422089 -0.938518941402 2.94941091537 -6.52558422089 -0.938743770123 2.94891762733 -6.52558422089 -0.806506931782 2.88160777092 -6.52558422089 -0.804970920086 2.88439559937 -6.52558422089 -0.64783602953 2.79850149155 -6.52558422089 -0.647542476654 2.79882144928 -6.52558422089 -0.515951752663 2.71580529213 -6.52558422089 -0.515728592873 2.71563649178 -6.52558422089 -0.356160640717 2.59822154045 -6.52558422089 -0.357947945595 2.59578251839 -6.52558422089 -0.235201954842 2.49816608429 -6.52558422089 -0.232777327299 2.50087070465 -6.52558422089 -0.0653661116958 2.35135412216 -6.52558422089 -0.0702165216208 2.34579706192 -6.52558422089 0.0421916879714 2.23171758652 -6.52558422089 0.042302403599 2.23181152344 -6.52558422089 0.0492779873312 2.22377490997 -6.52558422089 0.0489765293896 2.22351241112 -6.52558422089 0.131884545088 2.11633825302 -6.52558422089 0.136849835515 2.11978030205 -6.52558422089 0.238983109593 1.97299921513 -6.52558422089 0.238638415933 1.97255086899 -6.52558422089 0.30858913064 1.85054910183 -6.52558422089 0.31023272872 1.85093402863 -6.52558422089 0.385226547718 1.68540501595 -6.52558422089 0.381953269243 1.68379247189 -6.52558422089 0.432321637869 1.54638373852 -6.52558422089 0.433285295963 1.54662847519 -6.52558422089 0.492393732071 1.35447466373 -6.52558422089 0.486438512802 1.3526724577 -6.52558422089 0.528451263905 1.2005341053 -6.52558422089 0.529412448406 1.20072567463 -6.52558422089 0.532298147678 1.18580269814 -6.52558422089 0.532026052475 1.18573820591 -6.52558422089 0.544800043106 1.03064417839 -6.52558422089 0.542554557323 1.03041553497 -6.52558422089 0.552841186523 0.823078513145 -6.52558422089 0.555568873882 0.823000788689 -6.52558422089 0.553226232529 0.680283546448 -6.52558422089 0.554735183716 0.679396569729 -6.52558422089 0.532383441925 0.530524671078 -6.52558422089 0.527726411819 0.531211614609 -6.52558422089 0.497278779745 0.39933526516 -6.52558422089 0.499568551779 0.398706495762 -6.52558422089 0.455539852381 0.236624598503 -6.52558422089 0.453735202551 0.23702454567 -6.52558422089 0.407995998859 0.0981312766671 -6.52558422089 0.410428464413 0.0970035940409 -6.52558422089 0.343020528555 -0.0595089681447 -6.52558422089 0.3423140347 -0.0605209954083 -6.52558422089 0.264821112156 -0.187331825495 -6.52558422089 0.263105779886 -0.186275303364 -6.52558422089 0.173717156053 -0.312859117985 -6.52558422089 0.18008710444 -0.317874610424 -6.52558422089 0.092255063355 -0.42863675952 -6.52558422089 0.0920940190554 -0.428603112698 -6.52558422089 0.0184497758746 -0.503172457218 -6.52558422089 0.0148607008159 -0.499592423439 -6.52558422089 -0.0756968259811 -0.58377969265 -6.52558422089 -0.0753664001822 -0.584135890007 -6.52558422089 -0.175782829523 -0.677124202251 -6.52558422089 -0.176693350077 -0.676138222218 -6.52558422089 -0.263220578432 -0.755621194839 -6.52558422089 -0.262713760138 -0.75622844696 -6.52558422089 -0.278082996607 -0.768957853317 -6.52558422089 -0.278172522783 -0.768890440464 -6.52558422089 -0.382779419422 -0.839869499207 -6.52558422089 -0.381347090006 -0.843776524067 -6.52558422089 -0.530049026012 -0.921602725983 -6.52558422089 -0.532202780247 -0.917361974716 -6.52558422089 -0.651686310768 -0.972426831722 -6.52558422089 -0.65073621273 -0.974474668503 -6.52558422089 -0.800855636597 -1.04741108418 -6.52558422089 -0.801467120647 -1.04617655277 -6.52558422089 -0.906263172626 -1.09834671021 -6.52558422089 -0.905545294285 -1.09985649586 -6.52558422089 -0.970027208328 -1.1300201416 -6.52558422089 -0.970397472382 -1.12930727005 -6.52558422089 -1.07110667229 -1.16997945309 -6.52558422089 -1.06984531879 -1.17477965355 -6.52558422089 -1.19006299973 -1.20945692062 -6.52558422089 -1.19150567055 -1.20429170132 -6.52558422089 -1.30849075317 -1.22862386703 -6.52558422089 -1.30809104443 -1.23060798645 -6.52558422089 -1.46470856667 -1.26162147522 -6.52558422089 -1.4650285244 -1.26004827023 -6.52558422089 -1.58503711224 -1.28081774712 -6.52558422089 -1.58486807346 -1.28278577328 # objtype='BezierSpline'; ncoords=160; nparts=160; closed=True; sep=' '; name='splines-010' -3.82413864136 -3.2226805687 -1.59789729118 -3.82413864136 -3.13768076897 -1.56262731552 -3.82413864136 -3.0455057621 -1.53257584572 -3.82413864136 -2.93033194542 -1.49220967293 -3.82413864136 -2.86503458023 -1.47925388813 -3.82413864136 -2.71861386299 -1.42189371586 -3.82413864136 -2.69316387177 -1.41094207764 -3.82413864136 -2.54771351814 -1.30739271641 -3.82413864136 -2.52872371674 -1.2938785553 -3.82413864136 -2.37805557251 -1.18357598782 -3.82413864136 -2.35016536713 -1.16558015347 -3.82413864136 -2.21884918213 -1.03060233593 -3.82413864136 -2.21135377884 -1.02272391319 -3.82413864136 -2.21041631699 -1.021484375 -3.82413864136 -2.10532951355 -0.90828448534 -3.82413864136 -2.07558250427 -0.870351016521 -3.82413864136 -2.0211827755 -0.780469655991 -3.82413864136 -1.9755808115 -0.698885560036 -3.82413864136 -1.94732761383 -0.644748806953 -3.82413864136 -1.87655508518 -0.522148013115 -3.82413864136 -1.85933852196 -0.50171661377 -3.82413864136 -1.78952312469 -0.350037485361 -3.82413864136 -1.73587071896 -0.228839948773 -3.82413864136 -1.71649539471 -0.174630686641 -3.82413864136 -1.68655502796 -0.0824085772038 -3.82413864136 -1.65550291538 0.00403879024088 -3.82413864136 -1.63609683514 0.05811579898 -3.82413864136 -1.59009063244 0.185696870089 -3.82413864136 -1.5306634903 0.325892269611 -3.82413864136 -1.52304637432 0.37745437026 -3.82413864136 -1.51045274734 0.558219969273 -3.82413864136 -1.51276636124 0.618239164352 -3.82413864136 -1.51766514778 0.740223288536 -3.82413864136 -1.52236807346 0.874826729298 -3.82413864136 -1.52138221264 0.922845959663 -3.82413864136 -1.54785978794 0.980433642864 -3.82413864136 -1.58535254002 1.08201944828 -3.82413864136 -1.63976669312 1.18472790718 -3.82413864136 -1.66894233227 1.23648071289 -3.82413864136 -1.69767916203 1.26569116116 -3.82413864136 -1.79574728012 1.37450039387 -3.82413864136 -1.90299105644 1.47894561291 -3.82413864136 -1.93208909035 1.5074338913 -3.82413864136 -2.02432394028 1.56987810135 -3.82413864136 -2.08542704582 1.60883319378 -3.82413864136 -2.10227274895 1.61870265007 -3.82413864136 -2.25082659721 1.70647156239 -3.82413864136 -2.32469367981 1.74988138676 -3.82413864136 -2.44013834 1.82441878319 -3.82413864136 -2.59715390205 1.90452826023 -3.82413864136 -2.6358923912 1.92657017708 -3.82413864136 -2.73060941696 1.97448945045 -3.82413864136 -2.81848120689 2.01930093765 -3.82413864136 -2.8533642292 2.03385210037 -3.82413864136 -3.00532889366 2.1008939743 -3.82413864136 -3.10977864265 2.14310669899 -3.82413864136 -3.17737698555 2.17066860199 -3.82413864136 -3.31211733818 2.22181224823 -3.82413864136 -3.35101056099 2.23686766624 -3.82413864136 -3.37022686005 2.2428290844 -3.82413864136 -3.52973294258 2.29085636139 -3.82413864136 -3.62707066536 2.32226753235 -3.82413864136 -3.70462608337 2.34608721733 -3.82413864136 -3.83106827736 2.3842124939 -3.82413864136 -3.85521388054 2.39143204689 -3.82413864136 -3.87333846092 2.39464378357 -3.82413864136 -4.01672029495 2.41991877556 -3.82413864136 -4.13538551331 2.44077682495 -3.82413864136 -4.18309402466 2.44516563416 -3.82413864136 -4.27508831024 2.45343804359 -3.82413864136 -4.35136222839 2.45747256279 -3.82413864136 -4.38512182236 2.45719528198 -3.82413864136 -4.48221206665 2.45178174973 -3.82413864136 -4.61071252823 2.44455337524 -3.82413864136 -4.61213350296 2.4444527626 -3.82413864136 -4.63169717789 2.4365363121 -3.82413864136 -4.74373149872 2.39305925369 -3.82413864136 -4.74979114532 2.38872075081 -3.82413864136 -4.82721757889 2.33450627327 -3.82413864136 -4.86341190338 2.30924153328 -3.82413864136 -4.86645650864 2.30796432495 -3.82413864136 -4.98933315277 2.25709915161 -3.82413864136 -4.99250936508 2.25578165054 -3.82413864136 -5.0957531929 2.18431353569 -3.82413864136 -5.10980081558 2.1730864048 -3.82413864136 -5.16690397263 2.09993648529 -3.82413864136 -5.19508695602 2.06379437447 -3.82413864136 -5.23427963257 2.01298308372 -3.82413864136 -5.28595066071 1.9484307766 -3.82413864136 -5.30729436874 1.92179739475 -3.82413864136 -5.37483358383 1.83801555634 -3.82413864136 -5.38053035736 1.83010232449 -3.82413864136 -5.38907194138 1.81242609024 -3.82413864136 -5.42513132095 1.74686801434 -3.82413864136 -5.43748140335 1.7122426033 -3.82413864136 -5.45614910126 1.65855276585 -3.82413864136 -5.48433113098 1.57840061188 -3.82413864136 -5.4867477417 1.57137155533 -3.82413864136 -5.49131011963 1.55810308456 -3.82413864136 -5.52142810822 1.47028744221 -3.82413864136 -5.55214214325 1.38200831413 -3.82413864136 -5.56341028214 1.34922289848 -3.82413864136 -5.57695770264 1.2497215271 -3.82413864136 -5.58103370667 1.22065889835 -3.82413864136 -5.58710241318 1.1225976944 -3.82413864136 -5.58873510361 1.09359574318 -3.82413864136 -5.59428024292 0.994939386845 -3.82413864136 -5.59606266022 0.962969720364 -3.82413864136 -5.60259962082 0.855557620525 -3.82413864136 -5.60484600067 0.816938102245 -3.82413864136 -5.60358524323 0.724918365479 -3.82413864136 -5.61114645004 0.670133173466 -3.82413864136 -5.59790802002 0.595224201679 -3.82413864136 -5.59020090103 0.52694016695 -3.82413864136 -5.5828795433 0.465947061777 -3.82413864136 -5.57141065598 0.361591309309 -3.82413864136 -5.56492757797 0.307552963495 -3.82413864136 -5.55017614365 0.174648493528 -3.82413864136 -5.54360437393 0.146204262972 -3.82413864136 -5.51619005203 -0.00253032613546 -3.82413864136 -5.51388549805 -0.00963631737977 -3.82413864136 -5.4956111908 -0.0715999603271 -3.82413864136 -5.46742248535 -0.164396196604 -3.82413864136 -5.46443748474 -0.174241900444 -3.82413864136 -5.42583370209 -0.301237314939 -3.82413864136 -5.41595649719 -0.333054542542 -3.82413864136 -5.41516685486 -0.335546076298 -3.82413864136 -5.36234092712 -0.488248765469 -3.82413864136 -5.35104227066 -0.506706476212 -3.82413864136 -5.28073263168 -0.622964978218 -3.82413864136 -5.25558567047 -0.667336523533 -3.82413864136 -5.20026874542 -0.759512245655 -3.82413864136 -5.16115570068 -0.826685726643 -3.82413864136 -5.13217401505 -0.899489462376 -3.82413864136 -5.08270263672 -0.971164166927 -3.82413864136 -5.05959939957 -1.00199198723 -3.82413864136 -4.99350166321 -1.07973361015 -3.82413864136 -4.97454500198 -1.10046589375 -3.82413864136 -4.96769332886 -1.10760736465 -3.82413864136 -4.86787462234 -1.21245658398 -3.82413864136 -4.79760217667 -1.24755275249 -3.82413864136 -4.72686433792 -1.28573000431 -3.82413864136 -4.68401861191 -1.30808007717 -3.82413864136 -4.5982170105 -1.35283732414 -3.82413864136 -4.55153465271 -1.37608909607 -3.82413864136 -4.46477842331 -1.41326093674 -3.82413864136 -4.41746044159 -1.42826044559 -3.82413864136 -4.32177019119 -1.45858931541 -3.82413864136 -4.28227472305 -1.47380816936 -3.82413864136 -4.07685756683 -1.54651522636 -3.82413864136 -4.07356262207 -1.54790055752 -3.82413864136 -4.06320667267 -1.55036556721 -3.82413864136 -3.91113471985 -1.5837097168 -3.82413864136 -3.84584069252 -1.59226417542 -3.82413864136 -3.74949002266 -1.60836756229 -3.82413864136 -3.60423445702 -1.62477886677 -3.82413864136 -3.58520269394 -1.62658381462 -3.82413864136 -3.56680345535 -1.62625336647 -3.82413864136 -3.40044569969 -1.62767481804 -3.82413864136 -3.32594156265 -1.61962282658 -3.82413864136 -3.17891693115 -1.58434307575 -3.82413864136 -3.18080425262 -1.57857620716 -3.82413864136 -3.09225034714 -1.545825243 -3.82413864136 -3.09142279625 -1.54810535908 -3.82413864136 -2.98770427704 -1.51302695274 -3.82413864136 -2.98905277252 -1.50823915005 -3.82413864136 -2.89830183983 -1.48346614838 -3.82413864136 -2.89685487747 -1.48856329918 -3.82413864136 -2.78986740112 -1.45726263523 -3.82413864136 -2.7913312912 -1.45177352428 -3.82413864136 -2.70580196381 -1.41662931442 -3.82413864136 -2.70516896248 -1.41769719124 -3.82413864136 -2.61580014229 -1.36741113663 -3.82413864136 -2.62044334412 -1.35916090012 -3.82413864136 -2.53821921349 -1.30063474178 -3.82413864136 -2.53817272186 -1.3006991148 -3.82413864136 -2.45302248001 -1.23923575878 -3.82413864136 -2.45494818687 -1.23646140099 -3.82413864136 -2.36438751221 -1.17417526245 -3.82413864136 -2.36292433739 -1.17602682114 -3.82413864136 -2.2777774334 -1.10631072521 -3.82413864136 -2.28412866592 -1.09845542908 -3.82413864136 -2.21507954597 -1.026684165 -3.82413864136 -2.21486759186 -1.02686178684 -3.82413864136 -2.21085166931 -1.02213251591 -3.82413864136 -2.21091508865 -1.02207899094 -3.82413864136 -2.16085195541 -0.962386071682 -3.82413864136 -2.15542960167 -0.966970384121 -3.82413864136 -2.0896935463 -0.889968752861 -3.82413864136 -2.08925938606 -0.890144586563 -3.82413864136 -2.04577469826 -0.827212333679 -3.82413864136 -2.04759788513 -0.825866937637 -3.82413864136 -1.99768364429 -0.740083813667 -3.82413864136 -1.99779200554 -0.739996254444 -3.82413864136 -1.96106886864 -0.672025382519 -3.82413864136 -1.96202325821 -0.671504616737 -3.82413864136 -1.9132604599 -0.582723855972 -3.82413864136 -1.91705310345 -0.579861462116 -3.82413864136 -1.86891162395 -0.511255323887 -3.82413864136 -1.8664355278 -0.512892067432 -3.82413864136 -1.81498575211 -0.431875109673 -3.82413864136 -1.82387471199 -0.42612811923 -3.82413864136 -1.76225543022 -0.289638012648 -3.82413864136 -1.76043605804 -0.290341764688 -3.82413864136 -1.72520112991 -0.202127531171 -3.82413864136 -1.72578334808 -0.201871618629 -3.82413864136 -1.70085191727 -0.128749191761 -3.82413864136 -1.70223462582 -0.128277137876 -3.82413864136 -1.67170107365 -0.0389551632106 -3.82413864136 -1.67102241516 -0.0391872487962 -3.82413864136 -1.64579582214 0.0310758203268 -3.82413864136 -1.64582073689 0.0310848020017 -3.82413864136 -1.61314296722 0.121924057603 -3.82413864136 -1.61482465267 0.122584685683 -3.82413864136 -1.56232047081 0.256556212902 -3.82413864136 -1.55108356476 0.253184467554 -3.82413864136 -1.52367377281 0.350779891014 -3.82413864136 -1.52585625648 0.35156494379 -3.82413864136 -1.51327764988 0.467460334301 -3.82413864136 -1.51185619831 0.46776124835 -3.82413864136 -1.5099875927 0.58820438385 -3.82413864136 -1.51158547401 0.588230490685 -3.82413864136 -1.51516664028 0.679233133793 -3.82413864136 -1.5153747797 0.67922526598 -3.82413864136 -1.52019202709 0.807518422604 -3.82413864136 -1.5218834877 0.807511568069 -3.82413864136 -1.52254092693 0.898831546307 -3.82413864136 -1.51661276817 0.899931669235 -3.82413864136 -1.52767634392 0.953085303307 -3.82413864136 -1.53575384617 0.95117110014 -3.82413864136 -1.56854152679 1.03042590618 -3.82413864136 -1.56330609322 1.03270173073 -3.82413864136 -1.60901725292 1.13495719433 -3.82413864136 -1.61189305782 1.13373804092 -3.82413864136 -1.65401387215 1.21079051495 -3.82413864136 -1.65123224258 1.21295464039 -3.82413864136 -1.68115723133 1.25270700455 -3.82413864136 -1.68363666534 1.25077903271 -3.82413864136 -1.74787819386 1.31899869442 -3.82413864136 -1.74499571323 1.32174813747 -3.82413864136 -1.8476139307 1.42841172218 -3.82413864136 -1.84943795204 1.4266525507 -3.82413864136 -1.91755878925 1.49317061901 -3.82413864136 -1.91638433933 1.49460446835 -3.82413864136 -1.97504544258 1.54252552986 -3.82413864136 -1.97778487206 1.53929758072 -3.82413864136 -2.05460119247 1.58977305889 -3.82413864136 -2.05452036858 1.58993661404 -3.82413864136 -2.09375429153 1.61392450333 -3.82413864136 -2.09385895729 1.61375248432 -3.82413864136 -2.17663002014 1.66245043278 -3.82413864136 -2.17649841309 1.6626739502 -3.82413864136 -2.28773474693 1.72821962833 -3.82413864136 -2.28823208809 1.72741055489 -3.82413864136 -2.38317298889 1.78592157364 -3.82413864136 -2.38067579269 1.79017162323 -3.82413864136 -2.51641392708 1.86834931374 -3.82413864136 -2.51959848404 1.86270749569 -3.82413864136 -2.61676383018 1.91510272026 -3.82413864136 -2.61626529694 1.91602957249 -3.82413864136 -2.6826364994 1.95167374611 -3.82413864136 -2.68328952789 1.95045375824 -3.82413864136 -2.77458119392 1.99682450294 -3.82413864136 -2.77375435829 1.99860441685 -3.82413864136 -2.83561968803 2.02723145485 -3.82413864136 -2.83599829674 2.02640032768 -3.82413864136 -2.92967867851 2.06659889221 -3.82413864136 -2.92883896828 2.06857442856 -3.82413864136 -3.05720949173 2.12281513214 -3.82413864136 -3.0575864315 2.12191987038 -3.82413864136 -3.14359903336 2.15683555603 -3.82413864136 -3.14341497421 2.15730166435 -3.82413864136 -3.2444255352 2.197057724 -3.82413864136 -3.24483180046 2.19601964951 -3.82413864136 -3.3315885067 2.22927618027 -3.82413864136 -3.33132910728 2.23001456261 -3.82413864136 -3.36050534248 2.24017381668 -3.82413864136 -3.36060643196 2.23988842964 -3.82413864136 -3.44987821579 2.2671751976 -3.82413864136 -3.45022392273 2.26606011391 -3.82413864136 -3.57855176926 2.30608129501 -3.82413864136 -3.5782930851 2.30690741539 -3.82413864136 -3.66576218605 2.334451437 -3.82413864136 -3.665817976 2.3342769146 -3.82413864136 -3.76779794693 2.36531186104 -3.82413864136 -3.76782512665 2.36522293091 -3.82413864136 -3.84313678741 2.38783621788 -3.82413864136 -3.84297370911 2.38852787018 -3.82413864136 -3.864153862 2.39355325699 -3.82413864136 -3.86427545547 2.39304208755 -3.82413864136 -3.9450237751 2.40731334686 -3.82413864136 -3.94502639771 2.40729880333 -3.82413864136 -4.07605028152 2.43036222458 -3.82413864136 -4.07572460175 2.43280315399 -3.82413864136 -4.1591091156 2.44394755363 -3.82413864136 -4.1592373848 2.44299578667 -3.82413864136 -4.22908687592 2.44934892654 -3.82413864136 -4.22903060913 2.45015025139 -3.82413864136 -4.31317520142 2.45615696907 -3.82413864136 -4.31319904327 2.45662069321 -3.82413864136 -4.36823034286 2.45784902573 -3.82413864136 -4.36825466156 2.45773458481 -3.82413864136 -4.43370389938 2.45564222336 -3.82413864136 -4.43366765976 2.45450043678 -3.82413864136 -4.5464630127 2.44818329811 -3.82413864136 -4.54649209976 2.44863295555 -3.82413864136 -4.61142349243 2.44450831413 -3.82413864136 -4.61144828796 2.44461154938 -3.82413864136 -4.62228727341 2.44210100174 -3.82413864136 -4.62188768387 2.44042420387 -3.82413864136 -4.68755578995 2.4143974781 -3.82413864136 -4.6912946701 2.42141819 -3.82413864136 -4.74698352814 2.39130067825 -3.82413864136 -4.74674987793 2.39087414742 -3.82413864136 -4.78836107254 2.36141109467 -3.82413864136 -4.78848457336 2.36158514023 -3.82413864136 -4.84530544281 2.32186079025 -3.82413864136 -4.84418725967 2.31982660294 -3.82413864136 -4.86485004425 2.30844974518 -3.82413864136 -4.86493253708 2.30859923363 -3.82413864136 -4.9278345108 2.28238677979 -3.82413864136 -4.92790412903 2.28255391121 -3.82413864136 -4.99092149734 2.25644087791 -3.82413864136 -4.99100828171 2.25660037994 -3.82413864136 -5.04731655121 2.22588706017 -3.82413864136 -5.04542016983 2.22177910805 -3.82413864136 -5.10296154022 2.17894792557 -3.82413864136 -5.10352230072 2.17943692207 -3.82413864136 -5.1421995163 2.14031481743 -3.82413864136 -5.13836193085 2.13651895523 -3.82413864136 -5.1810002327 2.08186912537 -3.82413864136 -5.18104314804 2.08190250397 -3.82413864136 -5.21475028992 2.03844046593 -3.82413864136 -5.21445608139 2.038210392 -3.82413864136 -5.25982284546 1.98047709465 -3.82413864136 -5.26010608673 1.98069953918 -3.82413864136 -5.29661893845 1.93511104584 -3.82413864136 -5.29660320282 1.93509864807 -3.82413864136 -5.34100341797 1.87985789776 -3.82413864136 -5.34223031998 1.8807952404 -3.82413864136 -5.37778759003 1.83413946629 -3.82413864136 -5.37804555893 1.83427548409 -3.82413864136 -5.38553333282 1.82170009613 -3.82413864136 -5.38457107544 1.82114553452 -3.82413864136 -5.40622520447 1.77919471264 -3.82413864136 -5.40983247757 1.78087556362 -3.82413864136 -5.43264818192 1.73015892506 -3.82413864136 -5.43137550354 1.72957968712 -3.82413864136 -5.44692230225 1.68543541431 -3.82413864136 -5.44676828384 1.6853812933 -3.82413864136 -5.47017002106 1.61845231056 -3.82413864136 -5.47037982941 1.61852526665 -3.82413864136 -5.48555183411 1.57489037514 -3.82413864136 -5.48553943634 1.5748860836 -3.82413864136 -5.48902893066 1.56473731995 -3.82413864136 -5.48903131485 1.56473827362 -3.82413864136 -5.50638628006 1.51420116425 -3.82413864136 -5.50627231598 1.51416170597 -3.82413864136 -5.53668737411 1.42611408234 -3.82413864136 -5.53686857224 1.42617666721 -3.82413864136 -5.55780696869 1.36562633514 -3.82413864136 -5.55942392349 1.36600697041 -3.82413864136 -5.57495689392 1.30060577393 -3.82413864136 -5.570084095 1.29945838451 -3.82413864136 -5.57896661758 1.23518621922 -3.82413864136 -5.57956171036 1.23524725437 -3.82413864136 -5.58596229553 1.17181944847 -3.82413864136 -5.58420467377 1.171636343 -3.82413864136 -5.58795928955 1.10809910297 -3.82413864136 -5.58791923523 1.10809671879 -3.82413864136 -5.59150981903 1.04426765442 -3.82413864136 -5.59151887894 1.04426813126 -3.82413864136 -5.59517526627 0.978954732418 -3.82413864136 -5.59513092041 0.978952169418 -3.82413864136 -5.59919452667 0.909255683422 -3.82413864136 -5.59940338135 0.909267961979 -3.82413864136 -5.60374879837 0.836249411106 -3.82413864136 -5.60441684723 0.836263298988 -3.82413864136 -5.60586690903 0.770964920521 -3.82413864136 -5.60075473785 0.770714342594 -3.82413864136 -5.60528612137 0.697397232056 -3.82413864136 -5.61166238785 0.697444617748 -3.82413864136 -5.61043691635 0.632567048073 -3.82413864136 -5.60335063934 0.632848918438 -3.82413864136 -5.59299135208 0.561235964298 -3.82413864136 -5.59417533875 0.561068117619 -3.82413864136 -5.58664798737 0.496431052685 -3.82413864136 -5.58638763428 0.49646115303 -3.82413864136 -5.57688426971 0.413799166679 -3.82413864136 -5.57740402222 0.413739413023 -3.82413864136 -5.56830358505 0.334556698799 -3.82413864136 -5.56804943085 0.33458596468 -3.82413864136 -5.55725765228 0.241134703159 -3.82413864136 -5.56138944626 0.240446716547 -3.82413864136 -5.54772806168 0.160283595324 -3.82413864136 -5.54657030106 0.160492807627 -3.82413864136 -5.52823925018 0.0721810907125 -3.82413864136 -5.53470754623 0.0706191733479 -3.82413864136 -5.51527547836 -0.00614347308874 -3.82413864136 -5.51498985291 -0.00606851093471 -3.82413864136 -5.50433444977 -0.0404900573194 -3.82413864136 -5.50487422943 -0.0406557582319 -3.82413864136 -5.48170566559 -0.118054553866 -3.82413864136 -5.48150444031 -0.117994263768 -3.82413864136 -5.46592855453 -0.169318646193 -3.82413864136 -5.4659318924 -0.169319599867 -3.82413864136 -5.44515895844 -0.237746670842 -3.82413864136 -5.44532299042 -0.237797111273 -3.82413864136 -5.42094230652 -0.317160367966 -3.82413864136 -5.42094230652 -0.317160665989 -3.82413864136 -5.41556549072 -0.33430147171 -3.82413864136 -5.41557788849 -0.334305673838 -3.82413864136 -5.3897562027 -0.412229508162 -3.82413864136 -5.39663743973 -0.41562011838 -3.82413864136 -5.35774755478 -0.497976213694 -3.82413864136 -5.35666656494 -0.497462511063 -3.82413864136 -5.31573152542 -0.564740777016 -3.82413864136 -5.31505727768 -0.564349710941 -3.82413864136 -5.26784753799 -0.644968271255 -3.82413864136 -5.26843357086 -0.645310759544 -3.82413864136 -5.22850561142 -0.713761687279 -3.82413864136 -5.22762107849 -0.713243424892 -3.82413864136 -5.18049097061 -0.792968153954 -3.82413864136 -5.17812108994 -0.791837573051 -3.82413864136 -5.14405298233 -0.861815989017 -3.82413864136 -5.15054750443 -0.865166008472 -3.82413864136 -5.11175346375 -0.93763679266 -3.82413864136 -5.10812759399 -0.935822725296 -3.82413864136 -5.07145595551 -0.986797451973 -3.82413864136 -5.0716137886 -0.986947536469 -3.82413864136 -5.0277762413 -1.04184150696 -3.82413864136 -5.02724075317 -1.0414712429 -3.82413864136 -4.9842133522 -1.09026730061 -3.82413864136 -4.984146595 -1.09021496773 -3.82413864136 -4.97116231918 -1.10407721996 -3.82413864136 -4.97111225128 -1.10403001308 -3.82413864136 -4.91768264771 -1.15993523598 -3.82413864136 -4.92520713806 -1.17007374763 -3.82413864136 -4.83676624298 -1.23545324802 -3.82413864136 -4.83245134354 -1.22945213318 -3.82413864136 -4.7619395256 -1.26607584953 -3.82413864136 -4.76236581802 -1.26689147949 -3.82413864136 -4.70552110672 -1.29705536366 -3.82413864136 -4.70544147491 -1.29690504074 -3.82413864136 -4.64111804962 -1.33045876026 -3.82413864136 -4.6413230896 -1.33086156845 -3.82413864136 -4.57498645782 -1.36468029022 -3.82413864136 -4.57518959045 -1.36514127254 -3.82413864136 -4.50872468948 -1.39590215683 -3.82413864136 -4.50896072388 -1.39683783054 -3.82413864136 -4.44154214859 -1.4218981266 -3.82413864136 -4.44111967087 -1.42076122761 -3.82413864136 -4.36961555481 -1.44342589378 -3.82413864136 -4.36910963058 -1.44198381901 -3.82413864136 -4.30180931091 -1.46559107304 -3.82413864136 -4.3021235466 -1.46647274494 -3.82413864136 -4.18008756638 -1.5115724802 -3.82413864136 -4.17843008041 -1.50722467899 -3.82413864136 -4.0751914978 -1.54715967178 -3.82413864136 -4.07525587082 -1.54734730721 -3.82413864136 -4.06852054596 -1.54954826832 -3.82413864136 -4.06839513779 -1.54917931557 -3.82413864136 -3.98732542992 -1.56771409512 -3.82413864136 -3.98774409294 -1.570317626 -3.82413864136 -3.87873029709 -1.58937430382 -3.82413864136 -3.87840199471 -1.58741164207 -3.82413864136 -3.79753828049 -1.59946250916 -3.82413864136 -3.7978451252 -1.60159993172 -3.82413864136 -3.67713117599 -1.61849474907 -3.82413864136 -3.67692995071 -1.61722564697 -3.82413864136 -3.59472751617 -1.62576663494 -3.82413864136 -3.59473919868 -1.62621843815 -3.82413864136 -3.57602286339 -1.62693560123 -3.82413864136 -3.57600355148 -1.62629663944 -3.82413864136 -3.48362970352 -1.62586188316 -3.82413864136 -3.48338532448 -1.63178837299 -3.82413864136 -3.36308574677 -1.62582194805 -3.82413864136 -3.36290073395 -1.62549304962 -3.82413864136 -3.27389860153 -1.61135685444 -3.82413864136 -3.27286195755 -1.61343920231 # objtype='BezierSpline'; ncoords=133; nparts=133; closed=True; sep=' '; name='splines-011' -1.12269330025 -2.19744491577 -0.649691998959 -1.12269330025 -2.2191619873 -0.60410296917 -1.12269330025 -2.29948234558 -0.426245748997 -1.12269330025 -2.35924601555 -0.307624787092 -1.12269330025 -2.39264893532 -0.227742820978 -1.12269330025 -2.49717926979 -0.065270960331 -1.12269330025 -2.51399254799 -0.0370470024645 -1.12269330025 -2.64785766602 0.162848055363 -1.12269330025 -2.66998028755 0.195214390755 -1.12269330025 -2.8429851532 0.375507950783 -1.12269330025 -2.86431241035 0.397262632847 -1.12269330025 -2.92937421799 0.442427515984 -1.12269330025 -3.0435025692 0.522806048393 -1.12269330025 -3.0950255394 0.552223980427 -1.12269330025 -3.22713494301 0.627628207207 -1.12269330025 -3.39455151558 0.68249797821 -1.12269330025 -3.42382478714 0.693509340286 -1.12269330025 -3.46063089371 0.707683265209 -1.12269330025 -3.61216330528 0.765575051308 -1.12269330025 -3.69816994667 0.799334049225 -1.12269330025 -3.79186844826 0.83299356699 -1.12269330025 -3.96566510201 0.855880379677 -1.12269330025 -3.98163843155 0.858234405518 -1.12269330025 -4.01743125916 0.862788379192 -1.12269330025 -4.17544460297 0.880375146866 -1.12269330025 -4.2801990509 0.869406104088 -1.12269330025 -4.35964775085 0.858654618263 -1.12269330025 -4.44804477692 0.840867400169 -1.12269330025 -4.50731563568 0.830786943436 -1.12269330025 -4.5414648056 0.819837033749 -1.12269330025 -4.64757299423 0.796007215977 -1.12269330025 -4.68780994415 0.775871396065 -1.12269330025 -4.77222442627 0.720654606819 -1.12269330025 -4.84800624847 0.667703568935 -1.12269330025 -4.89690637589 0.633688807487 -1.12269330025 -4.93511104584 0.606135368347 -1.12269330025 -4.99949026108 0.558870911598 -1.12269330025 -5.05800914764 0.508577942848 -1.12269330025 -5.09142827988 0.479015797377 -1.12269330025 -5.15393829346 0.396943509579 -1.12269330025 -5.17201185226 0.37373149395 -1.12269330025 -5.22705125809 0.260880053043 -1.12269330025 -5.24181365967 0.230046331882 -1.12269330025 -5.24434280396 0.224384754896 -1.12269330025 -5.26441001892 0.170333787799 -1.12269330025 -5.30092477798 0.0723979100585 -1.12269330025 -5.3046631813 0.0621252432466 -1.12269330025 -5.35517883301 -0.0766911134124 -1.12269330025 -5.36416959763 -0.101489327848 -1.12269330025 -5.40188121796 -0.226748019457 -1.12269330025 -5.41865491867 -0.282015025616 -1.12269330025 -5.43884372711 -0.4357188344 -1.12269330025 -5.45331859589 -0.5089879632 -1.12269330025 -5.47234630585 -0.609168946743 -1.12269330025 -5.49273395538 -0.719727277756 -1.12269330025 -5.50037574768 -0.761688530445 -1.12269330025 -5.50641584396 -0.866016924381 -1.12269330025 -5.51076316833 -0.916588246822 -1.12269330025 -5.50833845139 -1.07996141911 -1.12269330025 -5.50825595856 -1.08549535275 -1.12269330025 -5.50697040558 -1.17337191105 -1.12269330025 -5.50572395325 -1.25406372547 -1.12269330025 -5.50456619263 -1.25909566879 -1.12269330025 -5.46943998337 -1.50434243679 -1.12269330025 -5.45793962479 -1.54426074028 -1.12269330025 -5.40208148956 -1.74669373035 -1.12269330025 -5.38817548752 -1.79695403576 -1.12269330025 -5.34683799744 -1.94804620743 -1.12269330025 -5.33380270004 -1.96784508228 -1.12269330025 -5.23864555359 -2.12880539894 -1.12269330025 -5.23684215546 -2.1317679882 -1.12269330025 -5.23149108887 -2.14051604271 -1.12269330025 -5.12121248245 -2.3187725544 -1.12269330025 -5.05722665787 -2.42249798775 -1.12269330025 -4.99449968338 -2.49519872665 -1.12269330025 -4.84669351578 -2.64942455292 -1.12269330025 -4.84302043915 -2.6531996727 -1.12269330025 -4.84146976471 -2.65469551086 -1.12269330025 -4.70363664627 -2.76788043976 -1.12269330025 -4.66703557968 -2.79000592232 -1.12269330025 -4.5470662117 -2.86166763306 -1.12269330025 -4.45142269135 -2.91494560242 -1.12269330025 -4.38443994522 -2.94887375832 -1.12269330025 -4.23092699051 -3.01661944389 -1.12269330025 -4.21349716187 -3.02427172661 -1.12269330025 -4.16322803497 -3.05033183098 -1.12269330025 -4.04730653763 -3.11337161064 -1.12269330025 -4.00725889206 -3.13485193253 -1.12269330025 -3.8969707489 -3.18987059593 -1.12269330025 -3.81828808784 -3.22585105896 -1.12269330025 -3.73558187485 -3.25436019897 -1.12269330025 -3.62513613701 -3.2741420269 -1.12269330025 -3.54659247398 -3.28966832161 -1.12269330025 -3.42675733566 -3.30698657036 -1.12269330025 -3.35370087624 -3.3117287159 -1.12269330025 -3.24499678612 -3.313798666 -1.12269330025 -3.1882545948 -3.31296181679 -1.12269330025 -3.06828784943 -3.30491471291 -1.12269330025 -3.00295472145 -3.29657840729 -1.12269330025 -2.90324044228 -3.27983188629 -1.12269330025 -2.82390141487 -3.25021624565 -1.12269330025 -2.77051949501 -3.22803497314 -1.12269330025 -2.69216537476 -3.16268181801 -1.12269330025 -2.6721830368 -3.14717078209 -1.12269330025 -2.66084909439 -3.13724374771 -1.12269330025 -2.5715367794 -3.05969619751 -1.12269330025 -2.50385904312 -3.00358247757 -1.12269330025 -2.46767926216 -2.97342634201 -1.12269330025 -2.4003868103 -2.91651844978 -1.12269330025 -2.36515617371 -2.88671875 -1.12269330025 -2.34640550613 -2.86989855766 -1.12269330025 -2.27327466011 -2.79128074646 -1.12269330025 -2.21834349632 -2.70605278015 -1.12269330025 -2.19133090973 -2.65999412537 -1.12269330025 -2.1203315258 -2.52323293686 -1.12269330025 -2.11921477318 -2.52109909058 -1.12269330025 -2.1188313961 -2.52029442787 -1.12269330025 -2.06820869446 -2.36459374428 -1.12269330025 -2.04871797562 -2.23192048073 -1.12269330025 -2.04428792 -2.20314741135 -1.12269330025 -2.02998042107 -2.10958743095 -1.12269330025 -2.01083898544 -1.99066960812 -1.12269330025 -2.00904917717 -1.95690751076 -1.12269330025 -2.008487463 -1.7786642313 -1.12269330025 -2.01202178001 -1.67847585678 -1.12269330025 -2.01550340652 -1.56855130196 -1.12269330025 -2.02402973175 -1.40049922466 -1.12269330025 -2.02558946609 -1.36154198647 -1.12269330025 -2.03182959557 -1.32163417339 -1.12269330025 -2.06061601639 -1.1199785471 -1.12269330025 -2.10012340546 -0.952498197556 -1.12269330025 -2.11923575401 -0.879435181618 -1.12269330025 -2.16508746147 -0.741921842098 -1.12269330025 -2.20705342293 -0.626382231712 -1.12269330025 -2.20853686333 -0.627005755901 -1.12269330025 -2.26022410393 -0.515592753887 -1.12269330025 -2.25745034218 -0.514280736446 -1.12269330025 -2.32809019089 -0.366327047348 -1.12269330025 -2.33149456978 -0.367915868759 -1.12269330025 -2.37733626366 -0.268323034048 -1.12269330025 -2.37258625031 -0.265917301178 -1.12269330025 -2.43741416931 -0.142565310001 -1.12269330025 -2.44632840157 -0.147382766008 -1.12269330025 -2.50582647324 -0.0513079240918 -1.12269330025 -2.50521922112 -0.0509271994233 -1.12269330025 -2.57823967934 0.0645978525281 -1.12269330025 -2.58045244217 0.063220269978 -1.12269330025 -2.65884184837 0.179083332419 -1.12269330025 -2.65766358376 0.180050894618 -1.12269330025 -2.74848151207 0.291860163212 -1.12269330025 -2.75600266457 0.285826802254 -1.12269330025 -2.85359025002 0.386442065239 -1.12269330025 -2.85272407532 0.387480765581 -1.12269330025 -2.89443922043 0.42269307375 -1.12269330025 -2.89692020416 0.419735014439 -1.12269330025 -2.98657393456 0.482422828674 -1.12269330025 -2.98466444016 0.485407680273 -1.12269330025 -3.06851005554 0.538701176643 -1.12269330025 -3.06926298141 0.537516891956 -1.12269330025 -3.16107749939 0.589930951595 -1.12269330025 -3.15797042847 0.596933424473 -1.12269330025 -3.30724143982 0.66317898035 -1.12269330025 -3.31147265434 0.653273582458 -1.12269330025 -3.40929985046 0.687685966492 -1.12269330025 -3.40920996666 0.687946617603 -1.12269330025 -3.44225525856 0.700524330139 -1.12269330025 -3.4422185421 0.700620830059 -1.12269330025 -3.53635835648 0.736729979515 -1.12269330025 -3.53653049469 0.736284732819 -1.12269330025 -3.6552426815 0.78225839138 -1.12269330025 -3.65492963791 0.783085107803 -1.12269330025 -3.74476385117 0.816843271255 -1.12269330025 -3.74376654625 0.821328997612 -1.12269330025 -3.87656140327 0.853531360626 -1.12269330025 -3.8788599968 0.843769192696 -1.12269330025 -3.97366023064 0.856995880604 -1.12269330025 -3.97364091873 0.857136428356 -1.12269330025 -3.99951052666 0.86068803072 -1.12269330025 -3.99951815605 0.860652089119 -1.12269330025 -4.0963640213 0.872201740742 -1.12269330025 -4.09641027451 0.880117833614 -1.12269330025 -4.22780323029 0.88054561615 -1.12269330025 -4.22791671753 0.875679552555 -1.12269330025 -4.31999540329 0.864630877972 -1.12269330025 -4.32013607025 0.865296304226 -1.12269330025 -4.40408563614 0.851184844971 -1.12269330025 -4.40372228622 0.849093794823 -1.12269330025 -4.47759771347 0.835382282734 -1.12269330025 -4.47818517685 0.837896406651 -1.12269330025 -4.52469158173 0.826546251774 -1.12269330025 -4.52417993546 0.824539065361 -1.12269330025 -4.59388113022 0.805578172207 -1.12269330025 -4.59673261642 0.81413179636 -1.12269330025 -4.66860723495 0.788508474827 -1.12269330025 -4.66833734512 0.787062883377 -1.12269330025 -4.73146486282 0.750781834126 -1.12269330025 -4.73044967651 0.748902440071 -1.12269330025 -4.81051158905 0.694765150547 -1.12269330025 -4.81008768082 0.694139122963 -1.12269330025 -4.87243843079 0.650670409203 -1.12269330025 -4.87260293961 0.650903403759 -1.12269330025 -4.91612482071 0.620075941086 -1.12269330025 -4.91606760025 0.619992733002 -1.12269330025 -4.96740007401 0.582639813423 -1.12269330025 -4.96825265884 0.583701074123 -1.12269330025 -5.02966976166 0.534881770611 -1.12269330025 -5.02893066406 0.533932209015 -1.12269330025 -5.07482337952 0.493917018175 -1.12269330025 -5.07631492615 0.495280057192 -1.12269330025 -5.12637376785 0.441409140825 -1.12269330025 -5.1224656105 0.437811881304 -1.12269330025 -5.16291284561 0.385289669037 -1.12269330025 -5.16426944733 0.386144846678 -1.12269330025 -5.20505619049 0.320751518011 -1.12269330025 -5.19973659515 0.317404776812 -1.12269330025 -5.23448801041 0.245490148664 -1.12269330025 -5.23463726044 0.245557934046 -1.12269330025 -5.24311542511 0.227232724428 -1.12269330025 -5.24317073822 0.227253422141 -1.12269330025 -5.25523853302 0.197711497545 -1.12269330025 -5.25435781479 0.197352305055 -1.12269330025 -5.2826333046 0.121353216469 -1.12269330025 -5.28286027908 0.121436841786 -1.12269330025 -5.30281400681 0.0672690048814 -1.12269330025 -5.30279397964 0.0672615915537 -1.12269330025 -5.32992124557 -0.00728277349845 -1.12269330025 -5.32996225357 -0.00726793892682 -1.12269330025 -5.35968160629 -0.0890875458717 -1.12269330025 -5.36002063751 -0.0889753103256 -1.12269330025 -5.38474416733 -0.163548827171 -1.12269330025 -5.38295555115 -0.164139777422 -1.12269330025 -5.41023731232 -0.254390835762 -1.12269330025 -5.41258096695 -0.253882139921 -1.12269330025 -5.43495750427 -0.357526540756 -1.12269330025 -5.42628526688 -0.359271734953 -1.12269330025 -5.44489383698 -0.472548425198 -1.12269330025 -5.44621562958 -0.472327291965 -1.12269330025 -5.46301651001 -0.559042811394 -1.12269330025 -5.46296644211 -0.559053421021 -1.12269330025 -5.4826874733 -0.664420485497 -1.12269330025 -5.48260116577 -0.664436876774 -1.12269330025 -5.49657821655 -0.740703642368 -1.12269330025 -5.49784898758 -0.740553200245 -1.12269330025 -5.50656652451 -0.813473641872 -1.12269330025 -5.50266838074 -0.813905060291 -1.12269330025 -5.5082359314 -0.89132797718 -1.12269330025 -5.50986480713 -0.891257345676 -1.12269330025 -5.51365566254 -0.998129248619 -1.12269330025 -5.50955343246 -0.998274862766 -1.12269330025 -5.50829744339 -1.08272838593 -1.12269330025 -5.50829696655 -1.08272838593 -1.12269330025 -5.50760698318 -1.12943351269 -1.12269330025 -5.50763130188 -1.12943387032 -1.12269330025 -5.50636386871 -1.21371805668 -1.12269330025 -5.51055955887 -1.2142291069 -1.12269330025 -5.50541448593 -1.25661242008 -1.12269330025 -5.50503873825 -1.25655984879 -1.12269330025 -5.48189687729 -1.38076770306 -1.12269330025 -5.49536800385 -1.38351404667 -1.12269330025 -5.465092659 -1.52460253239 -1.12269330025 -5.46357727051 -1.52426981926 -1.12269330025 -5.42944145203 -1.64531672001 -1.12269330025 -5.43004560471 -1.64548695087 -1.12269330025 -5.39513731003 -1.77182626724 -1.12269330025 -5.39509248734 -1.77181398869 -1.12269330025 -5.36739826202 -1.87247025967 -1.12269330025 -5.37870740891 -1.87756454945 -1.12269330025 -5.34201526642 -1.95871198177 -1.12269330025 -5.34007740021 -1.95779395103 -1.12269330025 -5.28430747986 -2.04712891579 -1.12269330025 -5.28674125671 -2.04863548279 -1.12269330025 -5.23775339127 -2.13029241562 -1.12269330025 -5.23774528503 -2.13028764725 -1.12269330025 -5.23417139053 -2.13614487648 -1.12269330025 -5.23417758942 -2.13614869118 -1.12269330025 -5.17657709122 -2.22978305817 -1.12269330025 -5.17629432678 -2.2296090126 -1.12269330025 -5.08918619156 -2.37061476707 -1.12269330025 -5.09312677383 -2.37349796295 -1.12269330025 -5.02894163132 -2.46110391617 -1.12269330025 -5.02679109573 -2.45969247818 -1.12269330025 -4.92266130447 -2.5741891861 -1.12269330025 -4.92088651657 -2.57259178162 -1.12269330025 -4.84486436844 -2.65131902695 -1.12269330025 -4.84488630295 -2.65134167671 -1.12269330025 -4.84225702286 -2.65395975113 -1.12269330025 -4.84227371216 -2.65397977829 -1.12269330025 -4.7749209404 -2.71394729614 -1.12269330025 -4.77625226974 -2.71651792526 -1.12269330025 -4.68622303009 -2.78019738197 -1.12269330025 -4.68536520004 -2.7789914608 -1.12269330025 -4.60714578629 -2.82599449158 -1.12269330025 -4.60757827759 -2.82675123215 -1.12269330025 -4.49965763092 -2.88902306557 -1.12269330025 -4.49975061417 -2.8892583847 -1.12269330025 -4.41827821732 -2.93256258965 -1.12269330025 -4.41835927963 -2.93281292915 -1.12269330025 -4.30863952637 -2.98476529121 -1.12269330025 -4.30771541595 -2.98281955719 -1.12269330025 -4.22221565247 -3.0204539299 -1.12269330025 -4.22207975388 -3.02016830444 -1.12269330025 -4.18796825409 -3.03647732735 -1.12269330025 -4.18823099136 -3.03705406189 -1.12269330025 -4.10496091843 -3.08127450943 -1.12269330025 -4.10535764694 -3.08201909065 -1.12269330025 -4.02731370926 -3.12416934967 -1.12269330025 -4.02743721008 -3.12441015244 -1.12269330025 -3.95253372192 -3.16317081451 -1.12269330025 -3.95256447792 -3.16330194473 -1.12269330025 -3.85794496536 -3.2085211277 -1.12269330025 -3.85840773582 -3.20980715752 -1.12269330025 -3.77772188187 -3.24207353592 -1.12269330025 -3.77778625488 -3.24337697029 -1.12269330025 -3.68145084381 -3.2684469223 -1.12269330025 -3.68026590347 -3.26375675201 -1.12269330025 -3.58579778671 -3.28155255318 -1.12269330025 -3.58603858948 -3.28292393684 -1.12269330025 -3.48693823814 -3.29986786842 -1.12269330025 -3.48692250252 -3.30069637299 -1.12269330025 -3.39037895203 -3.31079006195 -1.12269330025 -3.39026427269 -3.31019473076 -1.12269330025 -3.29940104485 -3.31400680542 -1.12269330025 -3.29935073853 -3.31368207932 -1.12269330025 -3.21662664413 -3.31385946274 -1.12269330025 -3.2165954113 -3.31412053108 -1.12269330025 -3.12820720673 -3.31050682068 -1.12269330025 -3.12809681892 -3.31073117256 -1.12269330025 -3.0355257988 -3.30172872543 -1.12269330025 -3.03552627563 -3.30138969421 -1.12269330025 -2.95295166969 -3.28919243813 -1.12269330025 -2.95185065269 -3.2928583622 -1.12269330025 -2.86252641678 -3.26892137527 -1.12269330025 -2.86328721046 -3.26574397087 -1.12269330025 -2.79701662064 -3.2396171093 -1.12269330025 -2.79496312141 -3.24283695221 -1.12269330025 -2.72737574577 -3.20190906525 -1.12269330025 -2.73190355301 -3.19466114044 -1.12269330025 -2.68231344223 -3.15475344658 -1.12269330025 -2.6819357872 -3.15521526337 -1.12269330025 -2.66637420654 -3.14237928391 -1.12269330025 -2.66652679443 -3.14219498634 -1.12269330025 -2.61627674103 -3.09837388992 -1.12269330025 -2.61662817001 -3.09795689583 -1.12269330025 -2.53802156448 -3.03125810623 -1.12269330025 -2.53766155243 -3.03168296814 -1.12269330025 -2.48574972153 -2.98852777481 -1.12269330025 -2.48571515083 -2.98856878281 -1.12269330025 -2.43393182755 -2.94509291649 -1.12269330025 -2.43403172493 -2.94497394562 -1.12269330025 -2.38277077675 -2.90161943436 -1.12269330025 -2.38255095482 -2.90187168121 -1.12269330025 -2.35566043854 -2.8784468174 -1.12269330025 -2.35538220406 -2.87871456146 -1.12269330025 -2.30814099312 -2.83231973648 -1.12269330025 -2.30609965324 -2.8334980011 -1.12269330025 -2.24227666855 -2.75141334534 -1.12269330025 -2.24490022659 -2.74922585487 -1.12269330025 -2.20435857773 -2.6833178997 -1.12269330025 -2.20423460007 -2.6833562851 -1.12269330025 -2.15409231186 -2.59257388115 -1.12269330025 -2.15594410896 -2.59155464172 -1.12269330025 -2.11977481842 -2.52216506004 -1.12269330025 -2.11975288391 -2.5221760273 -1.12269330025 -2.11901569366 -2.52070045471 -1.12269330025 -2.11899614334 -2.52070760727 -1.12269330025 -2.08857059479 -2.44441795349 -1.12269330025 -2.08681368828 -2.44401502609 -1.12269330025 -2.05297040939 -2.29954385757 -1.12269330025 -2.05869221687 -2.29822278023 -1.12269330025 -2.04655265808 -2.21752643585 -1.12269330025 -2.04649567604 -2.21753501892 -1.12269330025 -2.03711032867 -2.15637111664 -1.12269330025 -2.03731751442 -2.15633869171 -1.12269330025 -2.02064323425 -2.05009198189 -1.12269330025 -2.01721835136 -2.05046892166 -1.12269330025 -2.0090482235 -1.97388410568 -1.12269330025 -2.00952315331 -1.97380042076 -1.12269330025 -2.00654983521 -1.86784815788 -1.12269330025 -2.00705695152 -1.86775839329 -1.12269330025 -2.0092921257 -1.72855460644 -1.12269330025 -2.01034474373 -1.72857308388 -1.12269330025 -2.01386141777 -1.62351691723 -1.12269330025 -2.01323986053 -1.62349200249 -1.12269330025 -2.0189666748 -1.48449230194 -1.12269330025 -2.0202152729 -1.48454558849 -1.12269330025 -2.02491354942 -1.38102531433 -1.12269330025 -2.02369379997 -1.38091135025 -1.12269330025 -2.02755355835 -1.34147489071 -1.12269330025 -2.02884244919 -1.34160792828 -1.12269330025 -2.04689335823 -1.22090661526 -1.12269330025 -2.04172754288 -1.21995711327 -1.12269330025 -2.07657217979 -1.03552091122 -1.12269330025 -2.0793595314 -1.03598713875 -1.12269330025 -2.10923624039 -0.915856420994 -1.12269330025 -2.10848546028 -0.915611863136 -1.12269330025 -2.13986968994 -0.809997498989 -1.12269330025 -2.14162755013 -0.810495793819 -1.12269330025 -2.18090605736 -0.695683717728 -1.12269330025 -2.17884683609 -0.694809615612 # objtype='BezierSpline'; ncoords=135; nparts=135; closed=True; sep=' '; name='splines-012' 1.57875216007 -2.6177213192 -0.601302266121 1.57875216007 -2.61892366409 -0.600349605083 1.57875216007 -2.61977028847 -0.599926531315 1.57875216007 -2.7822470665 -0.518016040325 1.57875216007 -2.90077328682 -0.464298814535 1.57875216007 -2.94733381271 -0.441829651594 1.57875216007 -3.01488757133 -0.423658341169 1.57875216007 -3.12552928925 -0.394445002079 1.57875216007 -3.21295285225 -0.370213925838 1.57875216007 -3.3011071682 -0.344588249922 1.57875216007 -3.41413497925 -0.326435983181 1.57875216007 -3.49248862267 -0.314924687147 1.57875216007 -3.56969857216 -0.310328513384 1.57875216007 -3.68723964691 -0.303104639053 1.57875216007 -3.80732965469 -0.297404140234 1.57875216007 -3.88629627228 -0.293561041355 1.57875216007 -3.97381806374 -0.306025058031 1.57875216007 -4.08544683456 -0.322431176901 1.57875216007 -4.16120815277 -0.333507806063 1.57875216007 -4.24749422073 -0.347013741732 1.57875216007 -4.34898233414 -0.367429167032 1.57875216007 -4.40852451324 -0.379370868206 1.57875216007 -4.45124864578 -0.398509502411 1.57875216007 -4.5690908432 -0.450579285622 1.57875216007 -4.72895908356 -0.521413803101 1.57875216007 -4.73208379745 -0.522748589516 1.57875216007 -4.73407411575 -0.524167478085 1.57875216007 -4.86782026291 -0.624674618244 1.57875216007 -4.96002674103 -0.712214231491 1.57875216007 -4.99600696564 -0.746140658855 1.57875216007 -5.05604839325 -0.828213751316 1.57875216007 -5.11658000946 -0.904665350914 1.57875216007 -5.135887146 -0.945560395718 1.57875216007 -5.20895004272 -1.0838060379 1.57875216007 -5.247402668 -1.15690803528 1.57875216007 -5.30992650986 -1.2704615593 1.57875216007 -5.32920598984 -1.32366251945 1.57875216007 -5.3459072113 -1.42057454586 1.57875216007 -5.36328887939 -1.5198558569 1.57875216007 -5.37601280212 -1.58457183838 1.57875216007 -5.40438604355 -1.73145675659 1.57875216007 -5.41108465195 -1.9337400198 1.57875216007 -5.41180372238 -1.94221150875 1.57875216007 -5.41132068634 -1.94718027115 1.57875216007 -5.39352035522 -2.12418293953 1.57875216007 -5.38190793991 -2.24632072449 1.57875216007 -5.37417840958 -2.30786156654 1.57875216007 -5.35387659073 -2.36802792549 1.57875216007 -5.321393013 -2.47909498215 1.57875216007 -5.2858133316 -2.58138036728 1.57875216007 -5.26128768921 -2.65085959435 1.57875216007 -5.22064304352 -2.74121212959 1.57875216007 -5.17767095566 -2.84592986107 1.57875216007 -5.13288164139 -2.92740917206 1.57875216007 -5.07325696945 -3.03098702431 1.57875216007 -4.99544811249 -3.15661072731 1.57875216007 -4.96725034714 -3.20446777344 1.57875216007 -4.9441576004 -3.2346367836 1.57875216007 -4.84870195389 -3.35493397713 1.57875216007 -4.72382307053 -3.50474429131 1.57875216007 -4.70065546036 -3.5292301178 1.57875216007 -4.67132902145 -3.55885767937 1.57875216007 -4.53873348236 -3.69127869606 1.57875216007 -4.41963148117 -3.79638528824 1.57875216007 -4.35858297348 -3.84595966339 1.57875216007 -4.28236675262 -3.90685272217 1.57875216007 -4.1736164093 -3.99448275566 1.57875216007 -4.11849594116 -4.03785705566 1.57875216007 -4.00409936905 -4.10390377045 1.57875216007 -3.88902711868 -4.17766904831 1.57875216007 -3.8306760788 -4.21052503586 1.57875216007 -3.64054131508 -4.28131771088 1.57875216007 -3.62928318977 -4.28486633301 1.57875216007 -3.42091846466 -4.3532333374 1.57875216007 -3.40584731102 -4.357172966 1.57875216007 -3.24592256546 -4.38807535172 1.57875216007 -3.22523856163 -4.39213895798 1.57875216007 -3.22349238396 -4.39242696762 1.57875216007 -3.03475379944 -4.40798282623 1.57875216007 -3.02384376526 -4.40899419785 1.57875216007 -2.84417319298 -4.42519521713 1.57875216007 -2.82297134399 -4.42715263367 1.57875216007 -2.74941301346 -4.42440319061 1.57875216007 -2.65952277184 -4.42292404175 1.57875216007 -2.62911915779 -4.4183678627 1.57875216007 -2.51434969902 -4.39983654022 1.57875216007 -2.46436429024 -4.39219808578 1.57875216007 -2.37126278877 -4.37791919708 1.57875216007 -2.2981762886 -4.36415052414 1.57875216007 -2.22993302345 -4.35336828232 1.57875216007 -2.15406322479 -4.31020259857 1.57875216007 -2.11340236664 -4.28633356094 1.57875216007 -2.04245185852 -4.24503850937 1.57875216007 -1.99489617348 -4.21781492233 1.57875216007 -1.96391880512 -4.20194530487 1.57875216007 -1.87158060074 -4.15081930161 1.57875216007 -1.79159188271 -4.08284044266 1.57875216007 -1.76671886444 -4.05772018433 1.57875216007 -1.67992675304 -3.94612360001 1.57875216007 -1.65392875671 -3.91196703911 1.57875216007 -1.57842326164 -3.81408715248 1.57875216007 -1.54323494434 -3.76624655724 1.57875216007 -1.51486957073 -3.67892241478 1.57875216007 -1.49008595943 -3.58995723724 1.57875216007 -1.47492241859 -3.53718185425 1.57875216007 -1.43785846233 -3.37203621864 1.57875216007 -1.43281781673 -3.32832765579 1.57875216007 -1.42860281467 -3.23002958298 1.57875216007 -1.42573130131 -3.15320038795 1.57875216007 -1.42499339581 -3.11791706085 1.57875216007 -1.42067074776 -2.98022079468 1.57875216007 -1.42146718502 -2.86557102203 1.57875216007 -1.42631316185 -2.80790519714 1.57875216007 -1.4373036623 -2.71122503281 1.57875216007 -1.45419681072 -2.5927631855 1.57875216007 -1.47841382027 -2.47079062462 1.57875216007 -1.49913036823 -2.37877964973 1.57875216007 -1.52356851101 -2.3011636734 1.57875216007 -1.56643271446 -2.17873954773 1.57875216007 -1.62685525417 -2.03070044518 1.57875216007 -1.64593291283 -1.98439157009 1.57875216007 -1.66335439682 -1.94084560871 1.57875216007 -1.71853888035 -1.76460957527 1.57875216007 -1.79905354977 -1.60295319557 1.57875216007 -1.82276511192 -1.5545040369 1.57875216007 -1.86585128307 -1.46107649803 1.57875216007 -1.92199933529 -1.34374964237 1.57875216007 -1.96096563339 -1.28904283047 1.57875216007 -2.05614757538 -1.15561950207 1.57875216007 -2.15712809563 -1.02062749863 1.57875216007 -2.17962193489 -0.988352000713 1.57875216007 -2.22820425034 -0.936873376369 1.57875216007 -2.31771636009 -0.840510487556 1.57875216007 -2.37791180611 -0.792338788509 1.57875216007 -2.46662688255 -0.716305673122 1.57875216007 -2.61832714081 -0.60083180666 1.57875216007 -2.61827993393 -0.600759208202 1.57875216007 -2.61932086945 -0.60009688139 1.57875216007 -2.61934733391 -0.60013884306 1.57875216007 -2.7010807991 -0.5591147542 1.57875216007 -2.7001953125 -0.557271301746 1.57875216007 -2.84092855453 -0.489941626787 1.57875216007 -2.84184241295 -0.491867423058 1.57875216007 -2.92418551445 -0.453346312046 1.57875216007 -2.92321276665 -0.450804203749 1.57875216007 -2.97997283936 -0.429685890675 1.57875216007 -2.9810898304 -0.43266582489 1.57875216007 -3.07017445564 -0.408923774958 1.57875216007 -3.07029986382 -0.409389585257 1.57875216007 -3.1693136692 -0.382597357035 1.57875216007 -3.16931867599 -0.382602483034 1.57875216007 -3.25710844994 -0.35767737031 1.57875216007 -3.25640821457 -0.354633927345 1.57875216007 -3.35684561729 -0.332061558962 1.57875216007 -3.3575630188 -0.335133939981 1.57875216007 -3.45327162743 -0.320418715477 1.57875216007 -3.45313644409 -0.318978995085 1.57875216007 -3.53092241287 -0.310964971781 1.57875216007 -3.53109574318 -0.312663704157 1.57875216007 -3.6284725666 -0.306773096323 1.57875216007 -3.6284468174 -0.306306540966 1.57875216007 -3.74726176262 -0.299835771322 1.57875216007 -3.74728631973 -0.300290286541 1.57875216007 -3.84681415558 -0.295506209135 1.57875216007 -3.84698700905 -0.291735172272 1.57875216007 -3.93025183678 -0.295602709055 1.57875216007 -3.93007135391 -0.299695342779 1.57875216007 -4.02965068817 -0.314103424549 1.57875216007 -4.0296292305 -0.314249038696 1.57875216007 -4.1233253479 -0.32798370719 1.57875216007 -4.12335634232 -0.327778548002 1.57875216007 -4.20438432693 -0.340042948723 1.57875216007 -4.2045173645 -0.339331328869 1.57875216007 -4.29843521118 -0.356119781733 1.57875216007 -4.29823541641 -0.357236146927 1.57875216007 -4.37875175476 -0.373408645391 1.57875216007 -4.37978363037 -0.370178818703 1.57875216007 -4.4306807518 -0.386456996202 1.57875216007 -4.42986249924 -0.388994604349 1.57875216007 -4.51010322571 -0.424694180489 1.57875216007 -4.51018285751 -0.424514323473 1.57875216007 -4.64904308319 -0.485955744982 1.57875216007 -4.64879179001 -0.486532747746 1.57875216007 -4.73051691055 -0.522091627121 1.57875216007 -4.73061084747 -0.521921813488 1.57875216007 -4.73314332962 -0.523343384266 1.57875216007 -4.73308801651 -0.523445665836 1.57875216007 -4.80156755447 -0.573573350906 1.57875216007 -4.80405092239 -0.57075047493 1.57875216007 -4.91628217697 -0.66565489769 1.57875216007 -4.91384887695 -0.66852325201 1.57875216007 -4.97798776627 -0.729208111763 1.57875216007 -4.97971248627 -0.72768086195 1.57875216007 -5.02951383591 -0.784099817276 1.57875216007 -5.0252571106 -0.787763893604 1.57875216007 -5.08557510376 -0.867002129555 1.57875216007 -5.09103918076 -0.863507390022 1.57875216007 -5.12842512131 -0.923753023148 1.57875216007 -5.12577772141 -0.925340890884 1.57875216007 -5.17084217072 -1.01547145844 1.57875216007 -5.1724858284 -1.01464772224 1.57875216007 -5.22821187973 -1.12033832073 1.57875216007 -5.22782945633 -1.12054371834 1.57875216007 -5.27812051773 -1.21397769451 1.57875216007 -5.28325414658 -1.21160495281 1.57875216007 -5.32156991959 -1.29615414143 1.57875216007 -5.32198381424 -1.29642117023 1.57875216007 -5.34175777435 -1.37100470066 1.57875216007 -5.33749198914 -1.37212967873 1.57875216007 -5.35453176498 -1.47022664547 1.57875216007 -5.35408210754 -1.47031104565 1.57875216007 -5.36931324005 -1.55227661133 1.57875216007 -5.36970472336 -1.55220341682 1.57875216007 -5.39032125473 -1.65799057484 1.57875216007 -5.39605474472 -1.65735602379 1.57875216007 -5.41565704346 -1.83170771599 1.57875216007 -5.40513038635 -1.83275198936 1.57875216007 -5.41133499146 -1.93798220158 1.57875216007 -5.41182947159 -1.93797814846 1.57875216007 -5.41178846359 -1.94469726086 1.57875216007 -5.41156625748 -1.94469630718 1.57875216007 -5.40256738663 -2.03569602966 1.57875216007 -5.40217971802 -2.03565812111 1.57875216007 -5.38754796982 -2.18523550034 1.57875216007 -5.38863325119 -2.18535327911 1.57875216007 -5.37850809097 -2.27714252472 1.57875216007 -5.38106822968 -2.27778410912 1.57875216007 -5.3671245575 -2.33865427971 1.57875216007 -5.36340808868 -2.33774971962 1.57875216007 -5.33650636673 -2.42320609093 1.57875216007 -5.33901834488 -2.42400407791 1.57875216007 -5.30489826202 -2.5306520462 1.57875216007 -5.30372047424 -2.53027868271 1.57875216007 -5.27363014221 -2.616147995 1.57875216007 -5.27497577667 -2.61669111252 1.57875216007 -5.24288225174 -2.69680380821 1.57875216007 -5.24020719528 -2.69570994377 1.57875216007 -5.19829082489 -2.79319858551 1.57875216007 -5.20204544067 -2.79495215416 1.57875216007 -5.15764904022 -2.88780403137 1.57875216007 -5.15567541122 -2.88689422607 1.57875216007 -5.1035823822 -2.97948694229 1.57875216007 -5.10389566422 -2.97969174385 1.57875216007 -5.03537464142 -3.09440922737 1.57875216007 -5.03365373611 -3.09337663651 1.57875216007 -4.98108673096 -3.18038058281 1.57875216007 -4.98274040222 -3.18147659302 1.57875216007 -4.95665550232 -3.22019338608 1.57875216007 -4.95583486557 -3.21965408325 1.57875216007 -4.89695835114 -3.29519724846 1.57875216007 -4.89714813232 -3.29536962509 1.57875216007 -4.78717470169 -3.43058109283 1.57875216007 -4.78855371475 -3.43187427521 1.57875216007 -4.71263504028 -3.51733899117 1.57875216007 -4.71237564087 -3.51711940765 1.57875216007 -4.68616104126 -3.54420733452 1.57875216007 -4.68603467941 -3.54408621788 1.57875216007 -4.60522270203 -3.62525868416 1.57875216007 -4.60700893402 -3.62717461586 1.57875216007 -4.4808588028 -3.74561738968 1.57875216007 -4.48023462296 -3.74507498741 1.57875216007 -4.38962841034 -3.82178783417 1.57875216007 -4.38920497894 -3.82129406929 1.57875216007 -4.32059621811 -3.87655711174 1.57875216007 -4.32041120529 -3.87632703781 1.57875216007 -4.22790050507 -3.95055437088 1.57875216007 -4.22824287415 -3.95098352432 1.57875216007 -4.14618253708 -4.01632833481 1.57875216007 -4.14746189117 -4.01824617386 1.57875216007 -4.06394481659 -4.07479047775 1.57875216007 -4.06050014496 -4.06957006454 1.57875216007 -3.94573807716 -4.13943052292 1.57875216007 -3.94757080078 -4.14246177673 1.57875216007 -3.8603451252 -4.19491767883 1.57875216007 -3.86095309258 -4.19646930695 1.57875216007 -3.73894548416 -4.25310945511 1.57875216007 -3.73645043373 -4.2483716011 1.57875216007 -3.63496112823 -4.28323459625 1.57875216007 -3.63490176201 -4.28305912018 1.57875216007 -3.52490448952 -4.31843948364 1.57875216007 -3.52605104446 -4.32227659225 1.57875216007 -3.41345047951 -4.35543251038 1.57875216007 -3.413438797 -4.35544919968 1.57875216007 -3.32646918297 -4.37519693375 1.57875216007 -3.32586073875 -4.37249994278 1.57875216007 -3.23557734489 -4.39009094238 1.57875216007 -3.23560905457 -4.39026546478 1.57875216007 -3.22436785698 -4.39229631424 1.57875216007 -3.22436976433 -4.39231872559 1.57875216007 -3.12959432602 -4.40402078629 1.57875216007 -3.12908101082 -4.39972352982 1.57875216007 -3.02929639816 -4.40846061707 1.57875216007 -3.0292994976 -4.40849542618 1.57875216007 -2.93401885033 -4.41720724106 1.57875216007 -2.93399977684 -4.41699886322 1.57875216007 -2.8335711956 -4.42616271973 1.57875216007 -2.83359098434 -4.42686223984 1.57875216007 -2.786257267 -4.42815685272 1.57875216007 -2.78620243073 -4.42539310455 1.57875216007 -2.70448064804 -4.42319393158 1.57875216007 -2.70422267914 -4.42662477493 1.57875216007 -2.64423704147 -4.42165851593 1.57875216007 -2.64430761337 -4.42073202133 1.57875216007 -2.57168388367 -4.40942764282 1.57875216007 -2.57177257538 -4.40885972977 1.57875216007 -2.48937344551 -4.39591169357 1.57875216007 -2.48935604095 -4.39602422714 1.57875216007 -2.41781163216 -4.38507127762 1.57875216007 -2.41767859459 -4.38584852219 1.57875216007 -2.33461308479 -4.3716583252 1.57875216007 -2.33481311798 -4.37049436569 1.57875216007 -2.2641415596 -4.3582572937 1.57875216007 -2.26200652122 -4.36460542679 1.57875216007 -2.18941044807 -4.33917140961 1.57875216007 -2.19185018539 -4.33204174042 1.57875216007 -2.13365292549 -4.29840660095 1.57875216007 -2.13375496864 -4.2982301712 1.57875216007 -2.07796573639 -4.26561975479 1.57875216007 -2.07800078392 -4.2655582428 1.57875216007 -2.01872324944 -4.231341362 1.57875216007 -2.01897740364 -4.23086690903 1.57875216007 -1.97960019112 -4.2095246315 1.57875216007 -1.97927558422 -4.21012783051 1.57875216007 -1.91734981537 -4.17713260651 1.57875216007 -1.91477167606 -4.1806883812 1.57875216007 -1.82862448692 -4.12111282349 1.57875216007 -1.8300538063 -4.1184835434 1.57875216007 -1.77863931656 -4.07083702087 1.57875216007 -1.77836275101 -4.07097673416 1.57875216007 -1.72015309334 -4.00470590591 1.57875216007 -1.7230309248 -4.00214624405 1.57875216007 -1.66683912277 -3.92911362648 1.57875216007 -1.66698288918 -3.92900300026 1.57875216007 -1.61633467674 -3.86290550232 1.57875216007 -1.61561107635 -3.86345267296 1.57875216007 -1.56055772305 -3.79037117958 1.57875216007 -1.5566188097 -3.79232740402 1.57875216007 -1.5225430727 -3.7259247303 1.57875216007 -1.52812075615 -3.72286534309 1.57875216007 -1.50154078007 -3.63472247124 1.57875216007 -1.5026576519 -3.63438892365 1.57875216007 -1.48261117935 -3.56353926659 1.57875216007 -1.48171949387 -3.56377005577 1.57875216007 -1.45397162437 -3.45522737503 1.57875216007 -1.45197212696 -3.45535755157 1.57875216007 -1.43418955803 -3.35037660599 1.57875216007 -1.43454921246 -3.35024428368 1.57875216007 -1.42894613743 -3.27931809425 1.57875216007 -1.43057525158 -3.27918410301 1.57875216007 -1.42706155777 -3.19161915779 1.57875216007 -1.42685103416 -3.19162416458 1.57875216007 -1.42521727085 -3.13556289673 1.57875216007 -1.42545473576 -3.13555622101 1.57875216007 -1.42319262028 -3.04905939102 1.57875216007 -1.42151212692 -3.04908514023 1.57875216007 -1.41997051239 -2.9229092598 1.57875216007 -1.41886782646 -2.92279601097 1.57875216007 -1.42277920246 -2.83668756485 1.57875216007 -1.4234675169 -2.83669638634 1.57875216007 -1.4310978651 -2.75949478149 1.57875216007 -1.43112182617 -2.75947713852 1.57875216007 -1.44490587711 -2.65188598633 1.57875216007 -1.44414770603 -2.65172100067 1.57875216007 -1.46464002132 -2.53149294853 1.57875216007 -1.46553087234 -2.53161287308 1.57875216007 -1.48818469048 -2.42466068268 1.57875216007 -1.48687005043 -2.42427253723 1.57875216007 -1.50970840454 -2.33952927589 1.57875216007 -1.51073646545 -2.33976793289 1.57875216007 -1.54402339458 -2.23962688446 1.57875216007 -1.54346251488 -2.23936891556 1.57875216007 -1.59474802017 -2.10400176048 1.57875216007 -1.59652328491 -2.1046705246 1.57875216007 -1.63635623455 -2.00753045082 1.57875216007 -1.63651251793 -2.00759410858 1.57875216007 -1.65475463867 -1.96266365051 1.57875216007 -1.65549528599 -1.96292173862 1.57875216007 -1.69429969788 -1.85392129421 1.57875216007 -1.68415975571 -1.84999489784 1.57875216007 -1.75215911865 -1.68110907078 1.57875216007 -1.7590777874 -1.68392038345 1.57875216007 -1.810993433 -1.57877016068 1.57875216007 -1.81118988991 -1.57886195183 1.57875216007 -1.84484326839 -1.50804460049 1.57875216007 -1.84397661686 -1.50763452053 1.57875216007 -1.89350605011 -1.40221607685 1.57875216007 -1.88909721375 -1.39956712723 1.57875216007 -1.93898940086 -1.3149266243 1.57875216007 -1.94147241116 -1.31638908386 1.57875216007 -2.00853204727 -1.2223136425 1.57875216007 -2.00780892372 -1.2217849493 1.57875216007 -2.10586881638 -1.08756160736 1.57875216007 -2.10778522491 -1.08895218372 1.57875216007 -2.16864275932 -1.00468313694 1.57875216007 -2.16724801064 -1.00357365608 1.57875216007 -2.20188546181 -0.960964441299 1.57875216007 -2.20401525497 -0.962708294392 1.57875216007 -2.27314996719 -0.888869524002 1.57875216007 -2.2696659565 -0.88514560461 1.57875216007 -2.34588289261 -0.814345836639 1.57875216007 -2.34822797775 -0.816924512386 1.57875216007 -2.42289686203 -0.755079746246 1.57875216007 -2.42120504379 -0.753005027771 1.57875216007 -2.5404446125 -0.656663358212 1.57875216007 -2.54274082184 -0.659533560276 # objtype='BezierSpline'; ncoords=126; nparts=126; closed=True; sep=' '; name='splines-013' 4.28019762039 -0.870798170567 -2.10572814941 4.28019762039 -0.801010012627 -2.23753046989 4.28019762039 -0.749856710434 -2.37500119209 4.28019762039 -0.702518641949 -2.51770210266 4.28019762039 -0.647763073444 -2.69273304939 4.28019762039 -0.626629114151 -2.79075598717 4.28019762039 -0.606939792633 -2.97770190239 4.28019762039 -0.598661363125 -3.05135178566 4.28019762039 -0.581814944744 -3.26255106926 4.28019762039 -0.580817699432 -3.31518006325 4.28019762039 -0.594239473343 -3.54483580589 4.28019762039 -0.596972167492 -3.56937527657 4.28019762039 -0.627407848835 -3.82394695282 4.28019762039 -0.627657830715 -3.82601642609 4.28019762039 -0.632375597954 -3.84124279022 4.28019762039 -0.692432165146 -4.04725503922 4.28019762039 -0.704270064831 -4.07301998138 4.28019762039 -0.775892615318 -4.26264381409 4.28019762039 -0.796228766441 -4.30488157272 4.28019762039 -0.905666470528 -4.46803808212 4.28019762039 -0.918744087219 -4.48296785355 4.28019762039 -1.00214445591 -4.57911920547 4.28019762039 -1.06494212151 -4.65103149414 4.28019762039 -1.08369624615 -4.66433334351 4.28019762039 -1.24315202236 -4.76340675354 4.28019762039 -1.41869711876 -4.86047124863 4.28019762039 -1.42768228054 -4.86522722244 4.28019762039 -1.4378221035 -4.86916255951 4.28019762039 -1.60908091068 -4.93909692764 4.28019762039 -1.76699745655 -4.97829818726 4.28019762039 -1.79530584812 -4.98403120041 4.28019762039 -1.83177423477 -4.99111700058 4.28019762039 -1.99927902222 -5.02446603775 4.28019762039 -2.17099070549 -5.04284477234 4.28019762039 -2.20880103111 -5.04507160187 4.28019762039 -2.24316167831 -5.04249429703 4.28019762039 -2.4151160717 -5.03121232986 4.28019762039 -2.60237264633 -5.01664543152 4.28019762039 -2.6211707592 -5.01509666443 4.28019762039 -2.63986063004 -5.01136255264 4.28019762039 -2.84102153778 -4.97521686554 4.28019762039 -3.05484676361 -4.9142537117 4.28019762039 -3.05604529381 -4.91389942169 4.28019762039 -3.05713200569 -4.91357898712 4.28019762039 -3.2696750164 -4.85084676743 4.28019762039 -3.45186901093 -4.77067565918 4.28019762039 -3.46945381165 -4.76313352585 4.28019762039 -3.49237632751 -4.75255393982 4.28019762039 -3.6828546524 -4.66298246384 4.28019762039 -3.83169269562 -4.57570552826 4.28019762039 -3.88463068008 -4.54428863525 4.28019762039 -3.9486310482 -4.503033638 4.28019762039 -4.08030366898 -4.41691350937 4.28019762039 -4.1771197319 -4.33444499969 4.28019762039 -4.25960302353 -4.26318836212 4.28019762039 -4.40567493439 -4.12886571884 4.28019762039 -4.42083740234 -4.11520719528 4.28019762039 -4.48252439499 -4.0451464653 4.28019762039 -4.56618022919 -3.95014762878 4.28019762039 -4.58447933197 -3.92901611328 4.28019762039 -4.70605325699 -3.77010726929 4.28019762039 -4.73986721039 -3.72652959824 4.28019762039 -4.74601507187 -3.71794581413 4.28019762039 -4.87476110458 -3.51273608208 4.28019762039 -4.9062871933 -3.45949745178 4.28019762039 -4.9703540802 -3.32458972931 4.28019762039 -4.99613761902 -3.26029324532 4.28019762039 -5.04413700104 -3.1433699131 4.28019762039 -5.07955741882 -3.0493285656 4.28019762039 -5.1056394577 -2.94919967651 4.28019762039 -5.12950468063 -2.82384943962 4.28019762039 -5.14349651337 -2.75076675415 4.28019762039 -5.16191196442 -2.60167717934 4.28019762039 -5.16042709351 -2.56080842018 4.28019762039 -5.1531586647 -2.38716363907 4.28019762039 -5.15305233002 -2.38461828232 4.28019762039 -5.15302944183 -2.38408517838 4.28019762039 -5.15101575851 -2.36747264862 4.28019762039 -5.13008975983 -2.19155263901 4.28019762039 -5.1283121109 -2.18646121025 4.28019762039 -5.07037591934 -2.02369904518 4.28019762039 -5.06425952911 -2.00715136528 4.28019762039 -5.0153594017 -1.85864543915 4.28019762039 -4.99116373062 -1.82190287113 4.28019762039 -4.9031996727 -1.70494031906 4.28019762039 -4.82313489914 -1.59998893738 4.28019762039 -4.79153633118 -1.55721306801 4.28019762039 -4.67192697525 -1.44476735592 4.28019762039 -4.67017841339 -1.44314265251 4.28019762039 -4.66461515427 -1.43889260292 4.28019762039 -4.54153299332 -1.34335494041 4.28019762039 -4.48798704147 -1.30343317986 4.28019762039 -4.38047885895 -1.22505569458 4.28019762039 -4.24974060059 -1.15955138206 4.28019762039 -4.20100402832 -1.13532578945 4.28019762039 -4.09008026123 -1.08006596565 4.28019762039 -4.02000379562 -1.0450463295 4.28019762039 -3.98619222641 -1.03428089619 4.28019762039 -3.83711361885 -0.988301157951 4.28019762039 -3.67110013962 -0.940915882587 4.28019762039 -3.62946867943 -0.92976641655 4.28019762039 -3.56646490097 -0.92056787014 4.28019762039 -3.415610075 -0.89645332098 4.28019762039 -3.32362604141 -0.884926497936 4.28019762039 -3.19906425476 -0.872159600258 4.28019762039 -3.0074930191 -0.870254218578 4.28019762039 -2.97985959053 -0.869697809219 4.28019762039 -2.95784139633 -0.87006008625 4.28019762039 -2.71620583534 -0.880872249603 4.28019762039 -2.51440191269 -0.911460697651 4.28019762039 -2.4614276886 -0.919621169567 4.28019762039 -2.40309381485 -0.936458408833 4.28019762039 -2.25063872337 -0.980073034763 4.28019762039 -2.1285405159 -1.02498602867 4.28019762039 -2.04769492149 -1.05866408348 4.28019762039 -1.98315453529 -1.09035801888 4.28019762039 -1.85291218758 -1.15952813625 4.28019762039 -1.70152938366 -1.24884152412 4.28019762039 -1.64716517925 -1.28021836281 4.28019762039 -1.52609968185 -1.38150167465 4.28019762039 -1.41340959072 -1.47311592102 4.28019762039 -1.37351620197 -1.51021420956 4.28019762039 -1.18677639961 -1.68167471886 4.28019762039 -1.11248779297 -1.7650976181 4.28019762039 -1.01205670834 -1.88574683666 4.28019762039 -0.939604640007 -1.98581087589 4.28019762039 -0.834795415401 -2.17101788521 4.28019762039 -0.831459760666 -2.16963601112 4.28019762039 -0.77106231451 -2.30430555344 4.28019762039 -0.774190783501 -2.30582880974 4.28019762039 -0.724914014339 -2.44590353966 4.28019762039 -0.725575327873 -2.446154356 4.28019762039 -0.674393892288 -2.60497689247 4.28019762039 -0.671115040779 -2.60415625572 4.28019762039 -0.634994924068 -2.74116420746 4.28019762039 -0.634538352489 -2.74131941795 4.28019762039 -0.61180216074 -2.88343191147 4.28019762039 -0.617111444473 -2.88426446915 4.28019762039 -0.602929472923 -3.01454091072 4.28019762039 -0.602204203606 -3.01446962357 4.28019762039 -0.588533341885 -3.1567876339 4.28019762039 -0.587030053139 -3.15679311752 4.28019762039 -0.580519258976 -3.28882622719 4.28019762039 -0.580299258232 -3.28888559341 4.28019762039 -0.583083570004 -3.43009567261 4.28019762039 -0.584518909454 -3.43026328087 4.28019762039 -0.595282793045 -3.55713295937 4.28019762039 -0.595556199551 -3.55711126328 4.28019762039 -0.611674904823 -3.69672060013 4.28019762039 -0.612112343311 -3.69667053223 4.28019762039 -0.627532184124 -3.82498168945 4.28019762039 -0.627441108227 -3.82500123978 4.28019762039 -0.629315197468 -3.83377933502 4.28019762039 -0.63008081913 -3.83361029625 4.28019762039 -0.66326713562 -3.94398927689 4.28019762039 -0.655020534992 -3.94700431824 4.28019762039 -0.697375535965 -4.06050157547 4.28019762039 -0.698805868626 -4.05994749069 4.28019762039 -0.743332386017 -4.16647291183 4.28019762039 -0.736003935337 -4.16957950592 4.28019762039 -0.785117685795 -4.2841668129 4.28019762039 -0.784616351128 -4.28458929062 4.28019762039 -0.844894647598 -4.38992357254 4.28019762039 -0.845944821835 -4.39030313492 4.28019762039 -0.91169989109 -4.47589111328 4.28019762039 -0.91222345829 -4.47548723221 4.28019762039 -0.960560917854 -4.5309419632 4.28019762039 -0.960364162922 -4.53111314774 4.28019762039 -1.03348314762 -4.61512756348 4.28019762039 -1.02977442741 -4.61924505234 4.28019762039 -1.0734115839 -4.65868663788 4.28019762039 -1.07412528992 -4.65797424316 4.28019762039 -1.16184079647 -4.71625328064 4.28019762039 -1.16221654415 -4.71592855453 4.28019762039 -1.32963418961 -4.81413888931 4.28019762039 -1.33048856258 -4.81274461746 4.28019762039 -1.42316758633 -4.86289024353 4.28019762039 -1.42306661606 -4.86311864853 4.28019762039 -1.43262052536 -4.86748313904 4.28019762039 -1.43276977539 -4.86715078354 4.28019762039 -1.52375054359 -4.90337896347 4.28019762039 -1.52138161659 -4.91047096252 4.28019762039 -1.68621861935 -4.96427536011 4.28019762039 -1.68765032291 -4.96042394638 4.28019762039 -1.78108263016 -4.98147106171 4.28019762039 -1.78114056587 -4.9812207222 4.28019762039 -1.81352579594 -4.98764610291 4.28019762039 -1.81354832649 -4.98753213882 4.28019762039 -1.91556465626 -5.00759840012 4.28019762039 -1.91494727135 -5.01158475876 4.28019762039 -2.0845489502 -5.03749084473 4.28019762039 -2.08496427536 -5.03571176529 4.28019762039 -2.18985843658 -5.04440927505 4.28019762039 -2.1899061203 -5.04522323608 4.28019762039 -2.22599053383 -5.04493379593 4.28019762039 -2.22597575188 -5.04370260239 4.28019762039 -2.32911062241 -5.03645133972 4.28019762039 -2.32917618752 -5.03737401962 4.28019762039 -2.50878500938 -5.02449655533 4.28019762039 -2.50876140594 -5.02414274216 4.28019762039 -2.61177349091 -5.01589250565 4.28019762039 -2.61184716225 -5.01640748978 4.28019762039 -2.63059186935 -5.0137720108 4.28019762039 -2.63049840927 -5.01313877106 4.28019762039 -2.74025630951 -4.99231529236 4.28019762039 -2.74159383774 -4.99826288223 4.28019762039 -2.94918823242 -4.9501452446 4.28019762039 -2.94808411598 -4.94525194168 4.28019762039 -3.05544686317 -4.91407966614 4.28019762039 -3.0554459095 -4.91407632828 4.28019762039 -3.05658864975 -4.91373920441 4.28019762039 -3.05658864975 -4.91373920441 4.28019762039 -3.16340756416 -4.88222694397 4.28019762039 -3.16582965851 -4.88884353638 4.28019762039 -3.36295127869 -4.81671714783 4.28019762039 -3.3605864048 -4.81033372879 4.28019762039 -3.46064352989 -4.7668633461 4.28019762039 -3.46071434021 -4.76702356339 4.28019762039 -3.48098492622 -4.75800085068 4.28019762039 -3.48093414307 -4.75788450241 4.28019762039 -3.58777451515 -4.70810937881 4.28019762039 -3.58984184265 -4.71199321747 4.28019762039 -3.75909876823 -4.62280750275 4.28019762039 -3.75738883018 -4.61953926086 4.28019762039 -3.85820269585 -4.56006669998 4.28019762039 -3.85846114159 -4.56048107147 4.28019762039 -3.91700124741 -4.52425956726 4.28019762039 -3.9166996479 -4.52376699448 4.28019762039 -4.01460981369 -4.46019268036 4.28019762039 -4.01744222641 -4.46394920349 4.28019762039 -4.13111639023 -4.37889289856 4.28019762039 -4.12885570526 -4.37584733963 4.28019762039 -4.21848487854 -4.29896068573 4.28019762039 -4.21892356873 -4.29944753647 4.28019762039 -4.33366298676 -4.19717550278 4.28019762039 -4.33229637146 -4.19565057755 4.28019762039 -4.41322088242 -4.12199783325 4.28019762039 -4.41367530823 -4.12245082855 4.28019762039 -4.4535984993 -4.08207273483 4.28019762039 -4.45167970657 -4.08017587662 4.28019762039 -4.52435064316 -3.99764561653 4.28019762039 -4.52455043793 -3.99781990051 4.28019762039 -4.5753736496 -3.93962001801 4.28019762039 -4.57565832138 -3.93984937668 4.28019762039 -4.64761734009 -3.85147595406 4.28019762039 -4.64499568939 -3.84935307503 4.28019762039 -4.72288560867 -3.74826097488 4.28019762039 -4.72338438034 -3.74863481522 4.28019762039 -4.74302244186 -3.72229814529 4.28019762039 -4.74307537079 -3.7223277092 4.28019762039 -4.81346607208 -3.61740612984 4.28019762039 -4.81171560287 -3.61615037918 4.28019762039 -4.89086341858 -3.48632359505 4.28019762039 -4.89177036285 -3.48677968979 4.28019762039 -4.94132804871 -3.39364385605 4.28019762039 -4.94044065475 -3.39297103882 4.28019762039 -4.98422908783 -3.29287171364 4.28019762039 -4.98311471939 -3.29238820076 4.28019762039 -5.01989841461 -3.20173454285 4.28019762039 -5.02099943161 -3.20217084885 4.28019762039 -5.06253290176 -3.09661912918 4.28019762039 -5.0643696785 -3.09715032578 4.28019762039 -5.09519577026 -3.00008893013 4.28019762039 -5.09428119659 -2.99964308739 4.28019762039 -5.11964702606 -2.88699173927 4.28019762039 -5.11753988266 -2.88651847839 4.28019762039 -5.13648176193 -2.7873044014 4.28019762039 -5.13771820068 -2.78749966621 4.28019762039 -5.15516233444 -2.67660856247 4.28019762039 -5.15867185593 -2.67648053169 4.28019762039 -5.16279411316 -2.58131313324 4.28019762039 -5.16122579575 -2.58124065399 4.28019762039 -5.15703248978 -2.47397661209 4.28019762039 -5.15678930283 -2.47398614883 4.28019762039 -5.15310525894 -2.38589096069 4.28019762039 -5.15310621262 -2.38589096069 4.28019762039 -5.15304088593 -2.38435173035 4.28019762039 -5.15305137634 -2.38435077667 4.28019762039 -5.15234661102 -2.37575244904 4.28019762039 -5.15201330185 -2.37578010559 4.28019762039 -5.14045476913 -2.27952432632 4.28019762039 -5.14992046356 -2.27734732628 4.28019762039 -5.12948608398 -2.18894100189 4.28019762039 -5.12920856476 -2.18900418282 4.28019762039 -5.09959077835 -2.10499310493 4.28019762039 -5.09983444214 -2.10490226746 4.28019762039 -5.06736803055 -2.01540708542 4.28019762039 -5.06716823578 -2.0154774189 4.28019762039 -5.03848314285 -1.93336176872 4.28019762039 -5.04908180237 -1.92841684818 4.28019762039 -5.00587081909 -1.83901309967 4.28019762039 -5.00382328033 -1.83987855911 4.28019762039 -4.94905042648 -1.76210546494 4.28019762039 -4.94738197327 -1.76326990128 4.28019762039 -4.8633480072 -1.65232777596 4.28019762039 -4.86275911331 -1.65277087688 4.28019762039 -4.80717134476 -1.57872438431 4.28019762039 -4.80912256241 -1.577013731 4.28019762039 -4.73724842072 -1.49609029293 4.28019762039 -4.73189544678 -1.50081503391 4.28019762039 -4.67105484009 -1.44395244122 4.28019762039 -4.6710896492 -1.44391107559 4.28019762039 -4.66750526428 -1.44088876247 4.28019762039 -4.66738843918 -1.44102835655 4.28019762039 -4.60289096832 -1.39136135578 4.28019762039 -4.60353183746 -1.39052200317 4.28019762039 -4.5149564743 -1.32313609123 4.28019762039 -4.51486587524 -1.32325017452 4.28019762039 -4.43444442749 -1.26395785809 4.28019762039 -4.4370932579 -1.25954961777 4.28019762039 -4.31825351715 -1.18714344501 4.28019762039 -4.31516170502 -1.19219982624 4.28019762039 -4.22539138794 -1.14740002155 4.28019762039 -4.22536706924 -1.1474493742 4.28019762039 -4.14552974701 -1.10772049427 4.28019762039 -4.14552497864 -1.10773038864 4.28019762039 -4.05503129959 -1.06257796288 4.28019762039 -4.05618476868 -1.05974304676 4.28019762039 -4.00361537933 -1.03838944435 4.28019762039 -4.00312232971 -1.03958678246 4.28019762039 -3.91175889969 -1.01095318794 4.28019762039 -3.9118874073 -1.0105009079 4.28019762039 -3.75436639786 -0.963734149933 4.28019762039 -3.7542951107 -0.963927805424 4.28019762039 -3.65033149719 -0.935171186924 4.28019762039 -3.6505382061 -0.934110403061 4.28019762039 -3.59834170341 -0.923348844051 4.28019762039 -3.59793424606 -0.925380110741 4.28019762039 -3.4909594059 -0.909021615982 4.28019762039 -3.49121999741 -0.907230854034 4.28019762039 -3.36972880363 -0.889913320541 4.28019762039 -3.3696770668 -0.890171229839 4.28019762039 -3.26142501831 -0.877842485905 4.28019762039 -3.26150679588 -0.875662684441 4.28019762039 -3.103525877 -0.86679983139 4.28019762039 -3.10327124596 -0.871694743633 4.28019762039 -2.99367523193 -0.870046377182 4.28019762039 -2.99367666245 -0.869723260403 4.28019762039 -2.96885085106 -0.86967754364 4.28019762039 -2.96884584427 -0.869723439217 4.28019762039 -2.83697128296 -0.873757898808 4.28019762039 -2.83640122414 -0.869107067585 4.28019762039 -2.61477851868 -0.890800356865 4.28019762039 -2.61528515816 -0.896044671535 4.28019762039 -2.48791003227 -0.915508925915 4.28019762039 -2.48754549026 -0.913865089417 4.28019762039 -2.43184232712 -0.926141440868 4.28019762039 -2.43227052689 -0.92807418108 4.28019762039 -2.32689213753 -0.958355605602 4.28019762039 -2.32595801353 -0.955483615398 4.28019762039 -2.18884444237 -1.00024700165 4.28019762039 -2.18908834457 -1.00125086308 4.28019762039 -2.0877802372 -1.0409642458 4.28019762039 -2.08755946159 -1.04059350491 4.28019762039 -2.01496648788 -1.07349991798 4.28019762039 -2.01516532898 -1.0740032196 4.28019762039 -1.91750121117 -1.1239014864 4.28019762039 -1.91722595692 -1.12350177765 4.28019762039 -1.77625846863 -1.20246696472 4.28019762039 -1.77743268013 -1.20454788208 4.28019762039 -1.67442297935 -1.26465964317 4.28019762039 -1.67279207706 -1.26230502129 4.28019762039 -1.58272147179 -1.32526493073 4.28019762039 -1.58698546886 -1.33128809929 4.28019762039 -1.470079422 -1.42770266533 4.28019762039 -1.46817016602 -1.42548716068 4.28019762039 -1.39286851883 -1.49098169804 4.28019762039 -1.39352154732 -1.49172854424 4.28019762039 -1.28041911125 -1.59623968601 4.28019762039 -1.27561080456 -1.59147751331 4.28019762039 -1.14763367176 -1.7214179039 4.28019762039 -1.14892649651 -1.72277855873 4.28019762039 -1.06128060818 -1.82456839085 4.28019762039 -1.06018030643 -1.82379722595 4.28019762039 -0.974184393883 -1.93449997902 4.28019762039 -0.973088443279 -1.93400645256 4.28019762039 -0.902132511139 -2.04378604889 4.28019762039 -0.904173672199 -2.04520273209 # objtype='BezierSpline'; ncoords=122; nparts=122; closed=True; sep=' '; name='splines-014' 6.98164272308 -0.2287658602 -2.1387591362 6.98164272308 -0.151134431362 -2.36067342758 6.98164272308 -0.121619343758 -2.45009088516 6.98164272308 -0.0765051394701 -2.63410353661 6.98164272308 -0.0635233595967 -2.707498312 6.98164272308 -0.0278902538121 -2.91402959824 6.98164272308 -0.0182820223272 -2.97220420837 6.98164272308 -0.00164606817998 -3.20014190674 6.98164272308 -0.00162461877335 -3.24463963509 6.98164272308 -0.00648269755766 -3.43379163742 6.98164272308 -0.0087293908 -3.5318582058 6.98164272308 -0.0117664569989 -3.56282448769 6.98164272308 -0.0432982854545 -3.78233981133 6.98164272308 -0.0548015125096 -3.8327922821 6.98164272308 -0.0980012491345 -4.02667760849 6.98164272308 -0.118301294744 -4.09510755539 6.98164272308 -0.176588371396 -4.2705616951 6.98164272308 -0.214571490884 -4.34196138382 6.98164272308 -0.257151186466 -4.42857980728 6.98164272308 -0.326105445623 -4.56021642685 6.98164272308 -0.345420718193 -4.58727836609 6.98164272308 -0.453153222799 -4.70980882645 6.98164272308 -0.4850012362 -4.7461066246 6.98164272308 -0.584664344788 -4.85269498825 6.98164272308 -0.637242019176 -4.89751052856 6.98164272308 -0.732617914677 -4.95111894608 6.98164272308 -0.835416793823 -5.01101732254 6.98164272308 -0.875893175602 -5.03411197662 6.98164272308 -1.07277369499 -5.12593984604 6.98164272308 -1.07850205898 -5.12772512436 6.98164272308 -1.1022670269 -5.1328163147 6.98164272308 -1.28988182545 -5.17273139954 6.98164272308 -1.33588969707 -5.18301963806 6.98164272308 -1.49563908577 -5.21334409714 6.98164272308 -1.59136962891 -5.21543836594 6.98164272308 -1.71303582191 -5.20883226395 6.98164272308 -1.88047838211 -5.20043945312 6.98164272308 -1.98956227303 -5.19022369385 6.98164272308 -2.16150021553 -5.16013050079 6.98164272308 -2.26391625404 -5.13464736938 6.98164272308 -2.44430160522 -5.08398723602 6.98164272308 -2.53890442848 -5.05520772934 6.98164272308 -2.72120404243 -4.99030399323 6.98164272308 -2.79854559898 -4.95673465729 6.98164272308 -2.97230505943 -4.87927627563 6.98164272308 -3.04503202438 -4.84756612778 6.98164272308 -3.09532904625 -4.82294559479 6.98164272308 -3.29230308533 -4.7262969017 6.98164272308 -3.50857257843 -4.61021566391 6.98164272308 -3.53327703476 -4.59652996063 6.98164272308 -3.56941223145 -4.57580041885 6.98164272308 -3.7701997757 -4.46192455292 6.98164272308 -3.86810183525 -4.39949226379 6.98164272308 -3.96013760567 -4.33685874939 6.98164272308 -4.14770889282 -4.21184873581 6.98164272308 -4.14932155609 -4.21074962616 6.98164272308 -4.15347576141 -4.20722389221 6.98164272308 -4.33406591415 -4.05600595474 6.98164272308 -4.3973736763 -3.99880313873 6.98164272308 -4.50754261017 -3.88163971901 6.98164272308 -4.57961177826 -3.77088475227 6.98164272308 -4.63754892349 -3.6807718277 6.98164272308 -4.71596670151 -3.54381132126 6.98164272308 -4.74822616577 -3.46737265587 6.98164272308 -4.8063158989 -3.28733038902 6.98164272308 -4.82575035095 -3.22793030739 6.98164272308 -4.88119268417 -3.05498170853 6.98164272308 -4.89130544662 -3.01127743721 6.98164272308 -4.90244865417 -2.84796237946 6.98164272308 -4.90477752686 -2.81376624107 6.98164272308 -4.90528821945 -2.80399107933 6.98164272308 -4.91511726379 -2.62348747253 6.98164272308 -4.91454792023 -2.56195664406 6.98164272308 -4.91234731674 -2.43442296982 6.98164272308 -4.89141368866 -2.32776522636 6.98164272308 -4.86777162552 -2.24258971214 6.98164272308 -4.82493591309 -2.09028863907 6.98164272308 -4.81548404694 -2.05611133575 6.98164272308 -4.75228452682 -1.92525231838 6.98164272308 -4.72959899902 -1.87770080566 6.98164272308 -4.7224445343 -1.86606276035 6.98164272308 -4.61109256744 -1.70538341999 6.98164272308 -4.57546567917 -1.65514743328 6.98164272308 -4.48807621002 -1.53613996506 6.98164272308 -4.42346000671 -1.4729231596 6.98164272308 -4.32130718231 -1.38148438931 6.98164272308 -4.2285861969 -1.30363273621 6.98164272308 -4.13433170319 -1.23442864418 6.98164272308 -4.01913404465 -1.16076672077 6.98164272308 -3.94253396988 -1.11827552319 6.98164272308 -3.80038452148 -1.05066502094 6.98164272308 -3.74437260628 -1.02920651436 6.98164272308 -3.55409240723 -0.966454148293 6.98164272308 -3.47565531731 -0.944225847721 6.98164272308 -3.26203370094 -0.891845881939 6.98164272308 -3.22673869133 -0.885265946388 6.98164272308 -3.04716992378 -0.868099331856 6.98164272308 -2.97920155525 -0.861359596252 6.98164272308 -2.96569085121 -0.860980570316 6.98164272308 -2.73404312134 -0.848941981792 6.98164272308 -2.65697050095 -0.852625310421 6.98164272308 -2.47540020943 -0.868388295174 6.98164272308 -2.29152202606 -0.887034356594 6.98164272308 -2.21426177025 -0.894202411175 6.98164272308 -2.00424385071 -0.934325933456 6.98164272308 -1.95374834538 -0.9432117939 6.98164272308 -1.93597865105 -0.947536230087 6.98164272308 -1.69469916821 -1.00522375107 6.98164272308 -1.59814012051 -1.03045248985 6.98164272308 -1.44398927689 -1.0806863308 6.98164272308 -1.31160485744 -1.13796663284 6.98164272308 -1.20614922047 -1.18073260784 6.98164272308 -1.03848397732 -1.25908732414 6.98164272308 -0.977702617645 -1.29454517365 6.98164272308 -0.758401155472 -1.45029640198 6.98164272308 -0.741937279701 -1.46215283871 6.98164272308 -0.589225530624 -1.59945571423 6.98164272308 -0.533856868744 -1.64908862114 6.98164272308 -0.529415369034 -1.65385377407 6.98164272308 -0.374821603298 -1.85422444344 6.98164272308 -0.355207026005 -1.88449561596 6.98164272308 -0.255212664604 -2.06967639923 6.98164272308 -0.1883443892 -2.24912810326 6.98164272308 -0.188965260983 -2.24938154221 6.98164272308 -0.135982424021 -2.40524816513 6.98164272308 -0.134603559971 -2.40487289429 6.98164272308 -0.0954941734672 -2.54107260704 6.98164272308 -0.0960334762931 -2.54145884514 6.98164272308 -0.0688227415085 -2.67054986954 6.98164272308 -0.069936864078 -2.67078733444 6.98164272308 -0.0454891957343 -2.81072592735 6.98164272308 -0.0453366301954 -2.81070137024 6.98164272308 -0.0229819957167 -2.94309926033 6.98164272308 -0.0217570699751 -2.94295907021 6.98164272308 -0.00481247669086 -3.08556103706 6.98164272308 -0.005832598079 -3.08602142334 6.98164272308 -0.000830946722999 -3.22236132622 6.98164272308 -0.00134436134249 -3.2223944664 6.98164272308 -0.00281633646227 -3.33923125267 6.98164272308 -0.00418474245816 -3.3392124176 6.98164272308 -0.00767400069162 -3.48282337189 6.98164272308 -0.00577406352386 -3.48293566704 6.98164272308 -0.00966682005674 -3.54737639427 6.98164272308 -0.00990118458867 -3.54738330841 6.98164272308 -0.0250610206276 -3.67288064957 6.98164272308 -0.0230907443911 -3.67340612411 6.98164272308 -0.0480134934187 -3.8077583313 6.98164272308 -0.0491122379899 -3.80755209923 6.98164272308 -0.0766406729817 -3.9296810627 6.98164272308 -0.0730778351426 -3.93059706688 6.98164272308 -0.106957018375 -4.06120252609 6.98164272308 -0.107600562274 -4.06106567383 6.98164272308 -0.146018385887 -4.18328285217 6.98164272308 -0.140308767557 -4.1858921051 6.98164272308 -0.192458361387 -4.30759906769 6.98164272308 -0.196156159043 -4.3059668541 6.98164272308 -0.236549034715 -4.38491868973 6.98164272308 -0.235309749842 -4.38555049896 6.98164272308 -0.290779054165 -4.49482917786 6.98164272308 -0.28728428483 -4.49706888199 6.98164272308 -0.334791153669 -4.57434511185 6.98164272308 -0.335103482008 -4.57427072525 6.98164272308 -0.396050095558 -4.65111112595 6.98164272308 -0.399318486452 -4.64851570129 6.98164272308 -0.469086557627 -4.72794961929 6.98164272308 -0.468794137239 -4.72821426392 6.98164272308 -0.533977329731 -4.80017566681 6.98164272308 -0.531984746456 -4.80238294601 6.98164272308 -0.60960483551 -4.87651443481 6.98164272308 -0.609041512012 -4.87784385681 6.98164272308 -0.681902468204 -4.92865610123 6.98164272308 -0.685140788555 -4.92394638062 6.98164272308 -0.784246623516 -4.98066759109 6.98164272308 -0.783882379532 -4.98130226135 6.98164272308 -0.855602145195 -5.02265644073 6.98164272308 -0.855215668678 -5.02341365814 6.98164272308 -0.972285568714 -5.08398389816 6.98164272308 -0.971702694893 -5.08682346344 6.98164272308 -1.07556521893 -5.12702035904 6.98164272308 -1.07560324669 -5.12696456909 6.98164272308 -1.090244174 -5.13080596924 6.98164272308 -1.0903826952 -5.13027906418 6.98164272308 -1.19606029987 -5.15284013748 6.98164272308 -1.19618046284 -5.15228796005 6.98164272308 -1.3129118681 -5.17775583267 6.98164272308 -1.31280851364 -5.17824935913 6.98164272308 -1.41549777985 -5.19947195053 6.98164272308 -1.4150608778 -5.2048740387 6.98164272308 -1.54309010506 -5.21833181381 6.98164272308 -1.54353380203 -5.21621274948 6.98164272308 -1.65224027634 -5.21445322037 6.98164272308 -1.6521961689 -5.21200847626 6.98164272308 -1.79674804211 -5.20446157455 6.98164272308 -1.79688715935 -5.20644569397 6.98164272308 -1.93510532379 -5.19651412964 6.98164272308 -1.93531107903 -5.19749975204 6.98164272308 -2.07599449158 -5.17863130569 6.98164272308 -2.07616901398 -5.17819070816 6.98164272308 -2.21309375763 -5.14921092987 6.98164272308 -2.21291041374 -5.14815235138 6.98164272308 -2.35446763039 -5.11067199707 6.98164272308 -2.3543920517 -5.11028528214 6.98164272308 -2.49175238609 -5.0701084137 6.98164272308 -2.49196481705 -5.07069396973 6.98164272308 -2.63076233864 -5.02490186691 6.98164272308 -2.63125181198 -5.02579164505 6.98164272308 -2.76039671898 -4.97484207153 6.98164272308 -2.75995826721 -4.97370910645 6.98164272308 -2.88561367989 -4.91843366623 6.98164272308 -2.88526844978 -4.91764974594 6.98164272308 -3.00860309601 -4.86327266693 6.98164272308 -3.00903534889 -4.86421394348 6.98164272308 -3.07043933868 -4.83581542969 6.98164272308 -3.07018637657 -4.83526754379 6.98164272308 -3.19383883476 -4.77466726303 6.98164272308 -3.19472932816 -4.77640008926 6.98164272308 -3.40145969391 -4.67024612427 6.98164272308 -3.40082812309 -4.66897201538 6.98164272308 -3.52096962929 -4.60345506668 6.98164272308 -3.52097654343 -4.60346460342 6.98164272308 -3.55142092705 -4.58630084991 6.98164272308 -3.55131936073 -4.58612060547 6.98164272308 -3.66966533661 -4.51861572266 6.98164272308 -3.67134642601 -4.52142190933 6.98164272308 -3.81992578506 -4.43199586868 6.98164272308 -3.81962776184 -4.43143224716 6.98164272308 -3.91457700729 -4.36886930466 6.98164272308 -3.91396927834 -4.36795186996 6.98164272308 -4.05361843109 -4.27390146255 6.98164272308 -4.05424976349 -4.27483844757 6.98164272308 -4.14851808548 -4.2113032341 6.98164272308 -4.14854621887 -4.21133995056 6.98164272308 -4.15148591995 -4.2091012001 6.98164272308 -4.15139293671 -4.20897960663 6.98164272308 -4.24351882935 -4.131316185 6.98164272308 -4.24522686005 -4.13328886032 6.98164272308 -4.36624717712 -4.02801084518 6.98164272308 -4.36693477631 -4.02864360809 6.98164272308 -4.45474767685 -3.94255709648 6.98164272308 -4.45807170868 -3.94463014603 6.98164272308 -4.54818964005 -3.82988452911 6.98164272308 -4.54372930527 -3.8263604641 6.98164272308 -4.60870361328 -3.725908041 6.98164272308 -4.60975694656 -3.72654271126 6.98164272308 -4.67849111557 -3.61334395409 6.98164272308 -4.68102121353 -3.61440205574 6.98164272308 -4.7343378067 -3.50670146942 6.98164272308 -4.73379230499 -3.50622200966 6.98164272308 -4.78113794327 -3.3787882328 6.98164272308 -4.77708673477 -3.37729167938 6.98164272308 -4.81597232819 -3.25761055946 6.98164272308 -4.81612205505 -3.25765919685 6.98164272308 -4.85372972488 -3.14153957367 6.98164272308 -4.85709619522 -3.14245462418 6.98164272308 -4.88714456558 -3.03337621689 6.98164272308 -4.88801383972 -3.03339219093 6.98164272308 -4.90331697464 -2.93057847023 6.98164272308 -4.89688205719 -2.92962026596 6.98164272308 -4.90361404419 -2.83086442947 6.98164272308 -4.90374803543 -2.83087253571 6.98164272308 -4.90507125854 -2.80888104439 6.98164272308 -4.90502738953 -2.80887842178 6.98164272308 -4.91010332108 -2.71373391151 6.98164272308 -4.91307830811 -2.713804245 6.98164272308 -4.9158115387 -2.59274411201 6.98164272308 -4.914955616 -2.59272050858 6.98164272308 -4.91370248795 -2.49818634987 6.98164272308 -4.91903877258 -2.49759745598 6.98164272308 -4.906645298 -2.38058948517 6.98164272308 -4.90391492844 -2.38061285019 6.98164272308 -4.88124704361 -2.28478622437 6.98164272308 -4.87966537476 -2.28515720367 6.98164272308 -4.84648418427 -2.16640281677 6.98164272308 -4.84618759155 -2.16648554802 6.98164272308 -4.82017278671 -2.07321047783 6.98164272308 -4.82170248032 -2.07263851166 6.98164272308 -4.79000043869 -1.98838067055 6.98164272308 -4.78372764587 -1.99075698853 6.98164272308 -4.74088478088 -1.90150380135 6.98164272308 -4.74216842651 -1.90080940723 6.98164272308 -4.72633981705 -1.87170875072 6.98164272308 -4.72617864609 -1.87177944183 6.98164272308 -4.66901159286 -1.784258008 6.98164272308 -4.66720294952 -1.78541862965 6.98164272308 -4.59341573715 -1.68016958237 6.98164272308 -4.59348535538 -1.68011653423 6.98164272308 -4.53226566315 -1.59528684616 6.98164272308 -4.53630828857 -1.59170520306 6.98164272308 -4.45854616165 -1.50212013721 6.98164272308 -4.45645284653 -1.50380003452 6.98164272308 -4.37342214584 -1.42609417439 6.98164272308 -4.37309455872 -1.42638373375 6.98164272308 -4.27557468414 -1.34183442593 6.98164272308 -4.27616405487 -1.34100902081 6.98164272308 -4.18263483047 -1.26753401756 6.98164272308 -4.18252372742 -1.2674779892 6.98164272308 -4.07797813416 -1.19578206539 6.98164272308 -4.0778260231 -1.195764184 6.98164272308 -3.98153448105 -1.13834655285 6.98164272308 -3.98146009445 -1.13830435276 6.98164272308 -3.87258434296 -1.08228385448 6.98164272308 -3.87266969681 -1.08164596558 6.98164272308 -3.77283978462 -1.03885960579 6.98164272308 -3.77261662483 -1.03926765919 6.98164272308 -3.6500275135 -0.995598614216 6.98164272308 -3.64985489845 -0.995799481869 6.98164272308 -3.51512718201 -0.954513669014 6.98164272308 -3.51505970955 -0.954636693001 6.98164272308 -3.3693459034 -0.916138350964 6.98164272308 -3.36949515343 -0.915018379688 6.98164272308 -3.2444922924 -0.888063371181 6.98164272308 -3.24449753761 -0.887765109539 6.98164272308 -3.13751339912 -0.872709453106 6.98164272308 -3.13693881035 -0.876840949059 6.98164272308 -3.01318001747 -0.864789426327 6.98164272308 -3.01326227188 -0.863523364067 6.98164272308 -2.97246146202 -0.860931396484 6.98164272308 -2.97244286537 -0.861250698566 6.98164272308 -2.84981155396 -0.85634469986 6.98164272308 -2.84987902641 -0.849183440208 6.98164272308 -2.69551086426 -0.848861694336 6.98164272308 -2.69545650482 -0.850036084652 6.98164272308 -2.56606650352 -0.858741044998 6.98164272308 -2.56612372398 -0.859850823879 6.98164272308 -2.38339853287 -0.877046108246 6.98164272308 -2.38349938393 -0.878104329109 6.98164272308 -2.25290799141 -0.890783369541 6.98164272308 -2.25263023376 -0.888770282269 6.98164272308 -2.10853171349 -0.909171462059 6.98164272308 -2.10939359665 -0.915030896664 6.98164272308 -1.97902989388 -0.938952744007 6.98164272308 -1.97882652283 -0.937959432602 6.98164272308 -1.9448029995 -0.945085287094 6.98164272308 -1.94486784935 -0.945391952991 6.98164272308 -1.8153976202 -0.976623296738 6.98164272308 -1.81502461433 -0.975123763084 6.98164272308 -1.6462931633 -1.01733279228 6.98164272308 -1.64600217342 -1.01641476154 6.98164272308 -1.52038657665 -1.05325722694 6.98164272308 -1.51972651482 -1.05203235149 6.98164272308 -1.37660646439 -1.10617959499 6.98164272308 -1.37811887264 -1.11009442806 6.98164272308 -1.25913095474 -1.15995550156 6.98164272308 -1.25828683376 -1.15799629688 6.98164272308 -1.1213568449 -1.21770906448 6.98164272308 -1.12036466599 -1.2161847353 6.98164272308 -1.00735116005 -1.27539980412 6.98164272308 -1.00724065304 -1.27549433708 6.98164272308 -0.864792764187 -1.36736750603 6.98164272308 -0.867794811726 -1.3720613718 6.98164272308 -0.750149846077 -1.45619750023 6.98164272308 -0.749825060368 -1.45579743385 6.98164272308 -0.662097990513 -1.52648091316 6.98164272308 -0.665632486343 -1.5308611393 6.98164272308 -0.561559677124 -1.62429273129 6.98164272308 -0.560373902321 -1.62308192253 6.98164272308 -0.531533837318 -1.65136694908 6.98164272308 -0.531520545483 -1.65137314796 6.98164272308 -0.447628319263 -1.75022852421 6.98164272308 -0.447874873877 -1.75103485584 6.98164272308 -0.364409476519 -1.86893188953 6.98164272308 -0.364395260811 -1.86899316311 6.98164272308 -0.301598012447 -1.97494530678 6.98164272308 -0.299021840096 -1.97424530983 6.98164272308 -0.239814251661 -2.10321927071 6.98164272308 -0.241484031081 -2.1040327549 # objtype='BezierSpline'; ncoords=115; nparts=115; closed=True; sep=' '; name='splines-015' 9.68308830261 -1.0374866724 -0.787005543709 9.68308830261 -1.08449554443 -0.767647564411 9.68308830261 -1.15440261364 -0.742334008217 9.68308830261 -1.34409821033 -0.67636346817 9.68308830261 -1.52251887321 -0.634214818478 9.68308830261 -1.61026549339 -0.610849261284 9.68308830261 -1.70311367512 -0.59522074461 9.68308830261 -1.8816832304 -0.560127317905 9.68308830261 -2.06220412254 -0.534662604332 9.68308830261 -2.14983820915 -0.517822146416 9.68308830261 -2.24759221077 -0.528508484364 9.68308830261 -2.40368366241 -0.531552672386 9.68308830261 -2.59508490562 -0.546517491341 9.68308830261 -2.66115546227 -0.551284193993 9.68308830261 -2.71426963806 -0.559043765068 9.68308830261 -2.91539502144 -0.583397746086 9.68308830261 -3.15756988525 -0.651241183281 9.68308830261 -3.16101884842 -0.652165353298 9.68308830261 -3.16487836838 -0.653732836246 9.68308830261 -3.43581938744 -0.766888737679 9.68308830261 -3.6149392128 -0.862535893917 9.68308830261 -3.69832921028 -0.908874750137 9.68308830261 -3.80767464638 -0.995864212513 9.68308830261 -3.93127274513 -1.09289813042 9.68308830261 -4.00176763535 -1.16811549664 9.68308830261 -4.13395166397 -1.30382156372 9.68308830261 -4.23536586761 -1.43799221516 9.68308830261 -4.3089389801 -1.5328630209 9.68308830261 -4.42714548111 -1.76339447498 9.68308830261 -4.44380283356 -1.79613351822 9.68308830261 -4.44857692719 -1.81148672104 9.68308830261 -4.51827001572 -2.04839777946 9.68308830261 -4.55263137817 -2.20348787308 9.68308830261 -4.5731139183 -2.30377197266 9.68308830261 -4.57891941071 -2.41343212128 9.68308830261 -4.58694171906 -2.5555024147 9.68308830261 -4.5944981575 -2.76281476021 9.68308830261 -4.59554862976 -2.8047015667 9.68308830261 -4.59328985214 -2.83348894119 9.68308830261 -4.5778799057 -3.05732417107 9.68308830261 -4.5557346344 -3.25230884552 9.68308830261 -4.54859209061 -3.3158826828 9.68308830261 -4.51440668106 -3.47461700439 9.68308830261 -4.49195861816 -3.57829165459 9.68308830261 -4.47739315033 -3.6232278347 9.68308830261 -4.40274047852 -3.81963777542 9.68308830261 -4.33832788467 -3.96058988571 9.68308830261 -4.29746389389 -4.03907871246 9.68308830261 -4.18382358551 -4.18446350098 9.68308830261 -4.14987468719 -4.22810983658 9.68308830261 -4.13938808441 -4.2406744957 9.68308830261 -4.0447177887 -4.34332084656 9.68308830261 -3.99004936218 -4.4029955864 9.68308830261 -3.98028159142 -4.41179227829 9.68308830261 -3.73747944832 -4.59565067291 9.68308830261 -3.70087122917 -4.62055397034 9.68308830261 -3.46484494209 -4.75528240204 9.68308830261 -3.40843391418 -4.78498697281 9.68308830261 -3.1894903183 -4.88891839981 9.68308830261 -3.11511468887 -4.9187297821 9.68308830261 -2.92747545242 -4.99259328842 9.68308830261 -2.77164626122 -5.04359149933 9.68308830261 -2.6485748291 -5.08389234543 9.68308830261 -2.38607954979 -5.16078901291 9.68308830261 -2.36603093147 -5.16637134552 9.68308830261 -2.30704307556 -5.17974090576 9.68308830261 -2.06904006004 -5.23383426666 9.68308830261 -1.98541164398 -5.248067379 9.68308830261 -1.76761198044 -5.28449106216 9.68308830261 -1.54554331303 -5.31283998489 9.68308830261 -1.48485195637 -5.32164859772 9.68308830261 -1.37763357162 -5.32376241684 9.68308830261 -1.25368821621 -5.32497310638 9.68308830261 -1.18396055698 -5.32547807693 9.68308830261 -1.02155542374 -5.32277917862 9.68308830261 -0.822959423065 -5.28393268585 9.68308830261 -0.792517483234 -5.27795219421 9.68308830261 -0.770634293556 -5.27271986008 9.68308830261 -0.591623187065 -5.23233699799 9.68308830261 -0.528887093067 -5.20075511932 9.68308830261 -0.403840273619 -5.14566993713 9.68308830261 -0.283628731966 -5.08149242401 9.68308830261 -0.176775664091 -5.01609134674 9.68308830261 -0.0126168392599 -4.85296010971 9.68308830261 0.030701899901 -4.81075716019 9.68308830261 0.11024069041 -4.70102024078 9.68308830261 0.167804867029 -4.62438631058 9.68308830261 0.188673123717 -4.59038591385 9.68308830261 0.284262984991 -4.4322810173 9.68308830261 0.33516895771 -4.3016667366 9.68308830261 0.365212738514 -4.22664880753 9.68308830261 0.415206581354 -4.07440805435 9.68308830261 0.433863073587 -4.01765346527 9.68308830261 0.440468162298 -3.98919510841 9.68308830261 0.492300122976 -3.73673248291 9.68308830261 0.506884336472 -3.59714913368 9.68308830261 0.521535038948 -3.44178652763 9.68308830261 0.51515686512 -3.19508266449 9.68308830261 0.512132763863 -3.16074323654 9.68308830261 0.505565285683 -3.08289146423 9.68308830261 0.489304214716 -2.88799214363 9.68308830261 0.480916708708 -2.83004403114 9.68308830261 0.4274559021 -2.59374809265 9.68308830261 0.421561121941 -2.56671953201 9.68308830261 0.33052957058 -2.23292064667 9.68308830261 0.307354450226 -2.16774892807 9.68308830261 0.183223679662 -1.89720833302 9.68308830261 0.126719027758 -1.80120015144 9.68308830261 -0.00539919547737 -1.59919369221 9.68308830261 -0.105184674263 -1.47904455662 9.68308830261 -0.253092885017 -1.31496012211 9.68308830261 -0.388045251369 -1.18575143814 9.68308830261 -0.510975122452 -1.08024156094 9.68308830261 -0.690479099751 -0.953990399837 9.68308830261 -0.788776278496 -0.896201431751 9.68308830261 -1.06087625027 -0.777056634426 9.68308830261 -1.06079304218 -0.77681428194 9.68308830261 -1.11915934086 -0.754241645336 9.68308830261 -1.11936998367 -0.75476783514 9.68308830261 -1.24903666973 -0.708746433258 9.68308830261 -1.2478094101 -0.704399585724 9.68308830261 -1.43199312687 -0.650771379471 9.68308830261 -1.43362414837 -0.656545579433 9.68308830261 -1.56654846668 -0.623154342175 9.68308830261 -1.56594264507 -0.620458781719 9.68308830261 -1.65622353554 -0.600885212421 9.68308830261 -1.65680480003 -0.603666961193 9.68308830261 -1.79262125492 -0.578895509243 9.68308830261 -1.79199051857 -0.575255572796 9.68308830261 -1.97153508663 -0.544972240925 9.68308830261 -1.97231578827 -0.549629867077 9.68308830261 -2.10620331764 -0.527336299419 9.68308830261 -2.10575246811 -0.519607901573 9.68308830261 -2.19841909409 -0.515854358673 9.68308830261 -2.19857430458 -0.525357544422 9.68308830261 -2.32541418076 -0.53351098299 9.68308830261 -2.32574915886 -0.527749300003 9.68308830261 -2.49952125549 -0.536229729652 9.68308830261 -2.49936270714 -0.539322555065 9.68308830261 -2.62811279297 -0.549000024796 9.68308830261 -2.62825131416 -0.547698557377 9.68308830261 -2.68781876564 -0.554189741611 9.68308830261 -2.68766880035 -0.555490732193 9.68308830261 -2.81466770172 -0.572453916073 9.68308830261 -2.81634259224 -0.563646435738 9.68308830261 -3.03835725784 -0.607916772366 9.68308830261 -3.03629398346 -0.618006825447 9.68308830261 -3.15929174423 -0.651713013649 9.68308830261 -3.15932965279 -0.651598393917 9.68308830261 -3.16298961639 -0.652826786041 9.68308830261 -3.1629524231 -0.652939617634 9.68308830261 -3.30062389374 -0.709643125534 9.68308830261 -3.3033323288 -0.704023301601 9.68308830261 -3.52744245529 -0.810364127159 9.68308830261 -3.52578568459 -0.813966274261 9.68308830261 -3.65682506561 -0.885354816914 9.68308830261 -3.65881752968 -0.882441759109 9.68308830261 -3.75619959831 -0.947589576244 9.68308830261 -3.75286221504 -0.95254611969 9.68308830261 -3.86931681633 -1.04457986355 9.68308830261 -3.87350940704 -1.0399762392 9.68308830261 -3.96916770935 -1.12761700153 9.68308830261 -3.96616148949 -1.13084959984 9.68308830261 -4.06720066071 -1.23659837246 9.68308830261 -4.07234764099 -1.23211288452 9.68308830261 -4.18864297867 -1.3674839735 9.68308830261 -4.18424510956 -1.37122344971 9.68308830261 -4.27185726166 -1.48565363884 9.68308830261 -4.27685117722 -1.48243761063 9.68308830261 -4.37818145752 -1.64167654514 9.68308830261 -4.36822366714 -1.64803612232 9.68308830261 -4.43549966812 -1.77975082397 9.68308830261 -4.43691158295 -1.77917969227 9.68308830261 -4.44681930542 -1.8035544157 9.68308830261 -4.44624900818 -1.80379223824 9.68308830261 -4.48433160782 -1.92966747284 9.68308830261 -4.4874920845 -1.9288944006 9.68308830261 -4.53806781769 -2.12526869774 9.68308830261 -4.53609418869 -2.1258058548 9.68308830261 -4.56328725815 -2.25354170799 9.68308830261 -4.56664037704 -2.25314807892 9.68308830261 -4.58005905151 -2.35808515549 9.68308830261 -4.57592010498 -2.35860729218 9.68308830261 -4.58280563354 -2.48447418213 9.68308830261 -4.58364057541 -2.48443436623 9.68308830261 -4.59175491333 -2.65911054611 9.68308830261 -4.59130859375 -2.65914058685 9.68308830261 -4.5951423645 -2.78375458717 9.68308830261 -4.59610557556 -2.7837870121 9.68308830261 -4.59516477585 -2.8191151619 9.68308830261 -4.59435033798 -2.81909012794 9.68308830261 -4.58504962921 -2.94536709785 9.68308830261 -4.58806228638 -2.94563245773 9.68308830261 -4.5689740181 -3.15501403809 9.68308830261 -4.56674814224 -3.15480995178 9.68308830261 -4.5521440506 -3.28409361839 9.68308830261 -4.55374479294 -3.2843542099 9.68308830261 -4.53551340103 -3.39590597153 9.68308830261 -4.53154325485 -3.39525938034 9.68308830261 -4.50321149826 -3.52646064758 9.68308830261 -4.5057477951 -3.52714586258 9.68308830261 -4.48581790924 -3.60106778145 9.68308830261 -4.48523044586 -3.60095477104 9.68308830261 -4.4425330162 -3.72230052948 9.68308830261 -4.44323682785 -3.72275805473 9.68308830261 -4.3728723526 -3.89109110832 9.68308830261 -4.37232255936 -3.89098715782 9.68308830261 -4.31891679764 -4.00033283234 9.68308830261 -4.32130384445 -4.00202703476 9.68308830261 -4.24775028229 -4.11634349823 9.68308830261 -4.24055719376 -4.11170339584 9.68308830261 -4.16682291031 -4.2062664032 9.68308830261 -4.16721963882 -4.20658540726 9.68308830261 -4.14474105835 -4.23448038101 9.68308830261 -4.14478349686 -4.23452568054 9.68308830261 -4.09335184097 -4.29313755035 9.68308830261 -4.09196662903 -4.29191827774 9.68308830261 -4.01733350754 -4.37311220169 9.68308830261 -4.01875066757 -4.37453699112 9.68308830261 -3.98538756371 -4.40761804581 9.68308830261 -3.98534345627 -4.40760946274 9.68308830261 -3.86300301552 -4.50870990753 9.68308830261 -3.86113452911 -4.5068602562 9.68308830261 -3.71950292587 -4.60855865479 9.68308830261 -3.71963620186 -4.60884094238 9.68308830261 -3.58568763733 -4.69245147705 9.68308830261 -3.58396911621 -4.68994379044 9.68308830261 -3.43690013885 -4.77060985565 9.68308830261 -3.43693518639 -4.77072572708 9.68308830261 -3.30008673668 -4.83920001984 9.68308830261 -3.30046653748 -4.84039306641 9.68308830261 -3.15279984474 -4.904961586 9.68308830261 -3.15234827995 -4.90393924713 9.68308830261 -3.02141046524 -4.95595169067 9.68308830261 -3.02229809761 -4.95844697952 9.68308830261 -2.85037636757 -5.02035713196 9.68308830261 -2.84955859184 -5.01808595657 9.68308830261 -2.71010875702 -5.06373643875 9.68308830261 -2.71041250229 -5.06471538544 9.68308830261 -2.51796483994 -5.12439680099 9.68308830261 -2.51757907867 -5.12322235107 9.68308830261 -2.37607431412 -5.16364717484 9.68308830261 -2.37611722946 -5.16382551193 9.68308830261 -2.33671712875 -5.17376995087 9.68308830261 -2.33653497696 -5.17304706573 9.68308830261 -2.18803358078 -5.20675182343 9.68308830261 -2.18869400024 -5.21007299423 9.68308830261 -2.02745270729 -5.24209260941 9.68308830261 -2.0272359848 -5.24101114273 9.68308830261 -1.87653827667 -5.26643610001 9.68308830261 -1.8768235445 -5.26839447021 9.68308830261 -1.65689373016 -5.30080986023 9.68308830261 -1.65644788742 -5.29771375656 9.68308830261 -1.51516211033 -5.3169836998 9.68308830261 -1.51535367966 -5.31914424896 9.68308830261 -1.43151557446 -5.32602787018 9.68308830261 -1.4312466383 -5.32297229767 9.68308830261 -1.31566548347 -5.32467603683 9.68308830261 -1.31566154957 -5.32444620132 9.68308830261 -1.21882474422 -5.3252696991 9.68308830261 -1.21882247925 -5.32564163208 9.68308830261 -1.1027534008 -5.32509756088 9.68308830261 -1.10200834274 -5.33124923706 9.68308830261 -0.921323478222 -5.31222677231 9.68308830261 -0.922249436378 -5.30339670181 9.68308830261 -0.807737231255 -5.28094863892 9.68308830261 -0.807671308517 -5.28125095367 9.68308830261 -0.781527161598 -5.27555990219 9.68308830261 -0.781592190266 -5.27526569366 9.68308830261 -0.681261837482 -5.25195550919 9.68308830261 -0.677353918552 -5.26306152344 9.68308830261 -0.558810353279 -5.22057723999 9.68308830261 -0.560640275478 -5.2157292366 9.68308830261 -0.467112988234 -5.17162370682 9.68308830261 -0.465236902237 -5.17552947998 9.68308830261 -0.342610776424 -5.11589193344 9.68308830261 -0.342738509178 -5.1153216362 9.68308830261 -0.229286521673 -5.05039167404 9.68308830261 -0.225704789162 -5.05451822281 9.68308830261 -0.0863881334662 -4.945104599 9.68308830261 -0.0950982421637 -4.9341173172 9.68308830261 0.00893747992814 -4.83175182343 9.68308830261 0.0109990388155 -4.83354997635 9.68308830261 0.0748558118939 -4.75967884064 9.68308830261 0.0700062960386 -4.75554561615 9.68308830261 0.13869394362 -4.66246080399 9.68308830261 0.140879705548 -4.66396665573 9.68308830261 0.179011896253 -4.6079120636 9.68308830261 0.178295940161 -4.60742092133 9.68308830261 0.236731797457 -4.5114941597 9.68308830261 0.243592619896 -4.51484298706 9.68308830261 0.315121769905 -4.36963701248 9.68308830261 0.309413045645 -4.36685419083 9.68308830261 0.350016206503 -4.26408863068 9.68308830261 0.351398676634 -4.26459741592 9.68308830261 0.392604678869 -4.15140008926 9.68308830261 0.390198141336 -4.15052461624 9.68308830261 0.424530535936 -4.04602956772 9.68308830261 0.425822198391 -4.04639101028 9.68308830261 0.437795162201 -4.00360059738 9.68308830261 0.437348037958 -4.00346422195 9.68308830261 0.467993408442 -3.86331558228 9.68308830261 0.472646474838 -3.86393141747 9.68308830261 0.503002285957 -3.66746759415 9.68308830261 0.499944299459 -3.66697573662 9.68308830261 0.514601171017 -3.51950669289 9.68308830261 0.518880665302 -3.51962709427 9.68308830261 0.525732815266 -3.31868648529 9.68308830261 0.522163748741 -3.31821727753 9.68308830261 0.514178097248 -3.17788267136 9.68308830261 0.51361322403 -3.17791557312 9.68308830261 0.508777439594 -3.12182354927 9.68308830261 0.508831143379 -3.1218187809 9.68308830261 0.49738997221 -2.98544549942 9.68308830261 0.500373482704 -2.98510694504 9.68308830261 0.485990285873 -2.85891771317 9.68308830261 0.486243724823 -2.85880827904 9.68308830261 0.458875358105 -2.71102762222 9.68308830261 0.453727066517 -2.71199798584 9.68308830261 0.424456059933 -2.58024549484 9.68308830261 0.424854457378 -2.58014893532 9.68308830261 0.380371898413 -2.39875912666 9.68308830261 0.382268160582 -2.39786815643 9.68308830261 0.320186078548 -2.19994449615 9.68308830261 0.320359587669 -2.19975876808 9.68308830261 0.251389294863 -2.03000020981 9.68308830261 0.252000629902 -2.02897548676 9.68308830261 0.1574832201 -1.84789311886 9.68308830261 0.156089320779 -1.84851026535 9.68308830261 0.0630822256207 -1.69869315624 9.68308830261 0.066184155643 -1.69611692429 9.68308830261 -0.0517174489796 -1.53647911549 9.68308830261 -0.0540955029428 -1.53808379173 9.68308830261 -0.177446514368 -1.39553797245 9.68308830261 -0.176224693656 -1.39417457581 9.68308830261 -0.318104475737 -1.24796414375 9.68308830261 -0.318863540888 -1.24847459793 9.68308830261 -0.448031365871 -1.13136541843 9.68308830261 -0.447115659714 -1.12991833687 9.68308830261 -0.597483336926 -1.01294612885 9.68308830261 -0.598307013512 -1.01335859299 9.68308830261 -0.738370239735 -0.923143625259 9.68308830261 -0.738100409508 -0.922108530998 9.68308830261 -0.909493327141 -0.834487080574 9.68308830261 -0.912518143654 -0.840161442757 # objtype='BezierSpline'; ncoords=108; nparts=108; closed=True; sep=' '; name='splines-016' 12.3845338821 -0.619536399841 -0.455260872841 12.3845338821 -0.718705832958 -0.399332821369 12.3845338821 -0.923619687557 -0.299554824829 12.3845338821 -1.13880336285 -0.236164107919 12.3845338821 -1.26282668114 -0.197647944093 12.3845338821 -1.63742911816 -0.129191443324 12.3845338821 -1.64547646046 -0.129275456071 12.3845338821 -1.66468334198 -0.130531206727 12.3845338821 -2.0606739521 -0.160836204886 12.3845338821 -2.15621948242 -0.171039178967 12.3845338821 -2.41442370415 -0.24355751276 12.3845338821 -2.49464726448 -0.269808411598 12.3845338821 -2.74024152756 -0.380189478397 12.3845338821 -2.83573818207 -0.422597825527 12.3845338821 -2.87899804115 -0.44660872221 12.3845338821 -3.18384838104 -0.628578662872 12.3845338821 -3.39011001587 -0.797940969467 12.3845338821 -3.50415968895 -0.902994811535 12.3845338821 -3.68212842941 -1.10620963573 12.3845338821 -3.7787835598 -1.21410834789 12.3845338821 -3.80444264412 -1.24655246735 12.3845338821 -3.87369918823 -1.34183418751 12.3845338821 -4.00968360901 -1.52659583092 12.3845338821 -4.06990766525 -1.6387475729 12.3845338821 -4.18243932724 -1.85183429718 12.3845338821 -4.29220104218 -2.15501356125 12.3845338821 -4.31181240082 -2.21026802063 12.3845338821 -4.33490276337 -2.3326587677 12.3845338821 -4.38744449615 -2.59729123116 12.3845338821 -4.39706134796 -2.70103216171 12.3845338821 -4.41555118561 -2.91576623917 12.3845338821 -4.40418243408 -3.18197107315 12.3845338821 -4.40055370331 -3.2417242527 12.3845338821 -4.38255977631 -3.35774350166 12.3845338821 -4.34985303879 -3.5799343586 12.3845338821 -4.32114982605 -3.68380665779 12.3845338821 -4.25256490707 -3.90031671524 12.3845338821 -4.17917394638 -4.07809352875 12.3845338821 -4.14483737946 -4.15687227249 12.3845338821 -4.00954580307 -4.36849546432 12.3845338821 -3.99979710579 -4.38281440735 12.3845338821 -3.95923852921 -4.43644762039 12.3845338821 -3.83929443359 -4.596783638 12.3845338821 -3.80332708359 -4.63108253479 12.3845338821 -3.58253407478 -4.83609199524 12.3845338821 -3.54974555969 -4.86652040482 12.3845338821 -3.54015564919 -4.87427759171 12.3845338821 -3.32724022865 -5.01769590378 12.3845338821 -3.21101951599 -5.09324550629 12.3845338821 -3.11076617241 -5.15075922012 12.3845338821 -2.90185141563 -5.25606250763 12.3845338821 -2.88250780106 -5.26576423645 12.3845338821 -2.87055444717 -5.27102947235 12.3845338821 -2.56382894516 -5.38186216354 12.3845338821 -2.43936610222 -5.41700696945 12.3845338821 -2.23810195923 -5.46292209625 12.3845338821 -2.01165509224 -5.50022983551 12.3845338821 -1.91495978832 -5.51343107224 12.3845338821 -1.63767433167 -5.53413534164 12.3845338821 -1.5872426033 -5.53789949417 12.3845338821 -1.57376468182 -5.53860616684 12.3845338821 -1.33063995838 -5.54597425461 12.3845338821 -1.23647809029 -5.54694461823 12.3845338821 -1.07235252857 -5.54813432693 12.3845338821 -0.897022247314 -5.53829240799 12.3845338821 -0.811692297459 -5.53137922287 12.3845338821 -0.561632037163 -5.50989246368 12.3845338821 -0.553202748299 -5.50864934921 12.3845338821 -0.508728384972 -5.49688148499 12.3845338821 -0.332908093929 -5.45085287094 12.3845338821 -0.293354362249 -5.44070053101 12.3845338821 -0.114831134677 -5.37684106827 12.3845338821 -0.0360804982483 -5.33286094666 12.3845338821 0.100474819541 -5.2519159317 12.3845338821 0.211047112942 -5.17165136337 12.3845338821 0.296186745167 -5.09358167648 12.3845338821 0.395239889622 -4.99584388733 12.3845338821 0.50602555275 -4.85177373886 12.3845338821 0.6180113554 -4.69038295746 12.3845338821 0.693932294846 -4.55320930481 12.3845338821 0.785055816174 -4.36986732483 12.3845338821 0.823818087578 -4.25205278397 12.3845338821 0.874701321125 -4.04348039627 12.3845338821 0.890525579453 -3.92760920525 12.3845338821 0.917734146118 -3.72353339195 12.3845338821 0.912889003754 -3.4369456768 12.3845338821 0.910990834236 -3.37235879898 12.3845338821 0.902882933617 -3.27915072441 12.3845338821 0.883803665638 -2.99790024757 12.3845338821 0.853707075119 -2.8249130249 12.3845338821 0.823668122292 -2.63641357422 12.3845338821 0.751955866814 -2.37737417221 12.3845338821 0.726527988911 -2.28288006783 12.3845338821 0.703353822231 -2.21080708504 12.3845338821 0.643333554268 -2.02067160606 12.3845338821 0.591677963734 -1.89556956291 12.3845338821 0.537480652332 -1.76471436024 12.3845338821 0.449440717697 -1.56702613831 12.3845338821 0.418173313141 -1.49993360043 12.3845338821 0.378651887178 -1.43565392494 12.3845338821 0.247761458158 -1.22573685646 12.3845338821 0.1547549963 -1.11002516747 12.3845338821 0.0537632778287 -0.987136542797 12.3845338821 -0.111260682344 -0.81679546833 12.3845338821 -0.161625772715 -0.769756257534 12.3845338821 -0.33469325304 -0.630133748055 12.3845338821 -0.386572659016 -0.587966620922 12.3845338821 -0.402950048447 -0.578156411648 12.3845338821 -0.669084429741 -0.427231997252 12.3845338821 -0.668322741985 -0.425775647163 12.3845338821 -0.819564521313 -0.346398621798 12.3845338821 -0.817734718323 -0.340600520372 12.3845338821 -1.02783739567 -0.259155422449 12.3845338821 -1.03144907951 -0.268644660711 12.3845338821 -1.20095252991 -0.217360585928 12.3845338821 -1.19988322258 -0.213113412261 12.3845338821 -1.44739544392 -0.152298659086 12.3845338821 -1.44858217239 -0.145311743021 12.3845338821 -1.64142012596 -0.128850758076 12.3845338821 -1.64145696163 -0.129123196006 12.3845338821 -1.65508985519 -0.129639625549 12.3845338821 -1.65508365631 -0.12985008955 12.3845338821 -1.86275660992 -0.144585043192 12.3845338821 -1.86295044422 -0.14271736145 12.3845338821 -2.10851240158 -0.165219992399 12.3845338821 -2.10920572281 -0.161992967129 12.3845338821 -2.28744006157 -0.196288183331 12.3845338821 -2.28614878654 -0.204576119781 12.3845338821 -2.4547958374 -0.255826175213 12.3845338821 -2.4553437233 -0.254594922066 12.3845338821 -2.62002253532 -0.318338274956 12.3845338821 -2.61732172966 -0.325273692608 12.3845338821 -2.7879421711 -0.401500284672 12.3845338821 -2.78902387619 -0.399318605661 12.3845338821 -2.85785770416 -0.433620721102 12.3845338821 -2.85756206512 -0.434266209602 12.3845338821 -3.03281569481 -0.535175085068 12.3845338821 -3.03903961182 -0.526761472225 12.3845338821 -3.29270458221 -0.705116987228 12.3845338821 -3.28946995735 -0.710396528244 12.3845338821 -3.44858193398 -0.848804295063 12.3845338821 -3.45010757446 -0.847568750381 12.3845338821 -3.59832286835 -0.999551713467 12.3845338821 -3.59257674217 -1.0051047802 12.3845338821 -3.73015165329 -1.16042852402 12.3845338821 -3.73215508461 -1.15872848034 12.3845338821 -3.79209828377 -1.22992193699 12.3845338821 -3.79194784164 -1.23007655144 12.3845338821 -3.84002399445 -1.2934705019 12.3845338821 -3.83892941475 -1.29429686069 12.3845338821 -3.94141578674 -1.43441653252 12.3845338821 -3.94855451584 -1.42987728119 12.3845338821 -4.04360389709 -1.58026468754 12.3845338821 -4.03998994827 -1.58256816864 12.3845338821 -4.12654161453 -1.74509489536 12.3845338821 -4.13379859924 -1.74191665649 12.3845338821 -4.24752283096 -1.99890899658 12.3845338821 -4.23779821396 -2.00325250626 12.3845338821 -4.30209350586 -2.18260955811 12.3845338821 -4.30419206619 -2.18205070496 12.3845338821 -4.32800006866 -2.27020978928 12.3845338821 -4.32306623459 -2.27151966095 12.3845338821 -4.3605427742 -2.46509718895 12.3845338821 -4.36808300018 -2.46397161484 12.3845338821 -4.3949213028 -2.64877414703 12.3845338821 -4.39242267609 -2.64914655685 12.3845338821 -4.40665721893 -2.80836772919 12.3845338821 -4.41322755814 -2.80824971199 12.3845338821 -4.41842365265 -3.04868364334 12.3845338821 -4.4110622406 -3.04893040657 12.3845338821 -4.40263652802 -3.21186161041 12.3845338821 -4.40375471115 -3.21199703217 12.3845338821 -4.39427614212 -3.3000266552 12.3845338821 -4.39133262634 -3.29970002174 12.3845338821 -4.36577796936 -3.46877408028 12.3845338821 -4.37298440933 -3.47026848793 12.3845338821 -4.33875370026 -3.63255643845 12.3845338821 -4.33646154404 -3.63215517998 12.3845338821 -4.28888082504 -3.79266142845 12.3845338821 -4.29137706757 -3.79370713234 12.3845338821 -4.2196969986 -3.99059867859 12.3845338821 -4.21673345566 -3.98957180977 12.3845338821 -4.1623916626 -4.11764669418 12.3845338821 -4.16499376297 -4.11907625198 12.3845338821 -4.08592510223 -4.26734113693 12.3845338821 -4.07870721817 -4.26368427277 12.3845338821 -4.00477600098 -4.37572383881 12.3845338821 -4.00484657288 -4.37578058243 12.3845338821 -3.98019695282 -4.41011857986 12.3845338821 -3.97944784164 -4.40957832336 12.3845338821 -3.89905834198 -4.51645898819 12.3845338821 -3.90550780296 -4.5221529007 12.3845338821 -3.8228597641 -4.61530733109 12.3845338821 -3.82142400742 -4.6140537262 12.3845338821 -3.69361758232 -4.7343173027 12.3845338821 -3.69294428825 -4.73360204697 12.3845338821 -3.5661418438 -4.85130834579 12.3845338821 -3.5666372776 -4.85188007355 12.3845338821 -3.54508781433 -4.8705573082 12.3845338821 -3.54511070251 -4.87061548233 12.3845338821 -3.43702912331 -4.95049381256 12.3845338821 -3.4342777729 -4.9468626976 12.3845338821 -3.26944303513 -5.05594396591 12.3845338821 -3.27013397217 -5.05711364746 12.3845338821 -3.16173005104 -5.12337207794 12.3845338821 -3.16163182259 -5.12337493896 12.3845338821 -3.00780487061 -5.20618963242 12.3845338821 -3.00636124611 -5.2035150528 12.3845338821 -2.89218449593 -5.26092290878 12.3845338821 -2.89229464531 -5.2611579895 12.3845338821 -2.87660050392 -5.26854467392 12.3845338821 -2.87661385536 -5.26860332489 12.3845338821 -2.71925711632 -5.33160448074 12.3845338821 -2.71897602081 -5.33199739456 12.3845338821 -2.50230503082 -5.40163612366 12.3845338821 -2.50200438499 -5.40102958679 12.3845338821 -2.33938336372 -5.44251012802 12.3845338821 -2.33934020996 -5.44305372238 12.3845338821 -2.12555241585 -5.48501014709 12.3845338821 -2.12511444092 -5.48314189911 12.3845338821 -1.9634077549 -5.50749635696 12.3845338821 -1.96346390247 -5.50831413269 12.3845338821 -1.77676296234 -5.52801036835 12.3845338821 -1.77631723881 -5.52378511429 12.3845338821 -1.61245846748 -5.53601789474 12.3845338821 -1.61247622967 -5.53629636765 12.3845338821 -1.58050835133 -5.5383272171 12.3845338821 -1.5805066824 -5.5383272171 12.3845338821 -1.45225787163 -5.54363203049 12.3845338821 -1.45222699642 -5.54350566864 12.3845338821 -1.28356862068 -5.54692983627 12.3845338821 -1.28355967999 -5.54653120041 12.3845338821 -1.15441644192 -5.54766464233 12.3845338821 -1.15435194969 -5.55013656616 12.3845338821 -0.984619557858 -5.54599189758 12.3845338821 -0.984613001347 -5.544298172 12.3845338821 -0.85432100296 -5.53536462784 12.3845338821 -0.854348599911 -5.53493976593 12.3845338821 -0.68663674593 -5.52094078064 12.3845338821 -0.686221122742 -5.52441835403 12.3845338821 -0.557402431965 -5.50939941406 12.3845338821 -0.557369291782 -5.50950479507 12.3845338821 -0.530705988407 -5.50402927399 12.3845338821 -0.530973255634 -5.50273609161 12.3845338821 -0.420848578215 -5.47375202179 12.3845338821 -0.420872956514 -5.47365617752 12.3845338821 -0.313143521547 -5.44572925568 12.3845338821 -0.312855273485 -5.44667720795 12.3845338821 -0.202811524272 -5.41295146942 12.3845338821 -0.200845867395 -5.41591787338 12.3845338821 -0.0739111676812 -5.35825109482 12.3845338821 -0.0751661285758 -5.35535430908 12.3845338821 0.0327069908381 -5.29327440262 12.3845338821 0.0342196822166 -5.29546546936 12.3845338821 0.157501786947 -5.21443223953 12.3845338821 0.158227786422 -5.21480321884 12.3845338821 0.255702465773 -5.13516950607 12.3845338821 0.25434550643 -5.1333823204 12.3845338821 0.346590995789 -5.04563570023 12.3845338821 0.349269986153 -5.04785633087 12.3845338821 0.455277830362 -4.92791414261 12.3845338821 0.452427327633 -4.9251203537 12.3845338821 0.56395816803 -4.77249574661 12.3845338821 0.566233873367 -4.77369832993 12.3845338821 0.659336268902 -4.62388706207 12.3845338821 0.657507181168 -4.62260198593 12.3845338821 0.741499066353 -4.46259069443 12.3845338821 0.746278226376 -4.46432352066 12.3845338821 0.808546662331 -4.31264734268 12.3845338821 0.806778609753 -4.31162977219 12.3845338821 0.853313148022 -4.14892578125 12.3845338821 0.854718029499 -4.14880228043 12.3845338821 0.885586738586 -3.98610901833 12.3845338821 0.882705688477 -3.98555731773 12.3845338821 0.904292285442 -3.82559323311 12.3845338821 0.911802053452 -3.82601547241 12.3845338821 0.925992786884 -3.58085775375 12.3845338821 0.916205346584 -3.58021879196 12.3845338821 0.912141382694 -3.40464758873 12.3845338821 0.912865281105 -3.4045984745 12.3845338821 0.908276736736 -3.32567691803 12.3845338821 0.906493008137 -3.32578921318 12.3845338821 0.892005801201 -3.13862895966 12.3845338821 0.900653243065 -3.13764405251 12.3845338821 0.873308539391 -2.91085767746 12.3845338821 0.868139326572 -2.91150927544 12.3845338821 0.838017880917 -2.73077487946 12.3845338821 0.843909621239 -2.72952818871 12.3845338821 0.795165300369 -2.50529527664 12.3845338821 0.787344813347 -2.50702142715 12.3845338821 0.739071846008 -2.33017349243 12.3845338821 0.740373432636 -2.3297932148 12.3845338821 0.715816318989 -2.24658513069 12.3845338821 0.714844882488 -2.24687409401 12.3845338821 0.673090755939 -2.11581993103 12.3845338821 0.677362501621 -2.1142783165 12.3845338821 0.62023383379 -1.95712888241 12.3845338821 0.617539644241 -1.95810651779 12.3845338821 0.564614772797 -1.83012723923 12.3845338821 0.565435230732 -1.82977426052 12.3845338821 0.494768410921 -1.66530835629 12.3845338821 0.494303971529 -1.66548597813 12.3845338821 0.434095472097 -1.53334844112 12.3845338821 0.43568238616 -1.53247070312 12.3845338821 0.400324374437 -1.46676492691 12.3845338821 0.398513436317 -1.46773135662 12.3845338821 0.313537299633 -1.33049070835 12.3845338821 0.319229453802 -1.32642018795 12.3845338821 0.204872578382 -1.16531538963 12.3845338821 0.201571062207 -1.16762673855 12.3845338821 0.104594320059 -1.0483083725 12.3845338821 0.106680497527 -1.0464195013 12.3845338821 -0.0251383576542 -0.898743331432 12.3845338821 -0.0266724936664 -0.899851500988 12.3845338821 -0.135839924216 -0.792661428452 12.3845338821 -0.135625302792 -0.792333960533 12.3845338821 -0.245520368218 -0.696905791759 12.3845338821 -0.248287126422 -0.700102567673 12.3845338821 -0.360671311617 -0.609097540379 12.3845338821 -0.359264701605 -0.607097089291 12.3845338821 -0.394370645285 -0.582503795624 12.3845338821 -0.394704699516 -0.582964360714 12.3845338821 -0.510504066944 -0.515441000462 12.3845338821 -0.511162996292 -0.516566753387 # objtype='BezierSpline'; ncoords=100; nparts=100; closed=True; sep=' '; name='splines-017' 15.0859794617 -1.07252705097 -5.84364891052 15.0859794617 -1.24750137329 -5.84147262573 15.0859794617 -1.42863821983 -5.83964920044 15.0859794617 -1.51824760437 -5.83047866821 15.0859794617 -1.77428889275 -5.78046417236 15.0859794617 -2.02601075172 -5.72129106522 15.0859794617 -2.10606718063 -5.70186758041 15.0859794617 -2.25655555725 -5.6455039978 15.0859794617 -2.41660642624 -5.58548736572 15.0859794617 -2.5071246624 -5.54879570007 15.0859794617 -2.75132346153 -5.43115854263 15.0859794617 -2.94794535637 -5.31272745132 15.0859794617 -3.07270216942 -5.22784852982 15.0859794617 -3.36345696449 -4.97753572464 15.0859794617 -3.37842273712 -4.96324157715 15.0859794617 -3.41679596901 -4.91837453842 15.0859794617 -3.64692831039 -4.64944791794 15.0859794617 -3.69219660759 -4.58692407608 15.0859794617 -3.79913830757 -4.42810964584 15.0859794617 -3.87945842743 -4.29488658905 15.0859794617 -3.9571416378 -4.14000988007 15.0859794617 -4.06623601913 -3.88444185257 15.0859794617 -4.110247612 -3.74107241631 15.0859794617 -4.19002771378 -3.44573497772 15.0859794617 -4.21100330353 -3.3105969429 15.0859794617 -4.22941684723 -3.09894824028 15.0859794617 -4.24010801315 -2.88351511955 15.0859794617 -4.24375391006 -2.80224943161 15.0859794617 -4.22684812546 -2.68480205536 15.0859794617 -4.19483327866 -2.45649313927 15.0859794617 -4.12468481064 -2.1657037735 15.0859794617 -4.11302280426 -2.11913752556 15.0859794617 -4.09972429276 -2.08701229095 15.0859794617 -3.96997380257 -1.7793571949 15.0859794617 -3.8457159996 -1.58321249485 15.0859794617 -3.76729726791 -1.46161603928 15.0859794617 -3.57189583778 -1.22007906437 15.0859794617 -3.56824755669 -1.21559655666 15.0859794617 -3.5556974411 -1.20254456997 15.0859794617 -3.35216403008 -0.986765921116 15.0859794617 -3.28456830978 -0.922331273556 15.0859794617 -3.12492418289 -0.775359272957 15.0859794617 -2.97406482697 -0.657705545425 15.0859794617 -2.86803770065 -0.578310310841 15.0859794617 -2.58551716805 -0.421451628208 15.0859794617 -2.51995515823 -0.389662206173 15.0859794617 -2.13810634613 -0.261707812548 15.0859794617 -2.13021755219 -0.259182721376 15.0859794617 -2.08810997009 -0.253183752298 15.0859794617 -1.78942489624 -0.206888362765 15.0859794617 -1.72345101833 -0.19901509583 15.0859794617 -1.45481491089 -0.181035354733 15.0859794617 -1.350938797 -0.183370187879 15.0859794617 -1.13938307762 -0.198222443461 15.0859794617 -1.00343084335 -0.207152888179 15.0859794617 -0.902052283287 -0.229371219873 15.0859794617 -0.632227361202 -0.290776610374 15.0859794617 -0.275957435369 -0.424871087074 15.0859794617 -0.273853391409 -0.425723224878 15.0859794617 -0.271084070206 -0.427625060081 15.0859794617 0.0740692391992 -0.651961266994 15.0859794617 0.23101067543 -0.797117114067 15.0859794617 0.328757435083 -0.890189945698 15.0859794617 0.5011703372 -1.08086395264 15.0859794617 0.565039396286 -1.15033555031 15.0859794617 0.595123112202 -1.19090294838 15.0859794617 0.764292418957 -1.42336916924 15.0859794617 0.889857232571 -1.62592303753 15.0859794617 0.937995254993 -1.70566725731 15.0859794617 1.02721500397 -1.90726530552 15.0859794617 1.10725569725 -2.08585071564 15.0859794617 1.13912177086 -2.18086743355 15.0859794617 1.23475539684 -2.47867155075 15.0859794617 1.3002859354 -2.75902915001 15.0859794617 1.32279491425 -2.86714744568 15.0859794617 1.33732175827 -3.10611367226 15.0859794617 1.3455401659 -3.25541853905 15.0859794617 1.34779119492 -3.32144594193 15.0859794617 1.35073721409 -3.58427047729 15.0859794617 1.31716442108 -3.80402874947 15.0859794617 1.30382287502 -3.91014075279 15.0859794617 1.25714945793 -4.12981700897 15.0859794617 1.23499393463 -4.23064756393 15.0859794617 1.21773350239 -4.27607011795 15.0859794617 1.10414254665 -4.5531835556 15.0859794617 0.989975452423 -4.74394416809 15.0859794617 0.890512704849 -4.90577459335 15.0859794617 0.640894174576 -5.18747425079 15.0859794617 0.612615525723 -5.21702337265 15.0859794617 0.548515558243 -5.26646614075 15.0859794617 0.356513559818 -5.41052913666 15.0859794617 0.255182743073 -5.47149658203 15.0859794617 0.0878502130508 -5.56697416306 15.0859794617 -0.0729052126408 -5.63350772858 15.0859794617 -0.155872970819 -5.66667747498 15.0859794617 -0.298202961683 -5.71121358871 15.0859794617 -0.407702952623 -5.74163770676 15.0859794617 -0.474754482508 -5.7557964325 15.0859794617 -0.726907014847 -5.81012964249 15.0859794617 -0.987761616707 -5.83621501923 15.0859794617 -1.15985035896 -5.84692668915 15.0859794617 -1.16001307964 -5.84245729446 15.0859794617 -1.33806860447 -5.84045362473 15.0859794617 -1.33830285072 -5.84471559525 15.0859794617 -1.47355878353 -5.83713006973 15.0859794617 -1.47374367714 -5.8370885849 15.0859794617 -1.64713919163 -5.81133508682 15.0859794617 -1.64678907394 -5.80789232254 15.0859794617 -1.90066611767 -5.75327730179 15.0859794617 -1.9002571106 -5.75132656097 15.0859794617 -2.06607317924 -5.71172237396 15.0859794617 -2.06676673889 -5.71394681931 15.0859794617 -2.18273091316 -5.6783041954 15.0859794617 -2.18131685257 -5.67370033264 15.0859794617 -2.33658695221 -5.61551141739 15.0859794617 -2.33699035645 -5.61654472351 15.0859794617 -2.46209931374 -5.56774091721 15.0859794617 -2.46249651909 -5.56856584549 15.0859794617 -2.63097524643 -5.49392986298 15.0859794617 -2.63222622871 -5.49553155899 15.0859794617 -2.85217666626 -5.37664651871 15.0859794617 -2.85134577751 -5.37461423874 15.0859794617 -3.01144862175 -5.27204418182 15.0859794617 -3.01292467117 -5.27368021011 15.0859794617 -3.22469234467 -5.11131715775 15.0859794617 -3.2214076519 -5.106361866 15.0859794617 -3.37111926079 -4.97058677673 15.0859794617 -3.37131857872 -4.97074699402 15.0859794617 -3.39868950844 -4.94183063507 15.0859794617 -3.39760637283 -4.94080543518 15.0859794617 -3.53184366226 -4.78389549255 15.0859794617 -3.53750181198 -4.78835391998 15.0859794617 -3.67079234123 -4.61915493011 15.0859794617 -3.67010092735 -4.61856222153 15.0859794617 -3.74700284004 -4.50844955444 15.0859794617 -3.74768877029 -4.5088057518 15.0859794617 -3.84094047546 -4.36254501343 15.0859794617 -3.84194207191 -4.36295509338 15.0859794617 -3.92124462128 -4.21907091141 15.0859794617 -3.92071485519 -4.21856784821 15.0859794617 -4.01556158066 -4.01402139664 15.0859794617 -4.01857566833 -4.01474475861 15.0859794617 -4.0919585228 -3.81411671638 15.0859794617 -4.08946704865 -3.81311058998 15.0859794617 -4.15263700485 -3.59412455559 15.0859794617 -4.15835189819 -3.59514522552 15.0859794617 -4.20418739319 -3.37894439697 15.0859794617 -4.20279598236 -3.37844181061 15.0859794617 -4.22375297546 -3.20520114899 15.0859794617 -4.22218084335 -3.20490717888 15.0859794617 -4.23676347733 -2.99136829376 15.0859794617 -4.23501825333 -2.99124383926 15.0859794617 -4.24202728271 -2.84288692474 15.0859794617 -4.24573993683 -2.84269523621 15.0859794617 -4.24085712433 -2.74325299263 15.0859794617 -4.23519420624 -2.74354100227 15.0859794617 -4.21063280106 -2.57067704201 15.0859794617 -4.21635293961 -2.56959891319 15.0859794617 -4.16691112518 -2.30973768234 15.0859794617 -4.16038942337 -2.31094360352 15.0859794617 -4.11895513535 -2.14239573479 15.0859794617 -4.12052869797 -2.14186763763 15.0859794617 -4.10758638382 -2.10267448425 15.0859794617 -4.10642671585 -2.10305261612 15.0859794617 -4.03535938263 -1.93297159672 15.0859794617 -4.047082901 -1.92678618431 15.0859794617 -3.91635227203 -1.67683529854 15.0859794617 -3.90824103355 -1.68103158474 15.0859794617 -3.80675339699 -1.52225649357 15.0859794617 -3.80965256691 -1.52013731003 15.0859794617 -3.67635154724 -1.33595860004 15.0859794617 -3.66977477074 -1.34070289135 15.0859794617 -3.5700750351 -1.2178350687 15.0859794617 -3.5701611042 -1.21775865555 15.0859794617 -3.56225252151 -1.2088227272 15.0859794617 -3.56194114685 -1.20910048485 15.0859794617 -3.45341539383 -1.09514594078 15.0859794617 -3.45672369003 -1.09187686443 15.0859794617 -3.31924557686 -0.953673899174 15.0859794617 -3.31864333153 -0.954252660275 15.0859794617 -3.20539045334 -0.848157703876 15.0859794617 -3.20761275291 -0.845464110374 15.0859794617 -3.05202174187 -0.713551402092 15.0859794617 -3.05006408691 -0.715787112713 15.0859794617 -2.92144560814 -0.617491900921 15.0859794617 -2.92349624634 -0.6142334342 15.0859794617 -2.73274207115 -0.490672796965 15.0859794617 -2.72883915901 -0.495912760496 15.0859794617 -2.55320096016 -0.404662162066 15.0859794617 -2.55361747742 -0.403397172689 15.0859794617 -2.33390140533 -0.313747942448 15.0859794617 -2.32945537567 -0.324388444424 15.0859794617 -2.13417077065 -0.260418593884 15.0859794617 -2.13423991203 -0.260106056929 15.0859794617 -2.1095635891 -0.254441440105 15.0859794617 -2.10914468765 -0.256312161684 15.0859794617 -1.93863117695 -0.230952247977 15.0859794617 -1.93912672997 -0.227416291833 15.0859794617 -1.75651693344 -0.202375844121 15.0859794617 -1.75651788712 -0.202092662454 15.0859794617 -1.58945691586 -0.186544120312 15.0859794617 -1.58926618099 -0.184017747641 15.0859794617 -1.40292823315 -0.179884403944 15.0859794617 -1.40281963348 -0.180967345834 15.0859794617 -1.24504411221 -0.188274666667 15.0859794617 -1.24517714977 -0.191034138203 15.0859794617 -1.0714173317 -0.202840462327 15.0859794617 -1.07069051266 -0.197628393769 15.0859794617 -0.952195823193 -0.214408159256 15.0859794617 -0.95269626379 -0.218059152365 15.0859794617 -0.767018973827 -0.259532898664 15.0859794617 -0.764430224895 -0.251055628061 15.0859794617 -0.450365006924 -0.345418006182 15.0859794617 -0.453232795 -0.355623275042 15.0859794617 -0.27490028739 -0.425284028053 15.0859794617 -0.274847209454 -0.425188928843 15.0859794617 -0.272382616997 -0.426513969898 15.0859794617 -0.272480607033 -0.426691889763 15.0859794617 -0.0999614447355 -0.541969180107 15.0859794617 -0.0877711027861 -0.525998830795 15.0859794617 0.158115506172 -0.717375576496 15.0859794617 0.153070390224 -0.723973989487 15.0859794617 0.280218958855 -0.843296647072 15.0859794617 0.281689733267 -0.841893792152 15.0859794617 0.418403029442 -0.982175290585 15.0859794617 0.414571642876 -0.985884606838 15.0859794617 0.532960891724 -1.1157310009 15.0859794617 0.535019159317 -1.11401748657 15.0859794617 0.581105768681 -1.16977238655 15.0859794617 0.580172836781 -1.1705520153 15.0859794617 0.680229127407 -1.30675303936 15.0859794617 0.68412977457 -1.30416238308 15.0859794617 0.830740272999 -1.5221811533 15.0859794617 0.827675879002 -1.52427840233 15.0859794617 0.914161145687 -1.66565144062 15.0859794617 0.916536569595 -1.66443657875 15.0859794617 0.988783121109 -1.80325090885 15.0859794617 0.982368588448 -1.80657160282 15.0859794617 1.06702542305 -1.99665153027 15.0859794617 1.07168877125 -1.99481797218 15.0859794617 1.12546932697 -2.13246798515 15.0859794617 1.12349486351 -2.13325858116 15.0859794617 1.18789386749 -2.32945585251 15.0859794617 1.19304931164 -2.32807707787 15.0859794617 1.27314555645 -2.61729264259 15.0859794617 1.26923274994 -2.6184720993 15.0859794617 1.3121970892 -2.81294322014 15.0859794617 1.31549239159 -2.81255960464 15.0859794617 1.33862555027 -2.98548436165 15.0859794617 1.33040046692 -2.98661065102 15.0859794617 1.34164464474 -3.18075370789 15.0859794617 1.34221184254 -3.18073129654 15.0859794617 1.3470107317 -3.28841686249 15.0859794617 1.34704327583 -3.28842377663 15.0859794617 1.35076665878 -3.45282411575 15.0859794617 1.3599241972 -3.45360779762 15.0859794617 1.34296691418 -3.69478344917 15.0859794617 1.33249080181 -3.69394636154 15.0859794617 1.30979120731 -3.85698699951 15.0859794617 1.31271481514 -3.85745978355 15.0859794617 1.28515052795 -4.02076625824 15.0859794617 1.28086709976 -4.02006101608 15.0859794617 1.24624681473 -4.18027019501 15.0859794617 1.24970054626 -4.18131399155 15.0859794617 1.22807180882 -4.25386810303 15.0859794617 1.22665607929 -4.25347423553 15.0859794617 1.16273987293 -4.41533851624 15.0859794617 1.17099034786 -4.41965961456 15.0859794617 1.05452084541 -4.65229940414 15.0859794617 1.04761922359 -4.64890384674 15.0859794617 0.940722763538 -4.82514953613 15.0859794617 0.946872651577 -4.82977485657 15.0859794617 0.778837740421 -5.05636453629 15.0859794617 0.768357038498 -5.04906845093 15.0859794617 0.627043187618 -5.2025141716 15.0859794617 0.627781569958 -5.20339107513 15.0859794617 0.582597732544 -5.24400568008 15.0859794617 0.580728650093 -5.241959095 15.0859794617 0.452998250723 -5.33913326263 15.0859794617 0.455934226513 -5.3435754776 15.0859794617 0.30753287673 -5.44351434708 15.0859794617 0.306193947792 -5.44160270691 15.0859794617 0.172079816461 -5.52019643784 15.0859794617 0.174186199903 -5.52468585968 15.0859794617 0.00988342333585 -5.60516309738 15.0859794617 0.00767064094543 -5.60072803497 15.0859794617 -0.1142873317 -5.65034294128 15.0859794617 -0.113812200725 -5.65171432495 15.0859794617 -0.226075097919 -5.69165229797 15.0859794617 -0.22669750452 -5.69009828568 15.0859794617 -0.352693498135 -5.72730445862 15.0859794617 -0.352528959513 -5.72816181183 15.0859794617 -0.440973043442 -5.74976396561 15.0859794617 -0.441243469715 -5.74864768982 15.0859794617 -0.600886285305 -5.78270292282 15.0859794617 -0.599703907967 -5.79012966156 15.0859794617 -0.856189072132 -5.83045625687 15.0859794617 -0.857259631157 -5.82396793365 15.0859794617 -1.03012013435 -5.84019041061 15.0859794617 -1.03006458282 -5.8420548439 # objtype='BezierSpline'; ncoords=96; nparts=96; closed=True; sep=' '; name='splines-018' 17.7874240875 -2.51186800003 -0.244853928685 17.7874240875 -2.51352715492 -0.245898202062 17.7874240875 -2.85603356361 -0.457417219877 17.7874240875 -2.95990967751 -0.543490946293 17.7874240875 -3.18151569366 -0.728347420692 17.7874240875 -3.34191679955 -0.893745839596 17.7874240875 -3.4181637764 -0.975453078747 17.7874240875 -3.59359383583 -1.22021925449 17.7874240875 -3.61579036713 -1.25118851662 17.7874240875 -3.62288451195 -1.26391267776 17.7874240875 -3.78560709953 -1.57000362873 17.7874240875 -3.83631801605 -1.69473659992 17.7874240875 -3.91245126724 -1.92064034939 17.7874240875 -3.95936250687 -2.13221144676 17.7874240875 -3.98038315773 -2.25301003456 17.7874240875 -4.0049071312 -2.56286001205 17.7874240875 -4.00664710999 -2.59639406204 17.7874240875 -4.00348615646 -2.73094129562 17.7874240875 -3.99935102463 -2.95089054108 17.7874240875 -3.99684262276 -3.00738382339 17.7874240875 -3.97150397301 -3.31206417084 17.7874240875 -3.95076847076 -3.4494073391 17.7874240875 -3.89552211761 -3.73156118393 17.7874240875 -3.82762432098 -3.90248608589 17.7874240875 -3.72721004486 -4.12399959564 17.7874240875 -3.62401795387 -4.3026881218 17.7874240875 -3.53309607506 -4.44713354111 17.7874240875 -3.37541532516 -4.648001194 17.7874240875 -3.29841804504 -4.73944473267 17.7874240875 -3.02682828903 -4.99116706848 17.7874240875 -3.01468396187 -5.00146865845 17.7874240875 -2.96973061562 -5.03329181671 17.7874240875 -2.69702529907 -5.22552442551 17.7874240875 -2.62136554718 -5.27270698547 17.7874240875 -2.34918379784 -5.43020868301 17.7874240875 -2.16535210609 -5.51987314224 17.7874240875 -1.98817336559 -5.59220647812 17.7874240875 -1.69049215317 -5.68055200577 17.7874240875 -1.63799905777 -5.69311380386 17.7874240875 -1.47013616562 -5.71712684631 17.7874240875 -1.3061965704 -5.74209070206 17.7874240875 -1.25168216228 -5.74503803253 17.7874240875 -0.965691208839 -5.74871444702 17.7874240875 -0.795225322247 -5.73085784912 17.7874240875 -0.623006701469 -5.70808839798 17.7874240875 -0.310770004988 -5.63173294067 17.7874240875 -0.238109946251 -5.60949373245 17.7874240875 -0.00637794891372 -5.50370264053 17.7874240875 0.109956033528 -5.45020437241 17.7874240875 0.144534006715 -5.42917490005 17.7874240875 0.369749814272 -5.27434158325 17.7874240875 0.445505648851 -5.21317386627 17.7874240875 0.607211768627 -5.07791757584 17.7874240875 0.773270368576 -4.88653182983 17.7874240875 0.809817016125 -4.84587144852 17.7874240875 0.879772365093 -4.75294923782 17.7874240875 0.994205534458 -4.5973739624 17.7874240875 1.04019474983 -4.52108669281 17.7874240875 1.1683396101 -4.30240011215 17.7874240875 1.26790440083 -4.07029628754 17.7874240875 1.30751764774 -3.9769487381 17.7874240875 1.37647843361 -3.72634100914 17.7874240875 1.40476727486 -3.62591767311 17.7874240875 1.40966892242 -3.58384203911 17.7874240875 1.43626320362 -3.27632522583 17.7874240875 1.44104588032 -3.09195494652 17.7874240875 1.44331371784 -2.92839288712 17.7874240875 1.43268930912 -2.59947681427 17.7874240875 1.43206059933 -2.58094906807 17.7874240875 1.4255168438 -2.53008556366 17.7874240875 1.38414430618 -2.20322537422 17.7874240875 1.35629940033 -2.09685277939 17.7874240875 1.28345704079 -1.82924592495 17.7874240875 1.1868827343 -1.59566247463 17.7874240875 1.12542951107 -1.45108914375 17.7874240875 0.918198645115 -1.10870110989 17.7874240875 0.91174441576 -1.09825146198 17.7874240875 0.895702242851 -1.07934939861 17.7874240875 0.698050379753 -0.841257572174 17.7874240875 0.601468861103 -0.747222900391 17.7874240875 0.427127748728 -0.586278736591 17.7874240875 0.165121689439 -0.395829170942 17.7874240875 0.11696241051 -0.363141864538 17.7874240875 0.0109917335212 -0.310229629278 17.7874240875 -0.218441426754 -0.19113625586 17.7874240875 -0.333787322044 -0.152079835534 17.7874240875 -0.592424094677 -0.0714838132262 17.7874240875 -0.887378931046 -0.00775848794729 17.7874240875 -0.980986893177 0.011079008691 17.7874240875 -1.18609714508 0.0265770331025 17.7874240875 -1.40984714031 0.0452531836927 17.7874240875 -1.52518677711 0.0344239659607 17.7874240875 -1.77702963352 0.00447144685313 17.7874240875 -2.01474881172 -0.0510753877461 17.7874240875 -2.14407348633 -0.084905564785 17.7874240875 -2.506524086 -0.242559701204 17.7874240875 -2.51273322105 -0.245308339596 17.7874240875 -2.5126953125 -0.24537961185 17.7874240875 -2.68432641029 -0.352385729551 17.7874240875 -2.69291472435 -0.340325862169 17.7874240875 -2.91069746017 -0.496656596661 17.7874240875 -2.90804243088 -0.500369012356 17.7874240875 -3.07086396217 -0.63573718071 17.7874240875 -3.07588744164 -0.630341827869 17.7874240875 -3.26584768295 -0.806593775749 17.7874240875 -3.2625181675 -0.810283660889 17.7874240875 -3.38042926788 -0.934229373932 17.7874240875 -3.38282608986 -0.932317376137 17.7874240875 -3.51338505745 -1.09168684483 17.7874240875 -3.5058786869 -1.09783625603 17.7874240875 -3.60469198227 -1.23570394516 17.7874240875 -3.60560274124 -1.23512637615 17.7874240875 -3.61968564987 -1.25732982159 17.7874240875 -3.61940145493 -1.25751578808 17.7874240875 -3.70576715469 -1.41612970829 17.7874240875 -3.71228694916 -1.41319823265 17.7874240875 -3.81408596039 -1.63090968132 17.7874240875 -3.81288981438 -1.63165438175 17.7874240875 -3.87779664993 -1.80642127991 17.7874240875 -3.88051700592 -1.80598056316 17.7874240875 -3.94148159027 -2.02487325668 17.7874240875 -3.93834662437 -2.02594351768 17.7874240875 -3.97125315666 -2.19233775139 17.7874240875 -3.97270941734 -2.19225239754 17.7874240875 -3.9998357296 -2.40702676773 17.7874240875 -3.99474978447 -2.40779709816 17.7874240875 -4.0060043335 -2.5796122551 17.7874240875 -4.00640916824 -2.57961797714 17.7874240875 -4.00760030746 -2.66363167763 17.7874240875 -4.00490903854 -2.66366434097 17.7874240875 -4.00116062164 -2.84091043472 17.7874240875 -4.00282430649 -2.84096026421 17.7874240875 -3.99845814705 -2.97914862633 17.7874240875 -3.99864149094 -2.97917199135 17.7874240875 -3.98711752892 -3.15991163254 17.7874240875 -3.98924875259 -3.16031742096 17.7874240875 -3.96344208717 -3.38100528717 17.7874240875 -3.96262478828 -3.38099384308 17.7874240875 -3.92622661591 -3.59101819992 17.7874240875 -3.93586945534 -3.59422230721 17.7874240875 -3.86971259117 -3.81941485405 17.7874240875 -3.86358213425 -3.81787753105 17.7874240875 -3.78007388115 -4.01437187195 17.7874240875 -3.78272080421 -4.01596784592 17.7874240875 -3.680113554 -4.21565580368 17.7874240875 -3.67729640007 -4.21435880661 17.7874240875 -3.57994866371 -4.37575006485 17.7874240875 -3.58217406273 -4.37745857239 17.7874240875 -3.45966720581 -4.5513792038 17.7874240875 -3.45595550537 -4.54894924164 17.7874240875 -3.3377122879 -4.69436979294 17.7874240875 -3.33958625793 -4.6962685585 17.7874240875 -3.17089271545 -4.87319087982 17.7874240875 -3.16532349586 -4.86835098267 17.7874240875 -3.02087235451 -4.99644899368 17.7874240875 -3.02096939087 -4.9965929985 17.7874240875 -2.99294519424 -5.01833152771 17.7874240875 -2.99222326279 -5.01740264893 17.7874240875 -2.83347439766 -5.12954473495 17.7874240875 -2.83597898483 -5.13332843781 17.7874240875 -2.6598906517 -5.25016355515 17.7874240875 -2.65957450867 -5.24974679947 17.7874240875 -2.48661160469 -5.35368299484 17.7874240875 -2.48788905144 -5.35636854172 17.7874240875 -2.25896835327 -5.47823524475 17.7874240875 -2.25865030289 -5.47813034058 17.7874240875 -2.07805609703 -5.55893039703 17.7874240875 -2.07833456993 -5.56051111221 17.7874240875 -1.84188306332 -5.64363384247 17.7874240875 -1.84040939808 -5.64039897919 17.7874240875 -1.66443276405 -5.68753147125 17.7874240875 -1.66448020935 -5.68806266785 17.7874240875 -1.55480444431 -5.70898342133 17.7874240875 -1.55401170254 -5.7047419548 17.7874240875 -1.38811171055 -5.72923851013 17.7874240875 -1.38857841492 -5.73361158371 17.7874240875 -1.27907502651 -5.74488210678 17.7874240875 -1.27895820141 -5.74412584305 17.7874240875 -1.10878503323 -5.74981737137 17.7874240875 -1.10830354691 -5.7552447319 17.7874240875 -0.880228638649 -5.74480104446 17.7874240875 -0.880321800709 -5.74093818665 17.7874240875 -0.70897769928 -5.72064113617 17.7874240875 -0.708247601986 -5.72409725189 17.7874240875 -0.465281486511 -5.67846679688 17.7874240875 -0.465669840574 -5.67434072495 17.7874240875 -0.274151921272 -5.62166070938 17.7874240875 -0.273556143045 -5.6229429245 17.7874240875 -0.119280956686 -5.56440734863 17.7874240875 -0.122170619667 -5.55675840378 17.7874240875 0.0518259033561 -5.47703409195 17.7874240875 0.053522143513 -5.48021268845 17.7874240875 0.127792790532 -5.44071960449 17.7874240875 0.127552092075 -5.4401640892 17.7874240875 0.259215593338 -5.35496282578 17.7874240875 0.2602853477 -5.35597324371 17.7874240875 0.40874761343 -5.24525928497 17.7874240875 0.407895237207 -5.24408340454 17.7874240875 0.526937901974 -5.14625024796 17.7874240875 0.532245218754 -5.15153980255 17.7874240875 0.697316229343 -4.98942899704 17.7874240875 0.689409852028 -4.98149061203 17.7874240875 0.791364371777 -4.86604309082 17.7874240875 0.792459905148 -4.86695575714 17.7874240875 0.846743822098 -4.80101490021 17.7874240875 0.845054268837 -4.79960346222 17.7874240875 0.937419950962 -4.6754822731 17.7874240875 0.940669894218 -4.6776175499 17.7874240875 1.01889789104 -4.56036281586 17.7874240875 1.01743876934 -4.55937194824 17.7874240875 1.10494613647 -4.41214704514 17.7874240875 1.11132264137 -4.41530656815 17.7874240875 1.2251522541 -4.18989849091 17.7874240875 1.21834814548 -4.18644475937 17.7874240875 1.28780186176 -4.02366113663 17.7874240875 1.29088830948 -4.02472829819 17.7874240875 1.35014212132 -3.8544793129 17.7874240875 1.34161913395 -3.8515393734 17.7874240875 1.39047074318 -3.67608690262 17.7874240875 1.39467692375 -3.67693114281 17.7874240875 1.40886414051 -3.60520553589 17.7874240875 1.40753102303 -3.60491156578 17.7874240875 1.42524671555 -3.4303150177 17.7874240875 1.42761361599 -3.4303445816 17.7874240875 1.44143152237 -3.18429613113 17.7874240875 1.43921101093 -3.18415117264 17.7874240875 1.44267332554 -3.01018381119 17.7874240875 1.44406700134 -3.01015663147 17.7874240875 1.44179821014 -2.76389980316 17.7874240875 1.43813562393 -2.76393032074 17.7874240875 1.43238246441 -2.59021258354 17.7874240875 1.43280911446 -2.59017777443 17.7874240875 1.42998981476 -2.55541992188 17.7874240875 1.42876267433 -2.55552053452 17.7874240875 1.40466356277 -2.3666768074 17.7874240875 1.41534566879 -2.36462259293 17.7874240875 1.37373113632 -2.14936065674 17.7874240875 1.37048041821 -2.14997005463 17.7874240875 1.32053053379 -1.962875247 17.7874240875 1.32815933228 -1.96022295952 17.7874240875 1.24271702766 -1.70987832546 17.7874240875 1.23574578762 -1.71221280098 17.7874240875 1.15651404858 -1.5232257843 17.7874240875 1.16112816334 -1.52083075047 17.7874240875 1.034481287 -1.27341115475 17.7874240875 1.02258443832 -1.27942407131 17.7874240875 0.914995193481 -1.10346186161 17.7874240875 0.915344834328 -1.10320496559 17.7874240875 0.904476881027 -1.08825278282 17.7874240875 0.903671681881 -1.08884382248 17.7874240875 0.796231508255 -0.960844695568 17.7874240875 0.80289131403 -0.954746842384 17.7874240875 0.652379870415 -0.791819691658 17.7874240875 0.650375783443 -0.79359036684 17.7874240875 0.515382826328 -0.665606856346 17.7874240875 0.518694281578 -0.661391854286 17.7874240875 0.302125900984 -0.483738213778 17.7874240875 0.297625243664 -0.488918095827 17.7874240875 0.141311690211 -0.379101723433 17.7874240875 0.142020761967 -0.377813965082 17.7874240875 0.0659687519073 -0.333284169436 17.7874240875 0.063766181469 -0.337099939585 17.7874240875 -0.10418510437 -0.251586914062 17.7874240875 -0.0998716726899 -0.241635963321 17.7874240875 -0.274299144745 -0.167346045375 17.7874240875 -0.275884717703 -0.170901492238 17.7874240875 -0.462594807148 -0.110210053623 17.7874240875 -0.461566418409 -0.10593508184 17.7874240875 -0.738186895847 -0.0331084355712 17.7874240875 -0.739682793617 -0.0385728739202 17.7874240875 -0.934113681316 0.00199196138419 17.7874240875 -0.93378162384 0.00457107042894 17.7874240875 -1.08267748356 0.0250985510647 17.7874240875 -1.08357405663 0.0184251237661 17.7874240875 -1.29800713062 0.0354753211141 17.7874240875 -1.2980234623 0.045831296593 17.7874240875 -1.46754336357 0.0449549034238 17.7874240875 -1.46759283543 0.0405516773462 17.7874240875 -1.65127432346 0.0210088714957 17.7874240875 -1.65232753754 0.0263864099979 17.7874240875 -1.89706301689 -0.0166230443865 17.7874240875 -1.8962751627 -0.0217433255166 17.7874240875 -2.0796225071 -0.0671369954944 17.7874240875 -2.08109664917 -0.0631182044744 17.7874240875 -2.33028268814 -0.149326324463 17.7874240875 -2.32511210442 -0.164164572954 17.7874240875 -2.50919318199 -0.24371317029 17.7874240875 -2.50930166245 -0.24350592494 # objtype='BezierSpline'; ncoords=96; nparts=96; closed=True; sep=' '; name='splines-019' 20.4888706207 -3.15503740311 -0.760906338692 20.4888706207 -3.34685969353 -1.01758158207 20.4888706207 -3.41168236732 -1.12140285969 20.4888706207 -3.51551795006 -1.29511773586 20.4888706207 -3.64540243149 -1.59051656723 20.4888706207 -3.67432260513 -1.66063642502 20.4888706207 -3.71272802353 -1.80770993233 20.4888706207 -3.78208494186 -2.06363034248 20.4888706207 -3.8006541729 -2.20008182526 20.4888706207 -3.83370399475 -2.48889064789 20.4888706207 -3.82777547836 -2.849421978 20.4888706207 -3.82661867142 -2.89176130295 20.4888706207 -3.82247662544 -2.92275214195 20.4888706207 -3.72754216194 -3.43684697151 20.4888706207 -3.72477149963 -3.44656991959 20.4888706207 -3.69119310379 -3.53174805641 20.4888706207 -3.52592968941 -3.95473909378 20.4888706207 -3.50308465958 -3.998265028 20.4888706207 -3.27637028694 -4.35404253006 20.4888706207 -3.20385766029 -4.4475312233 20.4888706207 -2.97912049294 -4.69822835922 20.4888706207 -2.82670235634 -4.8332400322 20.4888706207 -2.65515065193 -4.97024869919 20.4888706207 -2.43040466309 -5.12547826767 20.4888706207 -2.32014942169 -5.1939663887 20.4888706207 -1.96666884422 -5.35371828079 20.4888706207 -1.94269621372 -5.36385345459 20.4888706207 -1.85734307766 -5.38975524902 20.4888706207 -1.53303337097 -5.49150466919 20.4888706207 -1.42873084545 -5.50898265839 20.4888706207 -1.10725474358 -5.54642772675 20.4888706207 -0.839796483517 -5.54584217072 20.4888706207 -0.722553789616 -5.54349899292 20.4888706207 -0.45053294301 -5.49801158905 20.4888706207 -0.391237646341 -5.4882478714 20.4888706207 -0.364860743284 -5.48093271255 20.4888706207 -0.0648810043931 -5.39673471451 20.4888706207 0.149732708931 -5.30352258682 20.4888706207 0.2181828022 -5.27420854568 20.4888706207 0.476444244385 -5.11933851242 20.4888706207 0.490104168653 -5.11098861694 20.4888706207 0.64763879776 -4.98595619202 20.4888706207 0.82564419508 -4.84535646439 20.4888706207 0.8375030756 -4.83272600174 20.4888706207 1.10377013683 -4.53419685364 20.4888706207 1.13369882107 -4.49139404297 20.4888706207 1.30058097839 -4.24299240112 20.4888706207 1.35613799095 -4.13409805298 20.4888706207 1.45683276653 -3.92680263519 20.4888706207 1.52392625809 -3.74095392227 20.4888706207 1.5751324892 -3.59222054482 20.4888706207 1.64130461216 -3.29036569595 20.4888706207 1.65116846561 -3.24032926559 20.4888706207 1.66973567009 -3.02736520767 20.4888706207 1.68847417831 -2.81939387321 20.4888706207 1.68679702282 -2.76020956039 20.4888706207 1.66914308071 -2.41950082779 20.4888706207 1.64246082306 -2.23533558846 20.4888706207 1.60868883133 -2.02834272385 20.4888706207 1.51724696159 -1.71989154816 20.4888706207 1.49623572826 -1.65461325645 20.4888706207 1.42606317997 -1.491979599 20.4888706207 1.3567700386 -1.32842648029 20.4888706207 1.32507133484 -1.27246177197 20.4888706207 1.17276072502 -1.02050459385 20.4888706207 1.04098236561 -0.848644554615 20.4888706207 0.938948631287 -0.723256886005 20.4888706207 0.683209836483 -0.476590782404 20.4888706207 0.665813028812 -0.459587454796 20.4888706207 0.658516407013 -0.453999429941 20.4888706207 0.391479790211 -0.259664177895 20.4888706207 0.271537065506 -0.191292673349 20.4888706207 0.0717895030975 -0.0863012373447 20.4888706207 -0.184181436896 0.0233626328409 20.4888706207 -0.270721703768 0.0547737926245 20.4888706207 -0.507420361042 0.113573245704 20.4888706207 -0.620988667011 0.142277404666 20.4888706207 -0.654384613037 0.148016735911 20.4888706207 -0.890827834606 0.176687330008 20.4888706207 -1.04373645782 0.184305891395 20.4888706207 -1.1645411253 0.185675606132 20.4888706207 -1.44101190567 0.166292876005 20.4888706207 -1.44823217392 0.165832385421 20.4888706207 -1.45147001743 0.165359735489 20.4888706207 -1.83248019218 0.0894680172205 20.4888706207 -1.85974013805 0.0829570516944 20.4888706207 -1.94719099998 0.0521871075034 20.4888706207 -2.12172222137 -0.00829736050218 20.4888706207 -2.17346835136 -0.031822130084 20.4888706207 -2.37516188622 -0.12877382338 20.4888706207 -2.49845600128 -0.201467946172 20.4888706207 -2.61848306656 -0.277021229267 20.4888706207 -2.8381755352 -0.447264969349 20.4888706207 -2.9049384594 -0.502059519291 20.4888706207 -3.15235185623 -0.757498860359 20.4888706207 -3.15315914154 -0.758368134499 20.4888706207 -3.25064492226 -0.889469861984 20.4888706207 -3.25647759438 -0.885461568832 20.4888706207 -3.3813829422 -1.0680475235 20.4888706207 -3.37977743149 -1.06918287277 20.4888706207 -3.46443748474 -1.20774877071 20.4888706207 -3.46919417381 -1.20537281036 20.4888706207 -3.58937978745 -1.43821310997 20.4888706207 -3.58217191696 -1.44208788872 20.4888706207 -3.66026496887 -1.62540507317 20.4888706207 -3.66230154037 -1.62475931644 20.4888706207 -3.69841313362 -1.73253548145 20.4888706207 -3.69318652153 -1.7342633009 20.4888706207 -3.74681544304 -1.93582737446 20.4888706207 -3.75580716133 -1.93396759033 20.4888706207 -3.79573249817 -2.13097190857 20.4888706207 -3.79209780693 -2.13176488876 20.4888706207 -3.81871628761 -2.34429383278 20.4888706207 -3.82663631439 -2.34402489662 20.4888706207 -3.8424706459 -2.66858410835 20.4888706207 -3.83171963692 -2.66917777061 20.4888706207 -3.82731223106 -2.87059426308 20.4888706207 -3.82831072807 -2.87068104744 20.4888706207 -3.82536959648 -2.90732264519 20.4888706207 -3.82493162155 -2.90731787682 20.4888706207 -3.78142881393 -3.18082070351 20.4888706207 -3.78709340096 -3.18263030052 20.4888706207 -3.7263906002 -3.44176316261 20.4888706207 -3.72639107704 -3.44178771973 20.4888706207 -3.71010398865 -3.48987746239 20.4888706207 -3.70791745186 -3.48913359642 20.4888706207 -3.60823988914 -3.74311733246 20.4888706207 -3.62000823021 -3.74846434593 20.4888706207 -3.51574611664 -3.97706723213 20.4888706207 -3.51540017128 -3.97701978683 20.4888706207 -3.39739108086 -4.18059635162 20.4888706207 -3.39768862724 -4.18175983429 20.4888706207 -3.24234676361 -4.40235900879 20.4888706207 -3.24172949791 -4.40213489532 20.4888706207 -3.09608650208 -4.57671499252 20.4888706207 -3.09831142426 -4.57974290848 20.4888706207 -2.90703749657 -4.76988458633 20.4888706207 -2.90458250046 -4.76772069931 20.4888706207 -2.74272847176 -4.90388631821 20.4888706207 -2.74320054054 -4.9048037529 20.4888706207 -2.54560661316 -5.05166959763 20.4888706207 -2.54459667206 -5.05063915253 20.4888706207 -2.37614154816 -5.16104125977 20.4888706207 -2.37728261948 -5.16348075867 20.4888706207 -2.14940261841 -5.28507471085 20.4888706207 -2.14436006546 -5.27601718903 20.4888706207 -1.95474636555 -5.35893201828 20.4888706207 -1.95491576195 -5.35943031311 20.4888706207 -1.90081894398 -5.37901258469 20.4888706207 -1.89995789528 -5.37660455704 20.4888706207 -1.6949532032 -5.43986797333 20.4888706207 -1.69791650772 -5.452023983 20.4888706207 -1.48173093796 -5.50378894806 20.4888706207 -1.48106813431 -5.5015540123 20.4888706207 -1.26856207848 -5.5317158699 20.4888706207 -1.26853597164 -5.53724336624 20.4888706207 -0.973974466324 -5.55401754379 20.4888706207 -0.973512411118 -5.54732465744 20.4888706207 -0.781169354916 -5.54519224167 20.4888706207 -0.780779480934 -5.54892015457 20.4888706207 -0.585612893105 -5.53074932098 20.4888706207 -0.586571455002 -5.52058601379 20.4888706207 -0.420891404152 -5.49309301376 20.4888706207 -0.42053848505 -5.49470376968 20.4888706207 -0.377891212702 -5.48530721664 20.4888706207 -0.378043472767 -5.48461055756 20.4888706207 -0.214805856347 -5.4390668869 20.4888706207 -0.211321249604 -5.44881486893 20.4888706207 0.0450915321708 -5.35762405396 20.4888706207 0.0423072762787 -5.34985351562 20.4888706207 0.183920025826 -5.28877830505 20.4888706207 0.185105055571 -5.29111099243 20.4888706207 0.351953327656 -5.20585346222 20.4888706207 0.347644656897 -5.19732046127 20.4888706207 0.48329180479 -5.11519241333 20.4888706207 0.483554154634 -5.11556434631 20.4888706207 0.572388350964 -5.0535068512 20.4888706207 0.568798184395 -5.04837989807 20.4888706207 0.73655885458 -4.91555213928 20.4888706207 0.742326319218 -4.92184829712 20.4888706207 0.832007825375 -4.83951425552 20.4888706207 0.831655323505 -4.8391160965 20.4888706207 0.972522497177 -4.68518733978 20.4888706207 0.979896724224 -4.69078683853 20.4888706207 1.11994349957 -4.51375198364 20.4888706207 1.11893522739 -4.51293325424 20.4888706207 1.21829009056 -4.36798143387 20.4888706207 1.22486054897 -4.3717341423 20.4888706207 1.33151340485 -4.1904001236 20.4888706207 1.32889521122 -4.18881177902 20.4888706207 1.40749526024 -4.03095340729 20.4888706207 1.41209542751 -4.03281784058 20.4888706207 1.49518942833 -3.8359079361 20.4888706207 1.49107265472 -3.83412265778 20.4888706207 1.55008113384 -3.66678190231 20.4888706207 1.55391001701 -3.66781711578 20.4888706207 1.61682450771 -3.44370913506 20.4888706207 1.60981941223 -3.44162631035 20.4888706207 1.64650070667 -3.26540255547 20.4888706207 1.64759516716 -3.26554012299 20.4888706207 1.66614675522 -3.13465428352 20.4888706207 1.66029798985 -3.13383364677 20.4888706207 1.67895448208 -2.92336630821 20.4888706207 1.68526828289 -2.92356920242 20.4888706207 1.68938314915 -2.78985548019 20.4888706207 1.68798220158 -2.78978776932 20.4888706207 1.67996752262 -2.58977508545 20.4888706207 1.68578612804 -2.58908820152 20.4888706207 1.6600651741 -2.32699990273 20.4888706207 1.65662264824 -2.32729172707 20.4888706207 1.62649977207 -2.13169670105 20.4888706207 1.63203465939 -2.13036084175 20.4888706207 1.57287704945 -1.8718495369 20.4888706207 1.56475043297 -1.87356615067 20.4888706207 1.50712132454 -1.68713498116 20.4888706207 1.50828051567 -1.68667411804 20.4888706207 1.46512508392 -1.57180285454 20.4888706207 1.46088087559 -1.57341122627 20.4888706207 1.3911472559 -1.41031825542 20.4888706207 1.39597868919 -1.40795385838 20.4888706207 1.34257256985 -1.29962968826 20.4888706207 1.34131443501 -1.30021369457 20.4888706207 1.2507185936 -1.14542818069 20.4888706207 1.25562548637 -1.14190340042 20.4888706207 1.11180686951 -0.931205809116 20.4888706207 1.10809993744 -0.933604180813 20.4888706207 0.990882396698 -0.785226345062 20.4888706207 0.993545532227 -0.782660365105 20.4888706207 0.818947970867 -0.592691779137 20.4888706207 0.810669660568 -0.600345671177 20.4888706207 0.674483418465 -0.468118011951 20.4888706207 0.674990534782 -0.467535972595 20.4888706207 0.662345707417 -0.456584453583 20.4888706207 0.66219830513 -0.456748425961 20.4888706207 0.526205956936 -0.355213999748 20.4888706207 0.529969453812 -0.349136859179 20.4888706207 0.333586633205 -0.222261697054 20.4888706207 0.332074731588 -0.224444299936 20.4888706207 0.172588899732 -0.137106701732 20.4888706207 0.173582673073 -0.134765520692 20.4888706207 -0.0538273714483 -0.026494352147 20.4888706207 -0.0547478124499 -0.0278061442077 20.4888706207 -0.226972803473 0.0402792729437 20.4888706207 -0.226749405265 0.041372101754 20.4888706207 -0.38721087575 0.0902768671513 20.4888706207 -0.389131456614 0.083932377398 20.4888706207 -0.564233541489 0.127809509635 20.4888706207 -0.563734829426 0.13014125824 20.4888706207 -0.637550771236 0.145788088441 20.4888706207 -0.637625873089 0.145562157035 20.4888706207 -0.772178888321 0.165269553661 20.4888706207 -0.77224701643 0.166556626558 20.4888706207 -0.967051267624 0.183199316263 20.4888706207 -0.967237174511 0.181967318058 20.4888706207 -1.1041033268 0.186151310802 20.4888706207 -1.10421085358 0.18744546175 20.4888706207 -1.30294167995 0.18161547184 20.4888706207 -1.30274736881 0.175548568368 20.4888706207 -1.44462132454 0.166051253676 20.4888706207 -1.44463729858 0.166208773851 20.4888706207 -1.44985806942 0.165662154555 20.4888706207 -1.44985830784 0.16563770175 20.4888706207 -1.64282774925 0.132357686758 20.4888706207 -1.64276111126 0.131004020572 20.4888706207 -1.84616684914 0.0864715352654 20.4888706207 -1.84631562233 0.086910367012 20.4888706207 -1.90414535999 0.0698803588748 20.4888706207 -1.90342950821 0.0674687102437 20.4888706207 -2.03438472748 0.0217389147729 20.4888706207 -2.03605103493 0.0259351860732 20.4888706207 -2.1480858326 -0.0188317988068 20.4888706207 -2.14772415161 -0.0197843890637 20.4888706207 -2.27482199669 -0.0792139172554 20.4888706207 -2.27654528618 -0.0761211514473 20.4888706207 -2.4382352829 -0.162449464202 20.4888706207 -2.43735027313 -0.16423265636 20.4888706207 -2.5590057373 -0.238364443183 20.4888706207 -2.56044960022 -0.236414596438 20.4888706207 -2.73220992088 -0.356597214937 20.4888706207 -2.7295422554 -0.360622316599 20.4888706207 -2.87193393707 -0.474189639091 20.4888706207 -2.8732252121 -0.472851008177 20.4888706207 -3.03551363945 -0.622321665287 20.4888706207 -3.02999949455 -0.628494620323 20.4888706207 -3.15276002884 -0.757929205894 20.4888706207 -3.15278100967 -0.757912397385 20.4888706207 -3.1541659832 -0.75958108902 20.4888706207 -3.15409517288 -0.759639441967 pyformex-0.8.6/pyformex/core.py0000644000211500021150000000316111705103030016374 0ustar benebene00000000000000# $Id$ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """pyFormex core module ## EXPERIMENATL : DO NOT USE The :mod:`core` module contains all the definitions which are considered to be the pyFormex Geometry Script Langauge. These definitions will be imported automatically in all executed scripts and can be imported elsewhere with:: from pyformex.core import * """ from numpy import * from arraytools import * from coords import * from formex import * from mesh import Mesh from plugins.curve import * from plugins.trisurface import TriSurface ### End pyformex-0.8.6/pyformex/filewrite.py0000644000211500021150000001444111705104656017460 0ustar benebene00000000000000# $Id: filewrite.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Write geometry to file in a whole number of formats. This module defines bothe the basic routines to write geometrical data to a file and the specialized exporters to write files in a number of well known standardized formats. The basic routines are very versatile as well as optimized (using the version in the pyFormex C-library) and allow to easily create new exporters for other formats. """ import pyformex as pf import numpy as np from lib import misc def writeData(data,fil,fmt=' '): """Write an array of numerical data to an open file. Parameters: - `data`: a numerical array of int or float type - `fil`: an open file object - `fmt`: a format string defining how a single data item is written. It should be one of: - '': an empty string: in this case the data are written in binary mode, using the function numpy.tofile. - ' ': a single space: in this case the data are written in text mode, separated by a space, also using the function numpy.tofile. At the end, a newline is added. All the data of the array thus appear on a single line. - a format string compatible with the array data type. In this case float arrays will be forced to float32 and int arrays to int32. The format string should contain a valid format converter for a a single data item in both Python and C. They should also contain the necessary spacing or separator. Examples are '%5i ' for int data and '%f,' or '%10.3e' for float data. The array will be converted to a 2D array, keeping the lengt of the last axis. The all elements will be written row by row using the specified format string, and a newline character will be written after each row. This mode is written by pyFormex function misc.tofile_int32 or misc.tofile_float32, which have accelerated versions in the pyFormex C library. """ kind = data.dtype.kind if fmt == '' or fmt == ' ': data.tofile(fil,sep=fmt) if fmt == ' ': fil.write('\n') else: val = data.reshape(-1,data.shape[-1]) if kind == 'i': misc.tofile_int32(val.astype(np.int32),fil,fmt) elif kind == 'f': misc.tofile_float32(val.astype(np.float32),fil,fmt) else: raise ValueError,"Can not write data fo type %s" % data.dtype def writeIData(data,fil,fmt,ind=1): """Write an indexed array of numerical data to an open file. ind = i: autoindex from i array: use these indices """ kind = data.dtype.kind val = data.reshape(-1,data.shape[-1]) nrows = val.shape[0] if type(ind) is int: ind = ind + np.arange(nrows) else: ind = ind.reshape(-1) if ind.shape[0] != nrows: raise ValueError,"Index should have same length as data" if kind == 'i': raise ImplementationError misc.tofile_int32(val.astype(np.int32),fil,fmt) elif kind == 'f': misc.tofile_ifloat32(ind.astype(np.int32),val.astype(np.float32),fil,fmt) else: raise ValueError,"Can not write data fo type %s" % data.dtype # Output of mesh file formats def writeOFF(fn,coords,elems): """Write a mesh of polygons to a file in OFF format. Parameters: - `fn`: file name, by preference ending on '.off' - `coords`: float array with shape (ncoords,3), with the coordinates of `ncoords` vertices. - `elems`: int array with shape (nelems,nplex), with the definition of `nelems` polygon elements. """ if coords.dtype.kind != 'f' or coords.ndim != 2 or coords.shape[1] != 3 or elems.dtype.kind != 'i' or elems.ndim != 2: raise runtimeError, "Invalid type or shape of argument(s)" fil = open(fn,'w') fil.write("OFF\n") fil.write("%d %d 0\n" % (coords.shape[0],elems.shape[0])) writeData(coords,fil,'%f ') nelems = np.zeros_like(elems[:,:1]) nelems.fill(elems.shape[1]) elemdata = np.column_stack([nelems,elems]) writeData(elemdata,fil,'%i ') fil.close() # Output of surface file formats def writeGTS(fn,coords,edges,faces): """Write a mesh of triangles to a file in GTS format. Parameters: - `fn`: file name, by preference ending on '.gts' - `coords`: float array with shape (ncoords,3), with the coordinates of `ncoords` vertices. - `edges`: int array with shape (nedges,2), with the definition of `nedges` edges in function of the vertex indices. - `faces`: int array with shape (nfaces,3), with the definition of `nfaces` triangles in function of the edge indices. """ if coords.dtype.kind != 'f' or coords.ndim != 2 or coords.shape[1] != 3 or edges.dtype.kind != 'i' or edges.ndim != 2 or edges.shape[1] != 2 or faces.dtype.kind != 'i' or faces.ndim != 2 or faces.shape[1] != 3: raise runtimeError, "Invalid type or shape of argument(s)" fil = open(fn,'w') fil.write("%d %d %d\n" % (coords.shape[0],edges.shape[0],faces.shape[0])) writeData(coords,fil,'%f ') writeData(edges+1,fil,'%i ') writeData(faces+1,fil,'%i ') fil.write("#GTS file written by %s\n" % pf.Version) fil.close() # End pyformex-0.8.6/pyformex/sendmail.py0000644000211500021150000001035011705104656017255 0ustar benebene00000000000000#!/usr/bin/python # $Id: sendmail.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """sendmail.py: a simple program to send an email message (C) 2008 Benedict Verhegghe (benedict.verhegghe@ugent.be) I wrote this software in my free time, for my joy, not as a commissioned task. Any copyright claims made by my employer should therefore be considered void. Distributed under the GNU General Public License, version 3 or later """ import os,socket,getpass ################### global data ################################## host = socket.gethostname() user = getpass.getuser() mail = os.environ.get('MAIL',"%s@%s" % (user,host)) ################### mail access ################################## import smtplib import email.Message def message(sender='',to='',cc='',subject='',text=''): """Create an email message 'to' and 'cc' can be lists of email addresses. """ if type(to) is list: to = ', '.join(to) if type(cc) is list: cc = ', '.join(cc) message = email.Message.Message() message["From"] = sender message["To"] = to if cc: message["Cc"] = cc message["Subject"] = subject message.set_payload(text) return message def sendmail(message,sender,to,serverURL='localhost'): """Send an email message 'message' is an email message (e.g. returned by message()) 'sender' is a single mail address 'to' can be a list of addresses """ mailServer = smtplib.SMTP(serverURL) mailServer.sendmail(sender,to,message.as_string()) mailServer.quit() ################################################################## def input_message(prompt=True): print(""" This is Bene's simple mail program, version 0.00001. Enter lines of text, end with CTRL-D (on a blank line). Include at least one line starting with 'To:' Include exactly one line starting with 'Subj:' Optionally include a line starting with 'From:' Optionally include one or more lines starting with 'CC:' All other lines will be the text of your message. """) to = [] cc = [] subj = '' msg = '' sender = '' while True: try: s = raw_input() slower = s[:5].lower() if slower.startswith('to:'): to.append(s[3:]) elif slower.startswith('cc:'): cc.append(s[3:]) elif slower.startswith('subj:'): subj = s[5:] elif slower.startswith('from:'): sender = s[5:] else: msg += s+'\n' except EOFError: break return to,cc,subj,msg,sender if __name__ == '__main__': to,cc,subj,msg,sender = input_message() if not sender: sender = mail if to and subj and msg and sender: msg = message(sender,to,cc,subj,msg) print("\n\n Email message:") print(msg) if raw_input('\n Shall I send the email now? (y/n)') == 'y': sendmail(msg,sender,to) print("Mail has been sent!") else: print("Mail not sent!") else: print("Message can not be sent because of missing fields!") # End pyformex-0.8.6/pyformex/extra/0000755000211500021150000000000011705105304016222 5ustar benebene00000000000000pyformex-0.8.6/pyformex/extra/gts/0000755000211500021150000000000011705105304017017 5ustar benebene00000000000000pyformex-0.8.6/pyformex/extra/gts/README0000644000211500021150000000375411700622767017724 0ustar benebene00000000000000# ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # Installation procedure for GTS library and utilities ==================================================== The 'TriSurface' objects in pyFormex can make use of some powerful fetatures of the GTS (GNU Triangulated Surfaces) library. While the Linux distribution of your choice may provide a package for this library, in most cases it will not install the binaries required for pyFormex. The gts.install procedure in this directory will download, compile and install the GTS library and all that is needed to let it cooperate with pyFormex. At the same time it will fix some bugs. By default the installation is done under /usr/local and you will need root privileges to do so. Also, you will need to have the glib2 header files (on Debian and alikes: install package libglib2.0-dev. The full installation can be done with a single command: (sudo) ./install.sh all Dependencies: libglib2.0-dev pyformex-0.8.6/pyformex/extra/gts/install.sh0000755000211500021150000000540311700622767021042 0ustar benebene00000000000000#!/bin/bash # $Id: install.sh 1583 2010-10-11 16:49:51Z bverheg $ ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # # This script helps with installing the gts library and utilities from source: # # Prerequisites: libglib2.0-dev # # TO INSTALL: # ./install.sh get unpack patch make # sudo ./install.sh install rename # ./install.sh clean # # OR: ./install.sh all TO DO IT ALL AT ONCE # # Use at your own risk if you do not understand what is happening! # VERSION=0.7.6 NAME=gts-$VERSION ARCHIVE=$NAME.tar.gz URI=http://prdownloads.sourceforge.net/gts/$ARCHIVE examples="cartesian cleanup coarsen delaunay gtstoc iso merge oocs optimize partition refine set smooth sphere split stripe transform traverse volume" _get() { [ -f $ARCHIVE ] || wget $URI } _unpack() { rm -rf $NAME tar xvzf $ARCHIVE } _make() { pushd $NAME ./configure make popd } _install() { [ "$EUID" == "0" ] || { echo "install should be done as root!" return } pushd $NAME make install ldconfig popd } _patch() { pushd $NAME patch -p0 < ../gts-0.7.6-bv.patch popd } # !! Only examples delaunay and transform are installed. # Better copy from build directory _rename() { [ "$EUID" == "0" ] || { echo "install should be done as root!" return } for name in $examples; do gtsname=gts${name#gts} src=/usr/local/bin/$name tgt=/usr/local/bin/$gtsname [ -f "$src" -a "$src" != "$tgt" ] && mv $src $tgt done } _clean() { rm -rf $NAME rm -f $ARCHIVE } for cmd in "$@"; do case $cmd in get | unpack | patch | make | install | rename | clean ) _$cmd;; all ) _get;_unpack;_patch;_make;_install;_rename;_clean;; * ) echo "UNKNOWN command $cmd";; esac done pyformex-0.8.6/pyformex/extra/pygl2ps/0000755000211500021150000000000011705105304017622 5ustar benebene00000000000000pyformex-0.8.6/pyformex/extra/pygl2ps/gl2ps_wrap.c0000644000211500021150000042506211700622767022073 0ustar benebene00000000000000/* ---------------------------------------------------------------------------- * This file was automatically generated by SWIG (http://www.swig.org). * Version 2.0.4 * * This file is not intended to be easily readable and contains a number of * coding conventions designed to improve portability and efficiency. Do not make * changes to this file unless you know what you are doing--modify the SWIG * interface file instead. * ----------------------------------------------------------------------------- */ #define SWIGPYTHON #define SWIG_PYTHON_DIRECTOR_NO_VTABLE /* ----------------------------------------------------------------------------- * This section contains generic SWIG labels for method/variable * declarations/attributes, and other compiler dependent labels. * ----------------------------------------------------------------------------- */ /* template workaround for compilers that cannot correctly implement the C++ standard */ #ifndef SWIGTEMPLATEDISAMBIGUATOR # if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) # define SWIGTEMPLATEDISAMBIGUATOR template # elif defined(__HP_aCC) /* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ /* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ # define SWIGTEMPLATEDISAMBIGUATOR template # else # define SWIGTEMPLATEDISAMBIGUATOR # endif #endif /* inline attribute */ #ifndef SWIGINLINE # if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) # define SWIGINLINE inline # else # define SWIGINLINE # endif #endif /* attribute recognised by some compilers to avoid 'unused' warnings */ #ifndef SWIGUNUSED # if defined(__GNUC__) # if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) # define SWIGUNUSED __attribute__ ((__unused__)) # else # define SWIGUNUSED # endif # elif defined(__ICC) # define SWIGUNUSED __attribute__ ((__unused__)) # else # define SWIGUNUSED # endif #endif #ifndef SWIG_MSC_UNSUPPRESS_4505 # if defined(_MSC_VER) # pragma warning(disable : 4505) /* unreferenced local function has been removed */ # endif #endif #ifndef SWIGUNUSEDPARM # ifdef __cplusplus # define SWIGUNUSEDPARM(p) # else # define SWIGUNUSEDPARM(p) p SWIGUNUSED # endif #endif /* internal SWIG method */ #ifndef SWIGINTERN # define SWIGINTERN static SWIGUNUSED #endif /* internal inline SWIG method */ #ifndef SWIGINTERNINLINE # define SWIGINTERNINLINE SWIGINTERN SWIGINLINE #endif /* exporting methods */ #if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) # ifndef GCC_HASCLASSVISIBILITY # define GCC_HASCLASSVISIBILITY # endif #endif #ifndef SWIGEXPORT # if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) # if defined(STATIC_LINKED) # define SWIGEXPORT # else # define SWIGEXPORT __declspec(dllexport) # endif # else # if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) # define SWIGEXPORT __attribute__ ((visibility("default"))) # else # define SWIGEXPORT # endif # endif #endif /* calling conventions for Windows */ #ifndef SWIGSTDCALL # if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) # define SWIGSTDCALL __stdcall # else # define SWIGSTDCALL # endif #endif /* Deal with Microsoft's attempt at deprecating C standard runtime functions */ #if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) # define _CRT_SECURE_NO_DEPRECATE #endif /* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ #if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) # define _SCL_SECURE_NO_DEPRECATE #endif /* Python.h has to appear first */ #include /* ----------------------------------------------------------------------------- * swigrun.swg * * This file contains generic C API SWIG runtime support for pointer * type checking. * ----------------------------------------------------------------------------- */ /* This should only be incremented when either the layout of swig_type_info changes, or for whatever reason, the runtime changes incompatibly */ #define SWIG_RUNTIME_VERSION "4" /* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ #ifdef SWIG_TYPE_TABLE # define SWIG_QUOTE_STRING(x) #x # define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) # define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) #else # define SWIG_TYPE_TABLE_NAME #endif /* You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for creating a static or dynamic library from the SWIG runtime code. In 99.9% of the cases, SWIG just needs to declare them as 'static'. But only do this if strictly necessary, ie, if you have problems with your compiler or suchlike. */ #ifndef SWIGRUNTIME # define SWIGRUNTIME SWIGINTERN #endif #ifndef SWIGRUNTIMEINLINE # define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE #endif /* Generic buffer size */ #ifndef SWIG_BUFFER_SIZE # define SWIG_BUFFER_SIZE 1024 #endif /* Flags for pointer conversions */ #define SWIG_POINTER_DISOWN 0x1 #define SWIG_CAST_NEW_MEMORY 0x2 /* Flags for new pointer objects */ #define SWIG_POINTER_OWN 0x1 /* Flags/methods for returning states. The SWIG conversion methods, as ConvertPtr, return an integer that tells if the conversion was successful or not. And if not, an error code can be returned (see swigerrors.swg for the codes). Use the following macros/flags to set or process the returning states. In old versions of SWIG, code such as the following was usually written: if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) { // success code } else { //fail code } Now you can be more explicit: int res = SWIG_ConvertPtr(obj,vptr,ty.flags); if (SWIG_IsOK(res)) { // success code } else { // fail code } which is the same really, but now you can also do Type *ptr; int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags); if (SWIG_IsOK(res)) { // success code if (SWIG_IsNewObj(res) { ... delete *ptr; } else { ... } } else { // fail code } I.e., now SWIG_ConvertPtr can return new objects and you can identify the case and take care of the deallocation. Of course that also requires SWIG_ConvertPtr to return new result values, such as int SWIG_ConvertPtr(obj, ptr,...) { if () { if () { *ptr = ; return SWIG_NEWOBJ; } else { *ptr = ; return SWIG_OLDOBJ; } } else { return SWIG_BADOBJ; } } Of course, returning the plain '0(success)/-1(fail)' still works, but you can be more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the SWIG errors code. Finally, if the SWIG_CASTRANK_MODE is enabled, the result code allows to return the 'cast rank', for example, if you have this int food(double) int fooi(int); and you call food(1) // cast rank '1' (1 -> 1.0) fooi(1) // cast rank '0' just use the SWIG_AddCast()/SWIG_CheckState() */ #define SWIG_OK (0) #define SWIG_ERROR (-1) #define SWIG_IsOK(r) (r >= 0) #define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError) /* The CastRankLimit says how many bits are used for the cast rank */ #define SWIG_CASTRANKLIMIT (1 << 8) /* The NewMask denotes the object was created (using new/malloc) */ #define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1) /* The TmpMask is for in/out typemaps that use temporal objects */ #define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1) /* Simple returning values */ #define SWIG_BADOBJ (SWIG_ERROR) #define SWIG_OLDOBJ (SWIG_OK) #define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK) #define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK) /* Check, add and del mask methods */ #define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r) #define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r) #define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK)) #define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r) #define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r) #define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK)) /* Cast-Rank Mode */ #if defined(SWIG_CASTRANK_MODE) # ifndef SWIG_TypeRank # define SWIG_TypeRank unsigned long # endif # ifndef SWIG_MAXCASTRANK /* Default cast allowed */ # define SWIG_MAXCASTRANK (2) # endif # define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1) # define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK) SWIGINTERNINLINE int SWIG_AddCast(int r) { return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r; } SWIGINTERNINLINE int SWIG_CheckState(int r) { return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0; } #else /* no cast-rank mode */ # define SWIG_AddCast # define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0) #endif #include #ifdef __cplusplus extern "C" { #endif typedef void *(*swig_converter_func)(void *, int *); typedef struct swig_type_info *(*swig_dycast_func)(void **); /* Structure to store information on one type */ typedef struct swig_type_info { const char *name; /* mangled name of this type */ const char *str; /* human readable name of this type */ swig_dycast_func dcast; /* dynamic cast function down a hierarchy */ struct swig_cast_info *cast; /* linked list of types that can cast into this type */ void *clientdata; /* language specific type data */ int owndata; /* flag if the structure owns the clientdata */ } swig_type_info; /* Structure to store a type and conversion function used for casting */ typedef struct swig_cast_info { swig_type_info *type; /* pointer to type that is equivalent to this type */ swig_converter_func converter; /* function to cast the void pointers */ struct swig_cast_info *next; /* pointer to next cast in linked list */ struct swig_cast_info *prev; /* pointer to the previous cast */ } swig_cast_info; /* Structure used to store module information * Each module generates one structure like this, and the runtime collects * all of these structures and stores them in a circularly linked list.*/ typedef struct swig_module_info { swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */ size_t size; /* Number of types in this module */ struct swig_module_info *next; /* Pointer to next element in circularly linked list */ swig_type_info **type_initial; /* Array of initially generated type structures */ swig_cast_info **cast_initial; /* Array of initially generated casting structures */ void *clientdata; /* Language specific module data */ } swig_module_info; /* Compare two type names skipping the space characters, therefore "char*" == "char *" and "Class" == "Class", etc. Return 0 when the two name types are equivalent, as in strncmp, but skipping ' '. */ SWIGRUNTIME int SWIG_TypeNameComp(const char *f1, const char *l1, const char *f2, const char *l2) { for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { while ((*f1 == ' ') && (f1 != l1)) ++f1; while ((*f2 == ' ') && (f2 != l2)) ++f2; if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1; } return (int)((l1 - f1) - (l2 - f2)); } /* Check type equivalence in a name list like ||... Return 0 if not equal, 1 if equal */ SWIGRUNTIME int SWIG_TypeEquiv(const char *nb, const char *tb) { int equiv = 0; const char* te = tb + strlen(tb); const char* ne = nb; while (!equiv && *ne) { for (nb = ne; *ne; ++ne) { if (*ne == '|') break; } equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0; if (*ne) ++ne; } return equiv; } /* Check type equivalence in a name list like ||... Return 0 if equal, -1 if nb < tb, 1 if nb > tb */ SWIGRUNTIME int SWIG_TypeCompare(const char *nb, const char *tb) { int equiv = 0; const char* te = tb + strlen(tb); const char* ne = nb; while (!equiv && *ne) { for (nb = ne; *ne; ++ne) { if (*ne == '|') break; } equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0; if (*ne) ++ne; } return equiv; } /* Check the typename */ SWIGRUNTIME swig_cast_info * SWIG_TypeCheck(const char *c, swig_type_info *ty) { if (ty) { swig_cast_info *iter = ty->cast; while (iter) { if (strcmp(iter->type->name, c) == 0) { if (iter == ty->cast) return iter; /* Move iter to the top of the linked list */ iter->prev->next = iter->next; if (iter->next) iter->next->prev = iter->prev; iter->next = ty->cast; iter->prev = 0; if (ty->cast) ty->cast->prev = iter; ty->cast = iter; return iter; } iter = iter->next; } } return 0; } /* Identical to SWIG_TypeCheck, except strcmp is replaced with a pointer comparison */ SWIGRUNTIME swig_cast_info * SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *ty) { if (ty) { swig_cast_info *iter = ty->cast; while (iter) { if (iter->type == from) { if (iter == ty->cast) return iter; /* Move iter to the top of the linked list */ iter->prev->next = iter->next; if (iter->next) iter->next->prev = iter->prev; iter->next = ty->cast; iter->prev = 0; if (ty->cast) ty->cast->prev = iter; ty->cast = iter; return iter; } iter = iter->next; } } return 0; } /* Cast a pointer up an inheritance hierarchy */ SWIGRUNTIMEINLINE void * SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) { return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory); } /* Dynamic pointer casting. Down an inheritance hierarchy */ SWIGRUNTIME swig_type_info * SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { swig_type_info *lastty = ty; if (!ty || !ty->dcast) return ty; while (ty && (ty->dcast)) { ty = (*ty->dcast)(ptr); if (ty) lastty = ty; } return lastty; } /* Return the name associated with this type */ SWIGRUNTIMEINLINE const char * SWIG_TypeName(const swig_type_info *ty) { return ty->name; } /* Return the pretty name associated with this type, that is an unmangled type name in a form presentable to the user. */ SWIGRUNTIME const char * SWIG_TypePrettyName(const swig_type_info *type) { /* The "str" field contains the equivalent pretty names of the type, separated by vertical-bar characters. We choose to print the last name, as it is often (?) the most specific. */ if (!type) return NULL; if (type->str != NULL) { const char *last_name = type->str; const char *s; for (s = type->str; *s; s++) if (*s == '|') last_name = s+1; return last_name; } else return type->name; } /* Set the clientdata field for a type */ SWIGRUNTIME void SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { swig_cast_info *cast = ti->cast; /* if (ti->clientdata == clientdata) return; */ ti->clientdata = clientdata; while (cast) { if (!cast->converter) { swig_type_info *tc = cast->type; if (!tc->clientdata) { SWIG_TypeClientData(tc, clientdata); } } cast = cast->next; } } SWIGRUNTIME void SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) { SWIG_TypeClientData(ti, clientdata); ti->owndata = 1; } /* Search for a swig_type_info structure only by mangled name Search is a O(log #types) We start searching at module start, and finish searching when start == end. Note: if start == end at the beginning of the function, we go all the way around the circular list. */ SWIGRUNTIME swig_type_info * SWIG_MangledTypeQueryModule(swig_module_info *start, swig_module_info *end, const char *name) { swig_module_info *iter = start; do { if (iter->size) { register size_t l = 0; register size_t r = iter->size - 1; do { /* since l+r >= 0, we can (>> 1) instead (/ 2) */ register size_t i = (l + r) >> 1; const char *iname = iter->types[i]->name; if (iname) { register int compare = strcmp(name, iname); if (compare == 0) { return iter->types[i]; } else if (compare < 0) { if (i) { r = i - 1; } else { break; } } else if (compare > 0) { l = i + 1; } } else { break; /* should never happen */ } } while (l <= r); } iter = iter->next; } while (iter != end); return 0; } /* Search for a swig_type_info structure for either a mangled name or a human readable name. It first searches the mangled names of the types, which is a O(log #types) If a type is not found it then searches the human readable names, which is O(#types). We start searching at module start, and finish searching when start == end. Note: if start == end at the beginning of the function, we go all the way around the circular list. */ SWIGRUNTIME swig_type_info * SWIG_TypeQueryModule(swig_module_info *start, swig_module_info *end, const char *name) { /* STEP 1: Search the name field using binary search */ swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name); if (ret) { return ret; } else { /* STEP 2: If the type hasn't been found, do a complete search of the str field (the human readable name) */ swig_module_info *iter = start; do { register size_t i = 0; for (; i < iter->size; ++i) { if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name))) return iter->types[i]; } iter = iter->next; } while (iter != end); } /* neither found a match */ return 0; } /* Pack binary data into a string */ SWIGRUNTIME char * SWIG_PackData(char *c, void *ptr, size_t sz) { static const char hex[17] = "0123456789abcdef"; register const unsigned char *u = (unsigned char *) ptr; register const unsigned char *eu = u + sz; for (; u != eu; ++u) { register unsigned char uu = *u; *(c++) = hex[(uu & 0xf0) >> 4]; *(c++) = hex[uu & 0xf]; } return c; } /* Unpack binary data from a string */ SWIGRUNTIME const char * SWIG_UnpackData(const char *c, void *ptr, size_t sz) { register unsigned char *u = (unsigned char *) ptr; register const unsigned char *eu = u + sz; for (; u != eu; ++u) { register char d = *(c++); register unsigned char uu; if ((d >= '0') && (d <= '9')) uu = ((d - '0') << 4); else if ((d >= 'a') && (d <= 'f')) uu = ((d - ('a'-10)) << 4); else return (char *) 0; d = *(c++); if ((d >= '0') && (d <= '9')) uu |= (d - '0'); else if ((d >= 'a') && (d <= 'f')) uu |= (d - ('a'-10)); else return (char *) 0; *u = uu; } return c; } /* Pack 'void *' into a string buffer. */ SWIGRUNTIME char * SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) { char *r = buff; if ((2*sizeof(void *) + 2) > bsz) return 0; *(r++) = '_'; r = SWIG_PackData(r,&ptr,sizeof(void *)); if (strlen(name) + 1 > (bsz - (r - buff))) return 0; strcpy(r,name); return buff; } SWIGRUNTIME const char * SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) { if (*c != '_') { if (strcmp(c,"NULL") == 0) { *ptr = (void *) 0; return name; } else { return 0; } } return SWIG_UnpackData(++c,ptr,sizeof(void *)); } SWIGRUNTIME char * SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) { char *r = buff; size_t lname = (name ? strlen(name) : 0); if ((2*sz + 2 + lname) > bsz) return 0; *(r++) = '_'; r = SWIG_PackData(r,ptr,sz); if (lname) { strncpy(r,name,lname+1); } else { *r = 0; } return buff; } SWIGRUNTIME const char * SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) { if (*c != '_') { if (strcmp(c,"NULL") == 0) { memset(ptr,0,sz); return name; } else { return 0; } } return SWIG_UnpackData(++c,ptr,sz); } #ifdef __cplusplus } #endif /* Errors in SWIG */ #define SWIG_UnknownError -1 #define SWIG_IOError -2 #define SWIG_RuntimeError -3 #define SWIG_IndexError -4 #define SWIG_TypeError -5 #define SWIG_DivisionByZero -6 #define SWIG_OverflowError -7 #define SWIG_SyntaxError -8 #define SWIG_ValueError -9 #define SWIG_SystemError -10 #define SWIG_AttributeError -11 #define SWIG_MemoryError -12 #define SWIG_NullReferenceError -13 /* Compatibility macros for Python 3 */ #if PY_VERSION_HEX >= 0x03000000 #define PyClass_Check(obj) PyObject_IsInstance(obj, (PyObject *)&PyType_Type) #define PyInt_Check(x) PyLong_Check(x) #define PyInt_AsLong(x) PyLong_AsLong(x) #define PyInt_FromLong(x) PyLong_FromLong(x) #define PyString_Check(name) PyBytes_Check(name) #define PyString_FromString(x) PyUnicode_FromString(x) #define PyString_Format(fmt, args) PyUnicode_Format(fmt, args) #define PyString_AsString(str) PyBytes_AsString(str) #define PyString_Size(str) PyBytes_Size(str) #define PyString_InternFromString(key) PyUnicode_InternFromString(key) #define Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_BASETYPE #define PyString_AS_STRING(x) PyUnicode_AS_STRING(x) #define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x) #endif #ifndef Py_TYPE # define Py_TYPE(op) ((op)->ob_type) #endif /* SWIG APIs for compatibility of both Python 2 & 3 */ #if PY_VERSION_HEX >= 0x03000000 # define SWIG_Python_str_FromFormat PyUnicode_FromFormat #else # define SWIG_Python_str_FromFormat PyString_FromFormat #endif /* Warning: This function will allocate a new string in Python 3, * so please call SWIG_Python_str_DelForPy3(x) to free the space. */ SWIGINTERN char* SWIG_Python_str_AsChar(PyObject *str) { #if PY_VERSION_HEX >= 0x03000000 char *cstr; char *newstr; Py_ssize_t len; str = PyUnicode_AsUTF8String(str); PyBytes_AsStringAndSize(str, &cstr, &len); newstr = (char *) malloc(len+1); memcpy(newstr, cstr, len+1); Py_XDECREF(str); return newstr; #else return PyString_AsString(str); #endif } #if PY_VERSION_HEX >= 0x03000000 # define SWIG_Python_str_DelForPy3(x) free( (void*) (x) ) #else # define SWIG_Python_str_DelForPy3(x) #endif SWIGINTERN PyObject* SWIG_Python_str_FromChar(const char *c) { #if PY_VERSION_HEX >= 0x03000000 return PyUnicode_FromString(c); #else return PyString_FromString(c); #endif } /* Add PyOS_snprintf for old Pythons */ #if PY_VERSION_HEX < 0x02020000 # if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM) # define PyOS_snprintf _snprintf # else # define PyOS_snprintf snprintf # endif #endif /* A crude PyString_FromFormat implementation for old Pythons */ #if PY_VERSION_HEX < 0x02020000 #ifndef SWIG_PYBUFFER_SIZE # define SWIG_PYBUFFER_SIZE 1024 #endif static PyObject * PyString_FromFormat(const char *fmt, ...) { va_list ap; char buf[SWIG_PYBUFFER_SIZE * 2]; int res; va_start(ap, fmt); res = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); return (res < 0 || res >= (int)sizeof(buf)) ? 0 : PyString_FromString(buf); } #endif /* Add PyObject_Del for old Pythons */ #if PY_VERSION_HEX < 0x01060000 # define PyObject_Del(op) PyMem_DEL((op)) #endif #ifndef PyObject_DEL # define PyObject_DEL PyObject_Del #endif /* A crude PyExc_StopIteration exception for old Pythons */ #if PY_VERSION_HEX < 0x02020000 # ifndef PyExc_StopIteration # define PyExc_StopIteration PyExc_RuntimeError # endif # ifndef PyObject_GenericGetAttr # define PyObject_GenericGetAttr 0 # endif #endif /* Py_NotImplemented is defined in 2.1 and up. */ #if PY_VERSION_HEX < 0x02010000 # ifndef Py_NotImplemented # define Py_NotImplemented PyExc_RuntimeError # endif #endif /* A crude PyString_AsStringAndSize implementation for old Pythons */ #if PY_VERSION_HEX < 0x02010000 # ifndef PyString_AsStringAndSize # define PyString_AsStringAndSize(obj, s, len) {*s = PyString_AsString(obj); *len = *s ? strlen(*s) : 0;} # endif #endif /* PySequence_Size for old Pythons */ #if PY_VERSION_HEX < 0x02000000 # ifndef PySequence_Size # define PySequence_Size PySequence_Length # endif #endif /* PyBool_FromLong for old Pythons */ #if PY_VERSION_HEX < 0x02030000 static PyObject *PyBool_FromLong(long ok) { PyObject *result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } #endif /* Py_ssize_t for old Pythons */ /* This code is as recommended by: */ /* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */ #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) typedef int Py_ssize_t; # define PY_SSIZE_T_MAX INT_MAX # define PY_SSIZE_T_MIN INT_MIN typedef inquiry lenfunc; typedef intargfunc ssizeargfunc; typedef intintargfunc ssizessizeargfunc; typedef intobjargproc ssizeobjargproc; typedef intintobjargproc ssizessizeobjargproc; typedef getreadbufferproc readbufferproc; typedef getwritebufferproc writebufferproc; typedef getsegcountproc segcountproc; typedef getcharbufferproc charbufferproc; static long PyNumber_AsSsize_t (PyObject *x, void *SWIGUNUSEDPARM(exc)) { long result = 0; PyObject *i = PyNumber_Int(x); if (i) { result = PyInt_AsLong(i); Py_DECREF(i); } return result; } #endif #if PY_VERSION_HEX < 0x02040000 #define Py_VISIT(op) \ do { \ if (op) { \ int vret = visit((op), arg); \ if (vret) \ return vret; \ } \ } while (0) #endif #if PY_VERSION_HEX < 0x02030000 typedef struct { PyTypeObject type; PyNumberMethods as_number; PyMappingMethods as_mapping; PySequenceMethods as_sequence; PyBufferProcs as_buffer; PyObject *name, *slots; } PyHeapTypeObject; #endif #if PY_VERSION_HEX < 0x02030000 typedef destructor freefunc; #endif #if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 6) || \ (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 0) || \ (PY_MAJOR_VERSION > 3)) # define SWIGPY_USE_CAPSULE # define SWIGPY_CAPSULE_NAME ((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION ".type_pointer_capsule" SWIG_TYPE_TABLE_NAME) #endif #if PY_VERSION_HEX < 0x03020000 #define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type) #define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name) #endif /* ----------------------------------------------------------------------------- * error manipulation * ----------------------------------------------------------------------------- */ SWIGRUNTIME PyObject* SWIG_Python_ErrorType(int code) { PyObject* type = 0; switch(code) { case SWIG_MemoryError: type = PyExc_MemoryError; break; case SWIG_IOError: type = PyExc_IOError; break; case SWIG_RuntimeError: type = PyExc_RuntimeError; break; case SWIG_IndexError: type = PyExc_IndexError; break; case SWIG_TypeError: type = PyExc_TypeError; break; case SWIG_DivisionByZero: type = PyExc_ZeroDivisionError; break; case SWIG_OverflowError: type = PyExc_OverflowError; break; case SWIG_SyntaxError: type = PyExc_SyntaxError; break; case SWIG_ValueError: type = PyExc_ValueError; break; case SWIG_SystemError: type = PyExc_SystemError; break; case SWIG_AttributeError: type = PyExc_AttributeError; break; default: type = PyExc_RuntimeError; } return type; } SWIGRUNTIME void SWIG_Python_AddErrorMsg(const char* mesg) { PyObject *type = 0; PyObject *value = 0; PyObject *traceback = 0; if (PyErr_Occurred()) PyErr_Fetch(&type, &value, &traceback); if (value) { char *tmp; PyObject *old_str = PyObject_Str(value); PyErr_Clear(); Py_XINCREF(type); PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg); SWIG_Python_str_DelForPy3(tmp); Py_DECREF(old_str); Py_DECREF(value); } else { PyErr_SetString(PyExc_RuntimeError, mesg); } } #if defined(SWIG_PYTHON_NO_THREADS) # if defined(SWIG_PYTHON_THREADS) # undef SWIG_PYTHON_THREADS # endif #endif #if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */ # if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL) # if (PY_VERSION_HEX >= 0x02030000) /* For 2.3 or later, use the PyGILState calls */ # define SWIG_PYTHON_USE_GIL # endif # endif # if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */ # ifndef SWIG_PYTHON_INITIALIZE_THREADS # define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads() # endif # ifdef __cplusplus /* C++ code */ class SWIG_Python_Thread_Block { bool status; PyGILState_STATE state; public: void end() { if (status) { PyGILState_Release(state); status = false;} } SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {} ~SWIG_Python_Thread_Block() { end(); } }; class SWIG_Python_Thread_Allow { bool status; PyThreadState *save; public: void end() { if (status) { PyEval_RestoreThread(save); status = false; }} SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {} ~SWIG_Python_Thread_Allow() { end(); } }; # define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block # define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end() # define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow # define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end() # else /* C code */ # define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure() # define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block) # define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread() # define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow) # endif # else /* Old thread way, not implemented, user must provide it */ # if !defined(SWIG_PYTHON_INITIALIZE_THREADS) # define SWIG_PYTHON_INITIALIZE_THREADS # endif # if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK) # define SWIG_PYTHON_THREAD_BEGIN_BLOCK # endif # if !defined(SWIG_PYTHON_THREAD_END_BLOCK) # define SWIG_PYTHON_THREAD_END_BLOCK # endif # if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW) # define SWIG_PYTHON_THREAD_BEGIN_ALLOW # endif # if !defined(SWIG_PYTHON_THREAD_END_ALLOW) # define SWIG_PYTHON_THREAD_END_ALLOW # endif # endif #else /* No thread support */ # define SWIG_PYTHON_INITIALIZE_THREADS # define SWIG_PYTHON_THREAD_BEGIN_BLOCK # define SWIG_PYTHON_THREAD_END_BLOCK # define SWIG_PYTHON_THREAD_BEGIN_ALLOW # define SWIG_PYTHON_THREAD_END_ALLOW #endif /* ----------------------------------------------------------------------------- * Python API portion that goes into the runtime * ----------------------------------------------------------------------------- */ #ifdef __cplusplus extern "C" { #endif /* ----------------------------------------------------------------------------- * Constant declarations * ----------------------------------------------------------------------------- */ /* Constant Types */ #define SWIG_PY_POINTER 4 #define SWIG_PY_BINARY 5 /* Constant information structure */ typedef struct swig_const_info { int type; char *name; long lvalue; double dvalue; void *pvalue; swig_type_info **ptype; } swig_const_info; /* ----------------------------------------------------------------------------- * Wrapper of PyInstanceMethod_New() used in Python 3 * It is exported to the generated module, used for -fastproxy * ----------------------------------------------------------------------------- */ #if PY_VERSION_HEX >= 0x03000000 SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func) { return PyInstanceMethod_New(func); } #else SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *SWIGUNUSEDPARM(func)) { return NULL; } #endif #ifdef __cplusplus } #endif /* ----------------------------------------------------------------------------- * pyrun.swg * * This file contains the runtime support for Python modules * and includes code for managing global variables and pointer * type checking. * * ----------------------------------------------------------------------------- */ /* Common SWIG API */ /* for raw pointers */ #define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0) #define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags) #define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own) #ifdef SWIGPYTHON_BUILTIN #define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(self, ptr, type, flags) #else #define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) #endif #define SWIG_InternalNewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) #define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty) #define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src) #define swig_owntype int /* for raw packed data */ #define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) #define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) /* for class or struct pointers */ #define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags) #define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags) /* for C or C++ function pointers */ #define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type) #define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(NULL, ptr, type, 0) /* for C++ member pointers, ie, member methods */ #define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) #define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) /* Runtime API */ #define SWIG_GetModule(clientdata) SWIG_Python_GetModule() #define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer) #define SWIG_NewClientData(obj) SwigPyClientData_New(obj) #define SWIG_SetErrorObj SWIG_Python_SetErrorObj #define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg #define SWIG_ErrorType(code) SWIG_Python_ErrorType(code) #define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg) #define SWIG_fail goto fail /* Runtime API implementation */ /* Error manipulation */ SWIGINTERN void SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) { SWIG_PYTHON_THREAD_BEGIN_BLOCK; PyErr_SetObject(errtype, obj); Py_DECREF(obj); SWIG_PYTHON_THREAD_END_BLOCK; } SWIGINTERN void SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) { SWIG_PYTHON_THREAD_BEGIN_BLOCK; PyErr_SetString(errtype, (char *) msg); SWIG_PYTHON_THREAD_END_BLOCK; } #define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj) /* Set a constant value */ #if defined(SWIGPYTHON_BUILTIN) SWIGINTERN void SwigPyBuiltin_AddPublicSymbol(PyObject *seq, const char *key) { PyObject *s = PyString_InternFromString(key); PyList_Append(seq, s); Py_DECREF(s); } SWIGINTERN void SWIG_Python_SetConstant(PyObject *d, PyObject *public_interface, const char *name, PyObject *obj) { PyDict_SetItemString(d, (char *)name, obj); Py_DECREF(obj); if (public_interface) SwigPyBuiltin_AddPublicSymbol(public_interface, name); } #else SWIGINTERN void SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) { PyDict_SetItemString(d, (char *)name, obj); Py_DECREF(obj); } #endif /* Append a value to the result obj */ SWIGINTERN PyObject* SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) { #if !defined(SWIG_PYTHON_OUTPUT_TUPLE) if (!result) { result = obj; } else if (result == Py_None) { Py_DECREF(result); result = obj; } else { if (!PyList_Check(result)) { PyObject *o2 = result; result = PyList_New(1); PyList_SetItem(result, 0, o2); } PyList_Append(result,obj); Py_DECREF(obj); } return result; #else PyObject* o2; PyObject* o3; if (!result) { result = obj; } else if (result == Py_None) { Py_DECREF(result); result = obj; } else { if (!PyTuple_Check(result)) { o2 = result; result = PyTuple_New(1); PyTuple_SET_ITEM(result, 0, o2); } o3 = PyTuple_New(1); PyTuple_SET_ITEM(o3, 0, obj); o2 = result; result = PySequence_Concat(o2, o3); Py_DECREF(o2); Py_DECREF(o3); } return result; #endif } /* Unpack the argument tuple */ SWIGINTERN int SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs) { if (!args) { if (!min && !max) { return 1; } else { PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none", name, (min == max ? "" : "at least "), (int)min); return 0; } } if (!PyTuple_Check(args)) { if (min <= 1 && max >= 1) { register int i; objs[0] = args; for (i = 1; i < max; ++i) { objs[i] = 0; } return 2; } PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple"); return 0; } else { register Py_ssize_t l = PyTuple_GET_SIZE(args); if (l < min) { PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", name, (min == max ? "" : "at least "), (int)min, (int)l); return 0; } else if (l > max) { PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", name, (min == max ? "" : "at most "), (int)max, (int)l); return 0; } else { register int i; for (i = 0; i < l; ++i) { objs[i] = PyTuple_GET_ITEM(args, i); } for (; l < max; ++l) { objs[l] = 0; } return i + 1; } } } /* A functor is a function object with one single object argument */ #if PY_VERSION_HEX >= 0x02020000 #define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL); #else #define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunction(functor, "O", obj); #endif /* Helper for static pointer initialization for both C and C++ code, for example static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...); */ #ifdef __cplusplus #define SWIG_STATIC_POINTER(var) var #else #define SWIG_STATIC_POINTER(var) var = 0; if (!var) var #endif /* ----------------------------------------------------------------------------- * Pointer declarations * ----------------------------------------------------------------------------- */ /* Flags for new pointer objects */ #define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1) #define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN) #define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1) #define SWIG_BUILTIN_TP_INIT (SWIG_POINTER_OWN << 2) #define SWIG_BUILTIN_INIT (SWIG_BUILTIN_TP_INIT | SWIG_POINTER_OWN) #ifdef __cplusplus extern "C" { #endif /* How to access Py_None */ #if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) # ifndef SWIG_PYTHON_NO_BUILD_NONE # ifndef SWIG_PYTHON_BUILD_NONE # define SWIG_PYTHON_BUILD_NONE # endif # endif #endif #ifdef SWIG_PYTHON_BUILD_NONE # ifdef Py_None # undef Py_None # define Py_None SWIG_Py_None() # endif SWIGRUNTIMEINLINE PyObject * _SWIG_Py_None(void) { PyObject *none = Py_BuildValue((char*)""); Py_DECREF(none); return none; } SWIGRUNTIME PyObject * SWIG_Py_None(void) { static PyObject *SWIG_STATIC_POINTER(none) = _SWIG_Py_None(); return none; } #endif /* The python void return value */ SWIGRUNTIMEINLINE PyObject * SWIG_Py_Void(void) { PyObject *none = Py_None; Py_INCREF(none); return none; } /* SwigPyClientData */ typedef struct { PyObject *klass; PyObject *newraw; PyObject *newargs; PyObject *destroy; int delargs; int implicitconv; PyTypeObject *pytype; } SwigPyClientData; SWIGRUNTIMEINLINE int SWIG_Python_CheckImplicit(swig_type_info *ty) { SwigPyClientData *data = (SwigPyClientData *)ty->clientdata; return data ? data->implicitconv : 0; } SWIGRUNTIMEINLINE PyObject * SWIG_Python_ExceptionType(swig_type_info *desc) { SwigPyClientData *data = desc ? (SwigPyClientData *) desc->clientdata : 0; PyObject *klass = data ? data->klass : 0; return (klass ? klass : PyExc_RuntimeError); } SWIGRUNTIME SwigPyClientData * SwigPyClientData_New(PyObject* obj) { if (!obj) { return 0; } else { SwigPyClientData *data = (SwigPyClientData *)malloc(sizeof(SwigPyClientData)); /* the klass element */ data->klass = obj; Py_INCREF(data->klass); /* the newraw method and newargs arguments used to create a new raw instance */ if (PyClass_Check(obj)) { data->newraw = 0; data->newargs = obj; Py_INCREF(obj); } else { #if (PY_VERSION_HEX < 0x02020000) data->newraw = 0; #else data->newraw = PyObject_GetAttrString(data->klass, (char *)"__new__"); #endif if (data->newraw) { Py_INCREF(data->newraw); data->newargs = PyTuple_New(1); PyTuple_SetItem(data->newargs, 0, obj); } else { data->newargs = obj; } Py_INCREF(data->newargs); } /* the destroy method, aka as the C++ delete method */ data->destroy = PyObject_GetAttrString(data->klass, (char *)"__swig_destroy__"); if (PyErr_Occurred()) { PyErr_Clear(); data->destroy = 0; } if (data->destroy) { int flags; Py_INCREF(data->destroy); flags = PyCFunction_GET_FLAGS(data->destroy); #ifdef METH_O data->delargs = !(flags & (METH_O)); #else data->delargs = 0; #endif } else { data->delargs = 0; } data->implicitconv = 0; data->pytype = 0; return data; } } SWIGRUNTIME void SwigPyClientData_Del(SwigPyClientData *data) { Py_XDECREF(data->newraw); Py_XDECREF(data->newargs); Py_XDECREF(data->destroy); } /* =============== SwigPyObject =====================*/ typedef struct { PyObject_HEAD void *ptr; swig_type_info *ty; int own; PyObject *next; #ifdef SWIGPYTHON_BUILTIN PyObject *dict; #endif } SwigPyObject; SWIGRUNTIME PyObject * SwigPyObject_long(SwigPyObject *v) { return PyLong_FromVoidPtr(v->ptr); } SWIGRUNTIME PyObject * SwigPyObject_format(const char* fmt, SwigPyObject *v) { PyObject *res = NULL; PyObject *args = PyTuple_New(1); if (args) { if (PyTuple_SetItem(args, 0, SwigPyObject_long(v)) == 0) { PyObject *ofmt = SWIG_Python_str_FromChar(fmt); if (ofmt) { #if PY_VERSION_HEX >= 0x03000000 res = PyUnicode_Format(ofmt,args); #else res = PyString_Format(ofmt,args); #endif Py_DECREF(ofmt); } Py_DECREF(args); } } return res; } SWIGRUNTIME PyObject * SwigPyObject_oct(SwigPyObject *v) { return SwigPyObject_format("%o",v); } SWIGRUNTIME PyObject * SwigPyObject_hex(SwigPyObject *v) { return SwigPyObject_format("%x",v); } SWIGRUNTIME PyObject * #ifdef METH_NOARGS SwigPyObject_repr(SwigPyObject *v) #else SwigPyObject_repr(SwigPyObject *v, PyObject *args) #endif { const char *name = SWIG_TypePrettyName(v->ty); PyObject *repr = SWIG_Python_str_FromFormat("", name, (void *)v); if (v->next) { # ifdef METH_NOARGS PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next); # else PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next, args); # endif # if PY_VERSION_HEX >= 0x03000000 PyObject *joined = PyUnicode_Concat(repr, nrep); Py_DecRef(repr); Py_DecRef(nrep); repr = joined; # else PyString_ConcatAndDel(&repr,nrep); # endif } return repr; } SWIGRUNTIME int SwigPyObject_print(SwigPyObject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) { char *str; #ifdef METH_NOARGS PyObject *repr = SwigPyObject_repr(v); #else PyObject *repr = SwigPyObject_repr(v, NULL); #endif if (repr) { str = SWIG_Python_str_AsChar(repr); fputs(str, fp); SWIG_Python_str_DelForPy3(str); Py_DECREF(repr); return 0; } else { return 1; } } SWIGRUNTIME PyObject * SwigPyObject_str(SwigPyObject *v) { char result[SWIG_BUFFER_SIZE]; return SWIG_PackVoidPtr(result, v->ptr, v->ty->name, sizeof(result)) ? SWIG_Python_str_FromChar(result) : 0; } SWIGRUNTIME int SwigPyObject_compare(SwigPyObject *v, SwigPyObject *w) { void *i = v->ptr; void *j = w->ptr; return (i < j) ? -1 : ((i > j) ? 1 : 0); } /* Added for Python 3.x, would it also be useful for Python 2.x? */ SWIGRUNTIME PyObject* SwigPyObject_richcompare(SwigPyObject *v, SwigPyObject *w, int op) { PyObject* res; if( op != Py_EQ && op != Py_NE ) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } res = PyBool_FromLong( (SwigPyObject_compare(v, w)==0) == (op == Py_EQ) ? 1 : 0); return res; } SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void); #ifdef SWIGPYTHON_BUILTIN static swig_type_info *SwigPyObject_stype = 0; SWIGRUNTIME PyTypeObject* SwigPyObject_type(void) { SwigPyClientData *cd; assert(SwigPyObject_stype); cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; assert(cd); assert(cd->pytype); return cd->pytype; } #else SWIGRUNTIME PyTypeObject* SwigPyObject_type(void) { static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyObject_TypeOnce(); return type; } #endif SWIGRUNTIMEINLINE int SwigPyObject_Check(PyObject *op) { #ifdef SWIGPYTHON_BUILTIN PyTypeObject *target_tp = SwigPyObject_type(); if (PyType_IsSubtype(op->ob_type, target_tp)) return 1; return (strcmp(op->ob_type->tp_name, "SwigPyObject") == 0); #else return (Py_TYPE(op) == SwigPyObject_type()) || (strcmp(Py_TYPE(op)->tp_name,"SwigPyObject") == 0); #endif } SWIGRUNTIME PyObject * SwigPyObject_New(void *ptr, swig_type_info *ty, int own); SWIGRUNTIME void SwigPyObject_dealloc(PyObject *v) { SwigPyObject *sobj = (SwigPyObject *) v; PyObject *next = sobj->next; if (sobj->own == SWIG_POINTER_OWN) { swig_type_info *ty = sobj->ty; SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; PyObject *destroy = data ? data->destroy : 0; if (destroy) { /* destroy is always a VARARGS method */ PyObject *res; if (data->delargs) { /* we need to create a temporary object to carry the destroy operation */ PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0); res = SWIG_Python_CallFunctor(destroy, tmp); Py_DECREF(tmp); } else { PyCFunction meth = PyCFunction_GET_FUNCTION(destroy); PyObject *mself = PyCFunction_GET_SELF(destroy); res = ((*meth)(mself, v)); } Py_XDECREF(res); } #if !defined(SWIG_PYTHON_SILENT_MEMLEAK) else { const char *name = SWIG_TypePrettyName(ty); printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown")); } #endif } Py_XDECREF(next); PyObject_DEL(v); } SWIGRUNTIME PyObject* SwigPyObject_append(PyObject* v, PyObject* next) { SwigPyObject *sobj = (SwigPyObject *) v; #ifndef METH_O PyObject *tmp = 0; if (!PyArg_ParseTuple(next,(char *)"O:append", &tmp)) return NULL; next = tmp; #endif if (!SwigPyObject_Check(next)) { return NULL; } sobj->next = next; Py_INCREF(next); return SWIG_Py_Void(); } SWIGRUNTIME PyObject* #ifdef METH_NOARGS SwigPyObject_next(PyObject* v) #else SwigPyObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) #endif { SwigPyObject *sobj = (SwigPyObject *) v; if (sobj->next) { Py_INCREF(sobj->next); return sobj->next; } else { return SWIG_Py_Void(); } } SWIGINTERN PyObject* #ifdef METH_NOARGS SwigPyObject_disown(PyObject *v) #else SwigPyObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) #endif { SwigPyObject *sobj = (SwigPyObject *)v; sobj->own = 0; return SWIG_Py_Void(); } SWIGINTERN PyObject* #ifdef METH_NOARGS SwigPyObject_acquire(PyObject *v) #else SwigPyObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) #endif { SwigPyObject *sobj = (SwigPyObject *)v; sobj->own = SWIG_POINTER_OWN; return SWIG_Py_Void(); } SWIGINTERN PyObject* SwigPyObject_own(PyObject *v, PyObject *args) { PyObject *val = 0; #if (PY_VERSION_HEX < 0x02020000) if (!PyArg_ParseTuple(args,(char *)"|O:own",&val)) #else if (!PyArg_UnpackTuple(args, (char *)"own", 0, 1, &val)) #endif { return NULL; } else { SwigPyObject *sobj = (SwigPyObject *)v; PyObject *obj = PyBool_FromLong(sobj->own); if (val) { #ifdef METH_NOARGS if (PyObject_IsTrue(val)) { SwigPyObject_acquire(v); } else { SwigPyObject_disown(v); } #else if (PyObject_IsTrue(val)) { SwigPyObject_acquire(v,args); } else { SwigPyObject_disown(v,args); } #endif } return obj; } } #ifdef METH_O static PyMethodDef swigobject_methods[] = { {(char *)"disown", (PyCFunction)SwigPyObject_disown, METH_NOARGS, (char *)"releases ownership of the pointer"}, {(char *)"acquire", (PyCFunction)SwigPyObject_acquire, METH_NOARGS, (char *)"aquires ownership of the pointer"}, {(char *)"own", (PyCFunction)SwigPyObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"}, {(char *)"append", (PyCFunction)SwigPyObject_append, METH_O, (char *)"appends another 'this' object"}, {(char *)"next", (PyCFunction)SwigPyObject_next, METH_NOARGS, (char *)"returns the next 'this' object"}, {(char *)"__repr__",(PyCFunction)SwigPyObject_repr, METH_NOARGS, (char *)"returns object representation"}, {0, 0, 0, 0} }; #else static PyMethodDef swigobject_methods[] = { {(char *)"disown", (PyCFunction)SwigPyObject_disown, METH_VARARGS, (char *)"releases ownership of the pointer"}, {(char *)"acquire", (PyCFunction)SwigPyObject_acquire, METH_VARARGS, (char *)"aquires ownership of the pointer"}, {(char *)"own", (PyCFunction)SwigPyObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"}, {(char *)"append", (PyCFunction)SwigPyObject_append, METH_VARARGS, (char *)"appends another 'this' object"}, {(char *)"next", (PyCFunction)SwigPyObject_next, METH_VARARGS, (char *)"returns the next 'this' object"}, {(char *)"__repr__",(PyCFunction)SwigPyObject_repr, METH_VARARGS, (char *)"returns object representation"}, {0, 0, 0, 0} }; #endif #if PY_VERSION_HEX < 0x02020000 SWIGINTERN PyObject * SwigPyObject_getattr(SwigPyObject *sobj,char *name) { return Py_FindMethod(swigobject_methods, (PyObject *)sobj, name); } #endif SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void) { static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer"; static PyNumberMethods SwigPyObject_as_number = { (binaryfunc)0, /*nb_add*/ (binaryfunc)0, /*nb_subtract*/ (binaryfunc)0, /*nb_multiply*/ /* nb_divide removed in Python 3 */ #if PY_VERSION_HEX < 0x03000000 (binaryfunc)0, /*nb_divide*/ #endif (binaryfunc)0, /*nb_remainder*/ (binaryfunc)0, /*nb_divmod*/ (ternaryfunc)0,/*nb_power*/ (unaryfunc)0, /*nb_negative*/ (unaryfunc)0, /*nb_positive*/ (unaryfunc)0, /*nb_absolute*/ (inquiry)0, /*nb_nonzero*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ 0, /*nb_and*/ 0, /*nb_xor*/ 0, /*nb_or*/ #if PY_VERSION_HEX < 0x03000000 0, /*nb_coerce*/ #endif (unaryfunc)SwigPyObject_long, /*nb_int*/ #if PY_VERSION_HEX < 0x03000000 (unaryfunc)SwigPyObject_long, /*nb_long*/ #else 0, /*nb_reserved*/ #endif (unaryfunc)0, /*nb_float*/ #if PY_VERSION_HEX < 0x03000000 (unaryfunc)SwigPyObject_oct, /*nb_oct*/ (unaryfunc)SwigPyObject_hex, /*nb_hex*/ #endif #if PY_VERSION_HEX >= 0x03000000 /* 3.0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index, nb_inplace_divide removed */ #elif PY_VERSION_HEX >= 0x02050000 /* 2.5.0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */ #elif PY_VERSION_HEX >= 0x02020000 /* 2.2.0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_true_divide */ #elif PY_VERSION_HEX >= 0x02000000 /* 2.0.0 */ 0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_or */ #endif }; static PyTypeObject swigpyobject_type; static int type_init = 0; if (!type_init) { const PyTypeObject tmp = { /* PyObject header changed in Python 3 */ #if PY_VERSION_HEX >= 0x03000000 PyVarObject_HEAD_INIT(NULL, 0) #else PyObject_HEAD_INIT(NULL) 0, /* ob_size */ #endif (char *)"SwigPyObject", /* tp_name */ sizeof(SwigPyObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)SwigPyObject_dealloc, /* tp_dealloc */ (printfunc)SwigPyObject_print, /* tp_print */ #if PY_VERSION_HEX < 0x02020000 (getattrfunc)SwigPyObject_getattr, /* tp_getattr */ #else (getattrfunc)0, /* tp_getattr */ #endif (setattrfunc)0, /* tp_setattr */ #if PY_VERSION_HEX >= 0x03000000 0, /* tp_reserved in 3.0.1, tp_compare in 3.0.0 but not used */ #else (cmpfunc)SwigPyObject_compare, /* tp_compare */ #endif (reprfunc)SwigPyObject_repr, /* tp_repr */ &SwigPyObject_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)0, /* tp_hash */ (ternaryfunc)0, /* tp_call */ (reprfunc)SwigPyObject_str, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ swigobject_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ (richcmpfunc)SwigPyObject_richcompare,/* tp_richcompare */ 0, /* tp_weaklistoffset */ #if PY_VERSION_HEX >= 0x02020000 0, /* tp_iter */ 0, /* tp_iternext */ swigobject_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ #endif #if PY_VERSION_HEX >= 0x02030000 0, /* tp_del */ #endif #if PY_VERSION_HEX >= 0x02060000 0, /* tp_version */ #endif #ifdef COUNT_ALLOCS 0,0,0,0 /* tp_alloc -> tp_next */ #endif }; swigpyobject_type = tmp; type_init = 1; #if PY_VERSION_HEX < 0x02020000 swigpyobject_type.ob_type = &PyType_Type; #else if (PyType_Ready(&swigpyobject_type) < 0) return NULL; #endif } return &swigpyobject_type; } SWIGRUNTIME PyObject * SwigPyObject_New(void *ptr, swig_type_info *ty, int own) { SwigPyObject *sobj = PyObject_NEW(SwigPyObject, SwigPyObject_type()); if (sobj) { sobj->ptr = ptr; sobj->ty = ty; sobj->own = own; sobj->next = 0; } return (PyObject *)sobj; } /* ----------------------------------------------------------------------------- * Implements a simple Swig Packed type, and use it instead of string * ----------------------------------------------------------------------------- */ typedef struct { PyObject_HEAD void *pack; swig_type_info *ty; size_t size; } SwigPyPacked; SWIGRUNTIME int SwigPyPacked_print(SwigPyPacked *v, FILE *fp, int SWIGUNUSEDPARM(flags)) { char result[SWIG_BUFFER_SIZE]; fputs("pack, v->size, 0, sizeof(result))) { fputs("at ", fp); fputs(result, fp); } fputs(v->ty->name,fp); fputs(">", fp); return 0; } SWIGRUNTIME PyObject * SwigPyPacked_repr(SwigPyPacked *v) { char result[SWIG_BUFFER_SIZE]; if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) { return SWIG_Python_str_FromFormat("", result, v->ty->name); } else { return SWIG_Python_str_FromFormat("", v->ty->name); } } SWIGRUNTIME PyObject * SwigPyPacked_str(SwigPyPacked *v) { char result[SWIG_BUFFER_SIZE]; if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){ return SWIG_Python_str_FromFormat("%s%s", result, v->ty->name); } else { return SWIG_Python_str_FromChar(v->ty->name); } } SWIGRUNTIME int SwigPyPacked_compare(SwigPyPacked *v, SwigPyPacked *w) { size_t i = v->size; size_t j = w->size; int s = (i < j) ? -1 : ((i > j) ? 1 : 0); return s ? s : strncmp((char *)v->pack, (char *)w->pack, 2*v->size); } SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void); SWIGRUNTIME PyTypeObject* SwigPyPacked_type(void) { static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyPacked_TypeOnce(); return type; } SWIGRUNTIMEINLINE int SwigPyPacked_Check(PyObject *op) { return ((op)->ob_type == SwigPyPacked_TypeOnce()) || (strcmp((op)->ob_type->tp_name,"SwigPyPacked") == 0); } SWIGRUNTIME void SwigPyPacked_dealloc(PyObject *v) { if (SwigPyPacked_Check(v)) { SwigPyPacked *sobj = (SwigPyPacked *) v; free(sobj->pack); } PyObject_DEL(v); } SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void) { static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer"; static PyTypeObject swigpypacked_type; static int type_init = 0; if (!type_init) { const PyTypeObject tmp = { /* PyObject header changed in Python 3 */ #if PY_VERSION_HEX>=0x03000000 PyVarObject_HEAD_INIT(NULL, 0) #else PyObject_HEAD_INIT(NULL) 0, /* ob_size */ #endif (char *)"SwigPyPacked", /* tp_name */ sizeof(SwigPyPacked), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)SwigPyPacked_dealloc, /* tp_dealloc */ (printfunc)SwigPyPacked_print, /* tp_print */ (getattrfunc)0, /* tp_getattr */ (setattrfunc)0, /* tp_setattr */ #if PY_VERSION_HEX>=0x03000000 0, /* tp_reserved in 3.0.1 */ #else (cmpfunc)SwigPyPacked_compare, /* tp_compare */ #endif (reprfunc)SwigPyPacked_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)0, /* tp_hash */ (ternaryfunc)0, /* tp_call */ (reprfunc)SwigPyPacked_str, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ swigpacked_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ #if PY_VERSION_HEX >= 0x02020000 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ #endif #if PY_VERSION_HEX >= 0x02030000 0, /* tp_del */ #endif #if PY_VERSION_HEX >= 0x02060000 0, /* tp_version */ #endif #ifdef COUNT_ALLOCS 0,0,0,0 /* tp_alloc -> tp_next */ #endif }; swigpypacked_type = tmp; type_init = 1; #if PY_VERSION_HEX < 0x02020000 swigpypacked_type.ob_type = &PyType_Type; #else if (PyType_Ready(&swigpypacked_type) < 0) return NULL; #endif } return &swigpypacked_type; } SWIGRUNTIME PyObject * SwigPyPacked_New(void *ptr, size_t size, swig_type_info *ty) { SwigPyPacked *sobj = PyObject_NEW(SwigPyPacked, SwigPyPacked_type()); if (sobj) { void *pack = malloc(size); if (pack) { memcpy(pack, ptr, size); sobj->pack = pack; sobj->ty = ty; sobj->size = size; } else { PyObject_DEL((PyObject *) sobj); sobj = 0; } } return (PyObject *) sobj; } SWIGRUNTIME swig_type_info * SwigPyPacked_UnpackData(PyObject *obj, void *ptr, size_t size) { if (SwigPyPacked_Check(obj)) { SwigPyPacked *sobj = (SwigPyPacked *)obj; if (sobj->size != size) return 0; memcpy(ptr, sobj->pack, size); return sobj->ty; } else { return 0; } } /* ----------------------------------------------------------------------------- * pointers/data manipulation * ----------------------------------------------------------------------------- */ SWIGRUNTIMEINLINE PyObject * _SWIG_This(void) { return SWIG_Python_str_FromChar("this"); } static PyObject *swig_this = NULL; SWIGRUNTIME PyObject * SWIG_This(void) { if (swig_this == NULL) swig_this = _SWIG_This(); return swig_this; } /* #define SWIG_PYTHON_SLOW_GETSET_THIS */ /* TODO: I don't know how to implement the fast getset in Python 3 right now */ #if PY_VERSION_HEX>=0x03000000 #define SWIG_PYTHON_SLOW_GETSET_THIS #endif SWIGRUNTIME SwigPyObject * SWIG_Python_GetSwigThis(PyObject *pyobj) { PyObject *obj; if (SwigPyObject_Check(pyobj)) return (SwigPyObject *) pyobj; #ifdef SWIGPYTHON_BUILTIN (void)obj; # ifdef PyWeakref_CheckProxy if (PyWeakref_CheckProxy(pyobj)) { pyobj = PyWeakref_GET_OBJECT(pyobj); if (pyobj && SwigPyObject_Check(pyobj)) return (SwigPyObject*) pyobj; } # endif return NULL; #else obj = 0; #if (!defined(SWIG_PYTHON_SLOW_GETSET_THIS) && (PY_VERSION_HEX >= 0x02030000)) if (PyInstance_Check(pyobj)) { obj = _PyInstance_Lookup(pyobj, SWIG_This()); } else { PyObject **dictptr = _PyObject_GetDictPtr(pyobj); if (dictptr != NULL) { PyObject *dict = *dictptr; obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0; } else { #ifdef PyWeakref_CheckProxy if (PyWeakref_CheckProxy(pyobj)) { PyObject *wobj = PyWeakref_GET_OBJECT(pyobj); return wobj ? SWIG_Python_GetSwigThis(wobj) : 0; } #endif obj = PyObject_GetAttr(pyobj,SWIG_This()); if (obj) { Py_DECREF(obj); } else { if (PyErr_Occurred()) PyErr_Clear(); return 0; } } } #else obj = PyObject_GetAttr(pyobj,SWIG_This()); if (obj) { Py_DECREF(obj); } else { if (PyErr_Occurred()) PyErr_Clear(); return 0; } #endif if (obj && !SwigPyObject_Check(obj)) { /* a PyObject is called 'this', try to get the 'real this' SwigPyObject from it */ return SWIG_Python_GetSwigThis(obj); } return (SwigPyObject *)obj; #endif } /* Acquire a pointer value */ SWIGRUNTIME int SWIG_Python_AcquirePtr(PyObject *obj, int own) { if (own == SWIG_POINTER_OWN) { SwigPyObject *sobj = SWIG_Python_GetSwigThis(obj); if (sobj) { int oldown = sobj->own; sobj->own = own; return oldown; } } return 0; } /* Convert a pointer value */ SWIGRUNTIME int SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) { int res; SwigPyObject *sobj; if (!obj) return SWIG_ERROR; if (obj == Py_None) { if (ptr) *ptr = 0; return SWIG_OK; } res = SWIG_ERROR; sobj = SWIG_Python_GetSwigThis(obj); if (own) *own = 0; while (sobj) { void *vptr = sobj->ptr; if (ty) { swig_type_info *to = sobj->ty; if (to == ty) { /* no type cast needed */ if (ptr) *ptr = vptr; break; } else { swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); if (!tc) { sobj = (SwigPyObject *)sobj->next; } else { if (ptr) { int newmemory = 0; *ptr = SWIG_TypeCast(tc,vptr,&newmemory); if (newmemory == SWIG_CAST_NEW_MEMORY) { assert(own); /* badly formed typemap which will lead to a memory leak - it must set and use own to delete *ptr */ if (own) *own = *own | SWIG_CAST_NEW_MEMORY; } } break; } } } else { if (ptr) *ptr = vptr; break; } } if (sobj) { if (own) *own = *own | sobj->own; if (flags & SWIG_POINTER_DISOWN) { sobj->own = 0; } res = SWIG_OK; } else { if (flags & SWIG_POINTER_IMPLICIT_CONV) { SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; if (data && !data->implicitconv) { PyObject *klass = data->klass; if (klass) { PyObject *impconv; data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/ impconv = SWIG_Python_CallFunctor(klass, obj); data->implicitconv = 0; if (PyErr_Occurred()) { PyErr_Clear(); impconv = 0; } if (impconv) { SwigPyObject *iobj = SWIG_Python_GetSwigThis(impconv); if (iobj) { void *vptr; res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0); if (SWIG_IsOK(res)) { if (ptr) { *ptr = vptr; /* transfer the ownership to 'ptr' */ iobj->own = 0; res = SWIG_AddCast(res); res = SWIG_AddNewMask(res); } else { res = SWIG_AddCast(res); } } } Py_DECREF(impconv); } } } } } return res; } /* Convert a function ptr value */ SWIGRUNTIME int SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) { if (!PyCFunction_Check(obj)) { return SWIG_ConvertPtr(obj, ptr, ty, 0); } else { void *vptr = 0; /* here we get the method pointer for callbacks */ const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc); const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0; if (desc) desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0; if (!desc) return SWIG_ERROR; if (ty) { swig_cast_info *tc = SWIG_TypeCheck(desc,ty); if (tc) { int newmemory = 0; *ptr = SWIG_TypeCast(tc,vptr,&newmemory); assert(!newmemory); /* newmemory handling not yet implemented */ } else { return SWIG_ERROR; } } else { *ptr = vptr; } return SWIG_OK; } } /* Convert a packed value value */ SWIGRUNTIME int SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) { swig_type_info *to = SwigPyPacked_UnpackData(obj, ptr, sz); if (!to) return SWIG_ERROR; if (ty) { if (to != ty) { /* check type cast? */ swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); if (!tc) return SWIG_ERROR; } } return SWIG_OK; } /* ----------------------------------------------------------------------------- * Create a new pointer object * ----------------------------------------------------------------------------- */ /* Create a new instance object, without calling __init__, and set the 'this' attribute. */ SWIGRUNTIME PyObject* SWIG_Python_NewShadowInstance(SwigPyClientData *data, PyObject *swig_this) { #if (PY_VERSION_HEX >= 0x02020000) PyObject *inst = 0; PyObject *newraw = data->newraw; if (newraw) { inst = PyObject_Call(newraw, data->newargs, NULL); if (inst) { #if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) PyObject **dictptr = _PyObject_GetDictPtr(inst); if (dictptr != NULL) { PyObject *dict = *dictptr; if (dict == NULL) { dict = PyDict_New(); *dictptr = dict; PyDict_SetItem(dict, SWIG_This(), swig_this); } } #else PyObject *key = SWIG_This(); PyObject_SetAttr(inst, key, swig_this); #endif } } else { #if PY_VERSION_HEX >= 0x03000000 inst = PyBaseObject_Type.tp_new((PyTypeObject*) data->newargs, Py_None, Py_None); PyObject_SetAttr(inst, SWIG_This(), swig_this); Py_TYPE(inst)->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; #else PyObject *dict = PyDict_New(); PyDict_SetItem(dict, SWIG_This(), swig_this); inst = PyInstance_NewRaw(data->newargs, dict); Py_DECREF(dict); #endif } return inst; #else #if (PY_VERSION_HEX >= 0x02010000) PyObject *inst; PyObject *dict = PyDict_New(); PyDict_SetItem(dict, SWIG_This(), swig_this); inst = PyInstance_NewRaw(data->newargs, dict); Py_DECREF(dict); return (PyObject *) inst; #else PyInstanceObject *inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type); if (inst == NULL) { return NULL; } inst->in_class = (PyClassObject *)data->newargs; Py_INCREF(inst->in_class); inst->in_dict = PyDict_New(); if (inst->in_dict == NULL) { Py_DECREF(inst); return NULL; } #ifdef Py_TPFLAGS_HAVE_WEAKREFS inst->in_weakreflist = NULL; #endif #ifdef Py_TPFLAGS_GC PyObject_GC_Init(inst); #endif PyDict_SetItem(inst->in_dict, SWIG_This(), swig_this); return (PyObject *) inst; #endif #endif } SWIGRUNTIME void SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this) { PyObject *dict; #if (PY_VERSION_HEX >= 0x02020000) && !defined(SWIG_PYTHON_SLOW_GETSET_THIS) PyObject **dictptr = _PyObject_GetDictPtr(inst); if (dictptr != NULL) { dict = *dictptr; if (dict == NULL) { dict = PyDict_New(); *dictptr = dict; } PyDict_SetItem(dict, SWIG_This(), swig_this); return; } #endif dict = PyObject_GetAttrString(inst, (char*)"__dict__"); PyDict_SetItem(dict, SWIG_This(), swig_this); Py_DECREF(dict); } SWIGINTERN PyObject * SWIG_Python_InitShadowInstance(PyObject *args) { PyObject *obj[2]; if (!SWIG_Python_UnpackTuple(args,(char*)"swiginit", 2, 2, obj)) { return NULL; } else { SwigPyObject *sthis = SWIG_Python_GetSwigThis(obj[0]); if (sthis) { SwigPyObject_append((PyObject*) sthis, obj[1]); } else { SWIG_Python_SetSwigThis(obj[0], obj[1]); } return SWIG_Py_Void(); } } /* Create a new pointer object */ SWIGRUNTIME PyObject * SWIG_Python_NewPointerObj(PyObject *self, void *ptr, swig_type_info *type, int flags) { SwigPyClientData *clientdata; PyObject * robj; int own; if (!ptr) return SWIG_Py_Void(); clientdata = type ? (SwigPyClientData *)(type->clientdata) : 0; own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0; if (clientdata && clientdata->pytype) { SwigPyObject *newobj; if (flags & SWIG_BUILTIN_TP_INIT) { newobj = (SwigPyObject*) self; if (newobj->ptr) { PyObject *next_self = clientdata->pytype->tp_alloc(clientdata->pytype, 0); while (newobj->next) newobj = (SwigPyObject *) newobj->next; newobj->next = next_self; newobj = (SwigPyObject *)next_self; } } else { newobj = PyObject_New(SwigPyObject, clientdata->pytype); } if (newobj) { newobj->ptr = ptr; newobj->ty = type; newobj->own = own; newobj->next = 0; #ifdef SWIGPYTHON_BUILTIN newobj->dict = 0; #endif return (PyObject*) newobj; } return SWIG_Py_Void(); } assert(!(flags & SWIG_BUILTIN_TP_INIT)); robj = SwigPyObject_New(ptr, type, own); if (clientdata && !(flags & SWIG_POINTER_NOSHADOW)) { PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj); if (inst) { Py_DECREF(robj); robj = inst; } } return robj; } /* Create a new packed object */ SWIGRUNTIMEINLINE PyObject * SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) { return ptr ? SwigPyPacked_New((void *) ptr, sz, type) : SWIG_Py_Void(); } /* -----------------------------------------------------------------------------* * Get type list * -----------------------------------------------------------------------------*/ #ifdef SWIG_LINK_RUNTIME void *SWIG_ReturnGlobalTypeList(void *); #endif SWIGRUNTIME swig_module_info * SWIG_Python_GetModule(void) { static void *type_pointer = (void *)0; /* first check if module already created */ if (!type_pointer) { #ifdef SWIG_LINK_RUNTIME type_pointer = SWIG_ReturnGlobalTypeList((void *)0); #else # ifdef SWIGPY_USE_CAPSULE type_pointer = PyCapsule_Import(SWIGPY_CAPSULE_NAME, 0); # else type_pointer = PyCObject_Import((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME); # endif if (PyErr_Occurred()) { PyErr_Clear(); type_pointer = (void *)0; } #endif } return (swig_module_info *) type_pointer; } #if PY_MAJOR_VERSION < 2 /* PyModule_AddObject function was introduced in Python 2.0. The following function is copied out of Python/modsupport.c in python version 2.3.4 */ SWIGINTERN int PyModule_AddObject(PyObject *m, char *name, PyObject *o) { PyObject *dict; if (!PyModule_Check(m)) { PyErr_SetString(PyExc_TypeError, "PyModule_AddObject() needs module as first arg"); return SWIG_ERROR; } if (!o) { PyErr_SetString(PyExc_TypeError, "PyModule_AddObject() needs non-NULL value"); return SWIG_ERROR; } dict = PyModule_GetDict(m); if (dict == NULL) { /* Internal error -- modules must have a dict! */ PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__", PyModule_GetName(m)); return SWIG_ERROR; } if (PyDict_SetItemString(dict, name, o)) return SWIG_ERROR; Py_DECREF(o); return SWIG_OK; } #endif SWIGRUNTIME void #ifdef SWIGPY_USE_CAPSULE SWIG_Python_DestroyModule(PyObject *obj) #else SWIG_Python_DestroyModule(void *vptr) #endif { #ifdef SWIGPY_USE_CAPSULE swig_module_info *swig_module = (swig_module_info *) PyCapsule_GetPointer(obj, SWIGPY_CAPSULE_NAME); #else swig_module_info *swig_module = (swig_module_info *) vptr; #endif swig_type_info **types = swig_module->types; size_t i; for (i =0; i < swig_module->size; ++i) { swig_type_info *ty = types[i]; if (ty->owndata) { SwigPyClientData *data = (SwigPyClientData *) ty->clientdata; if (data) SwigPyClientData_Del(data); } } Py_DECREF(SWIG_This()); swig_this = NULL; } SWIGRUNTIME void SWIG_Python_SetModule(swig_module_info *swig_module) { #if PY_VERSION_HEX >= 0x03000000 /* Add a dummy module object into sys.modules */ PyObject *module = PyImport_AddModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION); #else static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} }; /* Sentinel */ PyObject *module = Py_InitModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, swig_empty_runtime_method_table); #endif #ifdef SWIGPY_USE_CAPSULE PyObject *pointer = PyCapsule_New((void *) swig_module, SWIGPY_CAPSULE_NAME, SWIG_Python_DestroyModule); if (pointer && module) { PyModule_AddObject(module, (char*)"type_pointer_capsule" SWIG_TYPE_TABLE_NAME, pointer); } else { Py_XDECREF(pointer); } #else PyObject *pointer = PyCObject_FromVoidPtr((void *) swig_module, SWIG_Python_DestroyModule); if (pointer && module) { PyModule_AddObject(module, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME, pointer); } else { Py_XDECREF(pointer); } #endif } /* The python cached type query */ SWIGRUNTIME PyObject * SWIG_Python_TypeCache(void) { static PyObject *SWIG_STATIC_POINTER(cache) = PyDict_New(); return cache; } SWIGRUNTIME swig_type_info * SWIG_Python_TypeQuery(const char *type) { PyObject *cache = SWIG_Python_TypeCache(); PyObject *key = SWIG_Python_str_FromChar(type); PyObject *obj = PyDict_GetItem(cache, key); swig_type_info *descriptor; if (obj) { #ifdef SWIGPY_USE_CAPSULE descriptor = (swig_type_info *) PyCapsule_GetPointer(obj, NULL); #else descriptor = (swig_type_info *) PyCObject_AsVoidPtr(obj); #endif } else { swig_module_info *swig_module = SWIG_Python_GetModule(); descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type); if (descriptor) { #ifdef SWIGPY_USE_CAPSULE obj = PyCapsule_New((void*) descriptor, NULL, NULL); #else obj = PyCObject_FromVoidPtr(descriptor, NULL); #endif PyDict_SetItem(cache, key, obj); Py_DECREF(obj); } } Py_DECREF(key); return descriptor; } /* For backward compatibility only */ #define SWIG_POINTER_EXCEPTION 0 #define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg) #define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags) SWIGRUNTIME int SWIG_Python_AddErrMesg(const char* mesg, int infront) { if (PyErr_Occurred()) { PyObject *type = 0; PyObject *value = 0; PyObject *traceback = 0; PyErr_Fetch(&type, &value, &traceback); if (value) { char *tmp; PyObject *old_str = PyObject_Str(value); Py_XINCREF(type); PyErr_Clear(); if (infront) { PyErr_Format(type, "%s %s", mesg, tmp = SWIG_Python_str_AsChar(old_str)); } else { PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg); } SWIG_Python_str_DelForPy3(tmp); Py_DECREF(old_str); } return 1; } else { return 0; } } SWIGRUNTIME int SWIG_Python_ArgFail(int argnum) { if (PyErr_Occurred()) { /* add information about failing argument */ char mesg[256]; PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum); return SWIG_Python_AddErrMesg(mesg, 1); } else { return 0; } } SWIGRUNTIMEINLINE const char * SwigPyObject_GetDesc(PyObject *self) { SwigPyObject *v = (SwigPyObject *)self; swig_type_info *ty = v ? v->ty : 0; return ty ? ty->str : (char*)""; } SWIGRUNTIME void SWIG_Python_TypeError(const char *type, PyObject *obj) { if (type) { #if defined(SWIG_COBJECT_TYPES) if (obj && SwigPyObject_Check(obj)) { const char *otype = (const char *) SwigPyObject_GetDesc(obj); if (otype) { PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'SwigPyObject(%s)' is received", type, otype); return; } } else #endif { const char *otype = (obj ? obj->ob_type->tp_name : 0); if (otype) { PyObject *str = PyObject_Str(obj); const char *cstr = str ? SWIG_Python_str_AsChar(str) : 0; if (cstr) { PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received", type, otype, cstr); SWIG_Python_str_DelForPy3(cstr); } else { PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received", type, otype); } Py_XDECREF(str); return; } } PyErr_Format(PyExc_TypeError, "a '%s' is expected", type); } else { PyErr_Format(PyExc_TypeError, "unexpected type is received"); } } /* Convert a pointer value, signal an exception on a type mismatch */ SWIGRUNTIME void * SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int SWIGUNUSEDPARM(argnum), int flags) { void *result; if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) { PyErr_Clear(); #if SWIG_POINTER_EXCEPTION if (flags) { SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj); SWIG_Python_ArgFail(argnum); } #endif } return result; } SWIGRUNTIME int SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) { PyTypeObject *tp = obj->ob_type; PyObject *descr; PyObject *encoded_name; descrsetfunc f; int res; #ifdef Py_USING_UNICODE if (PyString_Check(name)) { name = PyUnicode_Decode(PyString_AsString(name), PyString_Size(name), NULL, NULL); if (!name) return -1; } else if (!PyUnicode_Check(name)) #else if (!PyString_Check(name)) #endif { PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", name->ob_type->tp_name); return -1; } else { Py_INCREF(name); } if (!tp->tp_dict) { if (PyType_Ready(tp) < 0) goto done; } res = -1; descr = _PyType_Lookup(tp, name); f = NULL; if (descr != NULL) f = descr->ob_type->tp_descr_set; if (!f) { if (PyString_Check(name)) { encoded_name = name; Py_INCREF(name); } else { encoded_name = PyUnicode_AsUTF8String(name); } PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%.200s'", tp->tp_name, PyString_AsString(encoded_name)); Py_DECREF(encoded_name); } else { res = f(descr, obj, value); } done: Py_DECREF(name); return res; } #ifdef __cplusplus } #endif #define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0) #define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else /* -------- TYPES TABLE (BEGIN) -------- */ #define SWIGTYPE_p_FILE swig_types[0] #define SWIGTYPE_p_GLenum swig_types[1] #define SWIGTYPE_p_GLsizei swig_types[2] #define SWIGTYPE_p_a_4__float swig_types[3] #define SWIGTYPE_p_char swig_types[4] #define SWIGTYPE_p_float swig_types[5] #define SWIGTYPE_p_int swig_types[6] #define SWIGTYPE_p_short swig_types[7] static swig_type_info *swig_types[9]; static swig_module_info swig_module = {swig_types, 8, 0, 0, 0, 0}; #define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name) #define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name) /* -------- TYPES TABLE (END) -------- */ #if (PY_VERSION_HEX <= 0x02000000) # if !defined(SWIG_PYTHON_CLASSIC) # error "This python version requires swig to be run with the '-classic' option" # endif #endif /*----------------------------------------------- @(target):= _gl2ps.so ------------------------------------------------*/ #if PY_VERSION_HEX >= 0x03000000 # define SWIG_init PyInit__gl2ps #else # define SWIG_init init_gl2ps #endif #define SWIG_name "_gl2ps" #define SWIGVERSION 0x020004 #define SWIG_VERSION SWIGVERSION #define SWIG_as_voidptr(a) (void *)((const void *)(a)) #define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a)) #include "gl2ps.h" #define SWIG_From_long PyInt_FromLong SWIGINTERNINLINE PyObject * SWIG_From_int (int value) { return SWIG_From_long (value); } SWIGINTERN swig_type_info* SWIG_pchar_descriptor(void) { static int init = 0; static swig_type_info* info = 0; if (!init) { info = SWIG_TypeQuery("_p_char"); init = 1; } return info; } SWIGINTERNINLINE PyObject * SWIG_FromCharPtrAndSize(const char* carray, size_t size) { if (carray) { if (size > INT_MAX) { swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); return pchar_descriptor ? SWIG_InternalNewPointerObj((char *)(carray), pchar_descriptor, 0) : SWIG_Py_Void(); } else { #if PY_VERSION_HEX >= 0x03000000 return PyUnicode_FromStringAndSize(carray, (int)(size)); #else return PyString_FromStringAndSize(carray, (int)(size)); #endif } } else { return SWIG_Py_Void(); } } SWIGINTERNINLINE PyObject * SWIG_FromCharPtr(const char *cptr) { return SWIG_FromCharPtrAndSize(cptr, (cptr ? strlen(cptr) : 0)); } #define SWIG_From_double PyFloat_FromDouble SWIGINTERN int SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc) { #if PY_VERSION_HEX>=0x03000000 if (PyUnicode_Check(obj)) #else if (PyString_Check(obj)) #endif { char *cstr; Py_ssize_t len; #if PY_VERSION_HEX>=0x03000000 if (!alloc && cptr) { /* We can't allow converting without allocation, since the internal representation of string in Python 3 is UCS-2/UCS-4 but we require a UTF-8 representation. TODO(bhy) More detailed explanation */ return SWIG_RuntimeError; } obj = PyUnicode_AsUTF8String(obj); PyBytes_AsStringAndSize(obj, &cstr, &len); if(alloc) *alloc = SWIG_NEWOBJ; #else PyString_AsStringAndSize(obj, &cstr, &len); #endif if (cptr) { if (alloc) { /* In python the user should not be able to modify the inner string representation. To warranty that, if you define SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string buffer is always returned. The default behavior is just to return the pointer value, so, be careful. */ #if defined(SWIG_PYTHON_SAFE_CSTRINGS) if (*alloc != SWIG_OLDOBJ) #else if (*alloc == SWIG_NEWOBJ) #endif { *cptr = (char *)memcpy((char *)malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1)); *alloc = SWIG_NEWOBJ; } else { *cptr = cstr; *alloc = SWIG_OLDOBJ; } } else { #if PY_VERSION_HEX>=0x03000000 assert(0); /* Should never reach here in Python 3 */ #endif *cptr = SWIG_Python_str_AsChar(obj); } } if (psize) *psize = len + 1; #if PY_VERSION_HEX>=0x03000000 Py_XDECREF(obj); #endif return SWIG_OK; } else { swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); if (pchar_descriptor) { void* vptr = 0; if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) { if (cptr) *cptr = (char *) vptr; if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0; if (alloc) *alloc = SWIG_OLDOBJ; return SWIG_OK; } } } return SWIG_TypeError; } #include #if !defined(SWIG_NO_LLONG_MAX) # if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__) # define LLONG_MAX __LONG_LONG_MAX__ # define LLONG_MIN (-LLONG_MAX - 1LL) # define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) # endif #endif SWIGINTERN int SWIG_AsVal_double (PyObject *obj, double *val) { int res = SWIG_TypeError; if (PyFloat_Check(obj)) { if (val) *val = PyFloat_AsDouble(obj); return SWIG_OK; } else if (PyInt_Check(obj)) { if (val) *val = PyInt_AsLong(obj); return SWIG_OK; } else if (PyLong_Check(obj)) { double v = PyLong_AsDouble(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_OK; } else { PyErr_Clear(); } } #ifdef SWIG_PYTHON_CAST_MODE { int dispatch = 0; double d = PyFloat_AsDouble(obj); if (!PyErr_Occurred()) { if (val) *val = d; return SWIG_AddCast(SWIG_OK); } else { PyErr_Clear(); } if (!dispatch) { long v = PyLong_AsLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_AddCast(SWIG_AddCast(SWIG_OK)); } else { PyErr_Clear(); } } } #endif return res; } #include #include SWIGINTERNINLINE int SWIG_CanCastAsInteger(double *d, double min, double max) { double x = *d; if ((min <= x && x <= max)) { double fx = floor(x); double cx = ceil(x); double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */ if ((errno == EDOM) || (errno == ERANGE)) { errno = 0; } else { double summ, reps, diff; if (rd < x) { diff = x - rd; } else if (rd > x) { diff = rd - x; } else { return 1; } summ = rd + x; reps = diff/summ; if (reps < 8*DBL_EPSILON) { *d = rd; return 1; } } } return 0; } SWIGINTERN int SWIG_AsVal_long (PyObject *obj, long* val) { if (PyInt_Check(obj)) { if (val) *val = PyInt_AsLong(obj); return SWIG_OK; } else if (PyLong_Check(obj)) { long v = PyLong_AsLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_OK; } else { PyErr_Clear(); } } #ifdef SWIG_PYTHON_CAST_MODE { int dispatch = 0; long v = PyInt_AsLong(obj); if (!PyErr_Occurred()) { if (val) *val = v; return SWIG_AddCast(SWIG_OK); } else { PyErr_Clear(); } if (!dispatch) { double d; int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) { if (val) *val = (long)(d); return res; } } } #endif return SWIG_TypeError; } SWIGINTERN int SWIG_AsVal_int (PyObject * obj, int *val) { long v; int res = SWIG_AsVal_long (obj, &v); if (SWIG_IsOK(res)) { if ((v < INT_MIN || v > INT_MAX)) { return SWIG_OverflowError; } else { if (val) *val = (int)(v); } } return res; } SWIGINTERN int SWIG_AsVal_short (PyObject * obj, short *val) { long v; int res = SWIG_AsVal_long (obj, &v); if (SWIG_IsOK(res)) { if ((v < SHRT_MIN || v > SHRT_MAX)) { return SWIG_OverflowError; } else { if (val) *val = (short)(v); } } return res; } SWIGINTERN int SWIG_AsVal_float (PyObject * obj, float *val) { double v; int res = SWIG_AsVal_double (obj, &v); if (SWIG_IsOK(res)) { if ((v < -FLT_MAX || v > FLT_MAX)) { return SWIG_OverflowError; } else { if (val) *val = (float)(v); } } return res; } #ifdef __cplusplus extern "C" { #endif SWIGINTERN PyObject *_wrap_gl2psBeginPage(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; GLint *arg3 ; GLint arg4 ; GLint arg5 ; GLint arg6 ; GLint arg7 ; GLint arg8 ; GL2PSrgba *arg9 = (GL2PSrgba *) 0 ; GLint arg10 ; GLint arg11 ; GLint arg12 ; GLint arg13 ; FILE *arg14 = (FILE *) 0 ; char *arg15 = (char *) 0 ; int res1 ; char *buf1 = 0 ; int alloc1 = 0 ; int res2 ; char *buf2 = 0 ; int alloc2 = 0 ; void *argp3 = 0 ; int res3 = 0 ; int val4 ; int ecode4 = 0 ; int val5 ; int ecode5 = 0 ; int val6 ; int ecode6 = 0 ; int val7 ; int ecode7 = 0 ; int val8 ; int ecode8 = 0 ; void *argp9 = 0 ; int res9 = 0 ; int val10 ; int ecode10 = 0 ; int val11 ; int ecode11 = 0 ; int val12 ; int ecode12 = 0 ; int val13 ; int ecode13 = 0 ; int res15 ; char *buf15 = 0 ; int alloc15 = 0 ; PyObject * obj0 = 0 ; PyObject * obj1 = 0 ; PyObject * obj2 = 0 ; PyObject * obj3 = 0 ; PyObject * obj4 = 0 ; PyObject * obj5 = 0 ; PyObject * obj6 = 0 ; PyObject * obj7 = 0 ; PyObject * obj8 = 0 ; PyObject * obj9 = 0 ; PyObject * obj10 = 0 ; PyObject * obj11 = 0 ; PyObject * obj12 = 0 ; PyObject * obj13 = 0 ; PyObject * obj14 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"OOOOOOOOOOOOOOO:gl2psBeginPage",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6,&obj7,&obj8,&obj9,&obj10,&obj11,&obj12,&obj13,&obj14)) SWIG_fail; res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, NULL, &alloc1); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "gl2psBeginPage" "', argument " "1"" of type '" "char const *""'"); } arg1 = (char *)(buf1); res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "gl2psBeginPage" "', argument " "2"" of type '" "char const *""'"); } arg2 = (char *)(buf2); res3 = SWIG_ConvertPtr(obj2, &argp3,SWIGTYPE_p_int, 0 | 0 ); if (!SWIG_IsOK(res3)) { SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "gl2psBeginPage" "', argument " "3"" of type '" "GLint [4]""'"); } arg3 = (GLint *)(argp3); ecode4 = SWIG_AsVal_int(obj3, &val4); if (!SWIG_IsOK(ecode4)) { SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "gl2psBeginPage" "', argument " "4"" of type '" "GLint""'"); } arg4 = (GLint)(val4); ecode5 = SWIG_AsVal_int(obj4, &val5); if (!SWIG_IsOK(ecode5)) { SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "gl2psBeginPage" "', argument " "5"" of type '" "GLint""'"); } arg5 = (GLint)(val5); ecode6 = SWIG_AsVal_int(obj5, &val6); if (!SWIG_IsOK(ecode6)) { SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "gl2psBeginPage" "', argument " "6"" of type '" "GLint""'"); } arg6 = (GLint)(val6); ecode7 = SWIG_AsVal_int(obj6, &val7); if (!SWIG_IsOK(ecode7)) { SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "gl2psBeginPage" "', argument " "7"" of type '" "GLint""'"); } arg7 = (GLint)(val7); ecode8 = SWIG_AsVal_int(obj7, &val8); if (!SWIG_IsOK(ecode8)) { SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "gl2psBeginPage" "', argument " "8"" of type '" "GLint""'"); } arg8 = (GLint)(val8); res9 = SWIG_ConvertPtr(obj8, &argp9,SWIGTYPE_p_a_4__float, 0 | 0 ); if (!SWIG_IsOK(res9)) { SWIG_exception_fail(SWIG_ArgError(res9), "in method '" "gl2psBeginPage" "', argument " "9"" of type '" "GL2PSrgba *""'"); } arg9 = (GL2PSrgba *)(argp9); ecode10 = SWIG_AsVal_int(obj9, &val10); if (!SWIG_IsOK(ecode10)) { SWIG_exception_fail(SWIG_ArgError(ecode10), "in method '" "gl2psBeginPage" "', argument " "10"" of type '" "GLint""'"); } arg10 = (GLint)(val10); ecode11 = SWIG_AsVal_int(obj10, &val11); if (!SWIG_IsOK(ecode11)) { SWIG_exception_fail(SWIG_ArgError(ecode11), "in method '" "gl2psBeginPage" "', argument " "11"" of type '" "GLint""'"); } arg11 = (GLint)(val11); ecode12 = SWIG_AsVal_int(obj11, &val12); if (!SWIG_IsOK(ecode12)) { SWIG_exception_fail(SWIG_ArgError(ecode12), "in method '" "gl2psBeginPage" "', argument " "12"" of type '" "GLint""'"); } arg12 = (GLint)(val12); ecode13 = SWIG_AsVal_int(obj12, &val13); if (!SWIG_IsOK(ecode13)) { SWIG_exception_fail(SWIG_ArgError(ecode13), "in method '" "gl2psBeginPage" "', argument " "13"" of type '" "GLint""'"); } arg13 = (GLint)(val13); { arg14 = (FILE *) PyFile_AsFile(obj13); printf("BV: Received a file\n"); } res15 = SWIG_AsCharPtrAndSize(obj14, &buf15, NULL, &alloc15); if (!SWIG_IsOK(res15)) { SWIG_exception_fail(SWIG_ArgError(res15), "in method '" "gl2psBeginPage" "', argument " "15"" of type '" "char const *""'"); } arg15 = (char *)(buf15); result = (GLint)gl2psBeginPage((char const *)arg1,(char const *)arg2,arg3,arg4,arg5,arg6,arg7,arg8,(float (*)[4])arg9,arg10,arg11,arg12,arg13,arg14,(char const *)arg15); resultobj = SWIG_From_int((int)(result)); if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); if (alloc15 == SWIG_NEWOBJ) free((char*)buf15); return resultobj; fail: if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); if (alloc15 == SWIG_NEWOBJ) free((char*)buf15); return NULL; } SWIGINTERN PyObject *_wrap_gl2psEndPage(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLint result; if (!PyArg_ParseTuple(args,(char *)":gl2psEndPage")) SWIG_fail; result = (GLint)gl2psEndPage(); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_gl2psSetOptions(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLint arg1 ; int val1 ; int ecode1 = 0 ; PyObject * obj0 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"O:gl2psSetOptions",&obj0)) SWIG_fail; ecode1 = SWIG_AsVal_int(obj0, &val1); if (!SWIG_IsOK(ecode1)) { SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "gl2psSetOptions" "', argument " "1"" of type '" "GLint""'"); } arg1 = (GLint)(val1); result = (GLint)gl2psSetOptions(arg1); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_gl2psGetOptions(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLint *arg1 = (GLint *) 0 ; void *argp1 = 0 ; int res1 = 0 ; PyObject * obj0 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"O:gl2psGetOptions",&obj0)) SWIG_fail; res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_int, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "gl2psGetOptions" "', argument " "1"" of type '" "GLint *""'"); } arg1 = (GLint *)(argp1); result = (GLint)gl2psGetOptions(arg1); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_gl2psBeginViewport(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLint *arg1 ; void *argp1 = 0 ; int res1 = 0 ; PyObject * obj0 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"O:gl2psBeginViewport",&obj0)) SWIG_fail; res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_int, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "gl2psBeginViewport" "', argument " "1"" of type '" "GLint [4]""'"); } arg1 = (GLint *)(argp1); result = (GLint)gl2psBeginViewport(arg1); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_gl2psEndViewport(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLint result; if (!PyArg_ParseTuple(args,(char *)":gl2psEndViewport")) SWIG_fail; result = (GLint)gl2psEndViewport(); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_gl2psText(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; GLshort arg3 ; int res1 ; char *buf1 = 0 ; int alloc1 = 0 ; int res2 ; char *buf2 = 0 ; int alloc2 = 0 ; short val3 ; int ecode3 = 0 ; PyObject * obj0 = 0 ; PyObject * obj1 = 0 ; PyObject * obj2 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"OOO:gl2psText",&obj0,&obj1,&obj2)) SWIG_fail; res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, NULL, &alloc1); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "gl2psText" "', argument " "1"" of type '" "char const *""'"); } arg1 = (char *)(buf1); res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "gl2psText" "', argument " "2"" of type '" "char const *""'"); } arg2 = (char *)(buf2); ecode3 = SWIG_AsVal_short(obj2, &val3); if (!SWIG_IsOK(ecode3)) { SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "gl2psText" "', argument " "3"" of type '" "GLshort""'"); } arg3 = (GLshort)(val3); result = (GLint)gl2psText((char const *)arg1,(char const *)arg2,arg3); resultobj = SWIG_From_int((int)(result)); if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); return resultobj; fail: if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); return NULL; } SWIGINTERN PyObject *_wrap_gl2psTextOpt(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; char *arg1 = (char *) 0 ; char *arg2 = (char *) 0 ; GLshort arg3 ; GLint arg4 ; GLfloat arg5 ; int res1 ; char *buf1 = 0 ; int alloc1 = 0 ; int res2 ; char *buf2 = 0 ; int alloc2 = 0 ; short val3 ; int ecode3 = 0 ; int val4 ; int ecode4 = 0 ; float val5 ; int ecode5 = 0 ; PyObject * obj0 = 0 ; PyObject * obj1 = 0 ; PyObject * obj2 = 0 ; PyObject * obj3 = 0 ; PyObject * obj4 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"OOOOO:gl2psTextOpt",&obj0,&obj1,&obj2,&obj3,&obj4)) SWIG_fail; res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, NULL, &alloc1); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "gl2psTextOpt" "', argument " "1"" of type '" "char const *""'"); } arg1 = (char *)(buf1); res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "gl2psTextOpt" "', argument " "2"" of type '" "char const *""'"); } arg2 = (char *)(buf2); ecode3 = SWIG_AsVal_short(obj2, &val3); if (!SWIG_IsOK(ecode3)) { SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "gl2psTextOpt" "', argument " "3"" of type '" "GLshort""'"); } arg3 = (GLshort)(val3); ecode4 = SWIG_AsVal_int(obj3, &val4); if (!SWIG_IsOK(ecode4)) { SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "gl2psTextOpt" "', argument " "4"" of type '" "GLint""'"); } arg4 = (GLint)(val4); ecode5 = SWIG_AsVal_float(obj4, &val5); if (!SWIG_IsOK(ecode5)) { SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "gl2psTextOpt" "', argument " "5"" of type '" "GLfloat""'"); } arg5 = (GLfloat)(val5); result = (GLint)gl2psTextOpt((char const *)arg1,(char const *)arg2,arg3,arg4,arg5); resultobj = SWIG_From_int((int)(result)); if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); return resultobj; fail: if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); return NULL; } SWIGINTERN PyObject *_wrap_gl2psSpecial(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLint arg1 ; char *arg2 = (char *) 0 ; int val1 ; int ecode1 = 0 ; int res2 ; char *buf2 = 0 ; int alloc2 = 0 ; PyObject * obj0 = 0 ; PyObject * obj1 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"OO:gl2psSpecial",&obj0,&obj1)) SWIG_fail; ecode1 = SWIG_AsVal_int(obj0, &val1); if (!SWIG_IsOK(ecode1)) { SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "gl2psSpecial" "', argument " "1"" of type '" "GLint""'"); } arg1 = (GLint)(val1); res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "gl2psSpecial" "', argument " "2"" of type '" "char const *""'"); } arg2 = (char *)(buf2); result = (GLint)gl2psSpecial(arg1,(char const *)arg2); resultobj = SWIG_From_int((int)(result)); if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); return resultobj; fail: if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); return NULL; } SWIGINTERN PyObject *_wrap_gl2psDrawPixels(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLsizei arg1 ; GLsizei arg2 ; GLint arg3 ; GLint arg4 ; GLenum arg5 ; GLenum arg6 ; void *arg7 = (void *) 0 ; void *argp1 ; int res1 = 0 ; void *argp2 ; int res2 = 0 ; int val3 ; int ecode3 = 0 ; int val4 ; int ecode4 = 0 ; void *argp5 ; int res5 = 0 ; void *argp6 ; int res6 = 0 ; int res7 ; PyObject * obj0 = 0 ; PyObject * obj1 = 0 ; PyObject * obj2 = 0 ; PyObject * obj3 = 0 ; PyObject * obj4 = 0 ; PyObject * obj5 = 0 ; PyObject * obj6 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:gl2psDrawPixels",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail; { res1 = SWIG_ConvertPtr(obj0, &argp1, SWIGTYPE_p_GLsizei, 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "gl2psDrawPixels" "', argument " "1"" of type '" "GLsizei""'"); } if (!argp1) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "gl2psDrawPixels" "', argument " "1"" of type '" "GLsizei""'"); } else { arg1 = *((GLsizei *)(argp1)); } } { res2 = SWIG_ConvertPtr(obj1, &argp2, SWIGTYPE_p_GLsizei, 0 ); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "gl2psDrawPixels" "', argument " "2"" of type '" "GLsizei""'"); } if (!argp2) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "gl2psDrawPixels" "', argument " "2"" of type '" "GLsizei""'"); } else { arg2 = *((GLsizei *)(argp2)); } } ecode3 = SWIG_AsVal_int(obj2, &val3); if (!SWIG_IsOK(ecode3)) { SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "gl2psDrawPixels" "', argument " "3"" of type '" "GLint""'"); } arg3 = (GLint)(val3); ecode4 = SWIG_AsVal_int(obj3, &val4); if (!SWIG_IsOK(ecode4)) { SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "gl2psDrawPixels" "', argument " "4"" of type '" "GLint""'"); } arg4 = (GLint)(val4); { res5 = SWIG_ConvertPtr(obj4, &argp5, SWIGTYPE_p_GLenum, 0 ); if (!SWIG_IsOK(res5)) { SWIG_exception_fail(SWIG_ArgError(res5), "in method '" "gl2psDrawPixels" "', argument " "5"" of type '" "GLenum""'"); } if (!argp5) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "gl2psDrawPixels" "', argument " "5"" of type '" "GLenum""'"); } else { arg5 = *((GLenum *)(argp5)); } } { res6 = SWIG_ConvertPtr(obj5, &argp6, SWIGTYPE_p_GLenum, 0 ); if (!SWIG_IsOK(res6)) { SWIG_exception_fail(SWIG_ArgError(res6), "in method '" "gl2psDrawPixels" "', argument " "6"" of type '" "GLenum""'"); } if (!argp6) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "gl2psDrawPixels" "', argument " "6"" of type '" "GLenum""'"); } else { arg6 = *((GLenum *)(argp6)); } } res7 = SWIG_ConvertPtr(obj6,SWIG_as_voidptrptr(&arg7), 0, 0); if (!SWIG_IsOK(res7)) { SWIG_exception_fail(SWIG_ArgError(res7), "in method '" "gl2psDrawPixels" "', argument " "7"" of type '" "void const *""'"); } result = (GLint)gl2psDrawPixels(arg1,arg2,arg3,arg4,arg5,arg6,(void const *)arg7); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_gl2psEnable(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLint arg1 ; int val1 ; int ecode1 = 0 ; PyObject * obj0 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"O:gl2psEnable",&obj0)) SWIG_fail; ecode1 = SWIG_AsVal_int(obj0, &val1); if (!SWIG_IsOK(ecode1)) { SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "gl2psEnable" "', argument " "1"" of type '" "GLint""'"); } arg1 = (GLint)(val1); result = (GLint)gl2psEnable(arg1); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_gl2psDisable(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLint arg1 ; int val1 ; int ecode1 = 0 ; PyObject * obj0 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"O:gl2psDisable",&obj0)) SWIG_fail; ecode1 = SWIG_AsVal_int(obj0, &val1); if (!SWIG_IsOK(ecode1)) { SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "gl2psDisable" "', argument " "1"" of type '" "GLint""'"); } arg1 = (GLint)(val1); result = (GLint)gl2psDisable(arg1); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_gl2psPointSize(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLfloat arg1 ; float val1 ; int ecode1 = 0 ; PyObject * obj0 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"O:gl2psPointSize",&obj0)) SWIG_fail; ecode1 = SWIG_AsVal_float(obj0, &val1); if (!SWIG_IsOK(ecode1)) { SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "gl2psPointSize" "', argument " "1"" of type '" "GLfloat""'"); } arg1 = (GLfloat)(val1); result = (GLint)gl2psPointSize(arg1); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_gl2psLineWidth(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLfloat arg1 ; float val1 ; int ecode1 = 0 ; PyObject * obj0 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"O:gl2psLineWidth",&obj0)) SWIG_fail; ecode1 = SWIG_AsVal_float(obj0, &val1); if (!SWIG_IsOK(ecode1)) { SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "gl2psLineWidth" "', argument " "1"" of type '" "GLfloat""'"); } arg1 = (GLfloat)(val1); result = (GLint)gl2psLineWidth(arg1); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } SWIGINTERN PyObject *_wrap_gl2psBlendFunc(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; GLenum arg1 ; GLenum arg2 ; void *argp1 ; int res1 = 0 ; void *argp2 ; int res2 = 0 ; PyObject * obj0 = 0 ; PyObject * obj1 = 0 ; GLint result; if (!PyArg_ParseTuple(args,(char *)"OO:gl2psBlendFunc",&obj0,&obj1)) SWIG_fail; { res1 = SWIG_ConvertPtr(obj0, &argp1, SWIGTYPE_p_GLenum, 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "gl2psBlendFunc" "', argument " "1"" of type '" "GLenum""'"); } if (!argp1) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "gl2psBlendFunc" "', argument " "1"" of type '" "GLenum""'"); } else { arg1 = *((GLenum *)(argp1)); } } { res2 = SWIG_ConvertPtr(obj1, &argp2, SWIGTYPE_p_GLenum, 0 ); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "gl2psBlendFunc" "', argument " "2"" of type '" "GLenum""'"); } if (!argp2) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "gl2psBlendFunc" "', argument " "2"" of type '" "GLenum""'"); } else { arg2 = *((GLenum *)(argp2)); } } result = (GLint)gl2psBlendFunc(arg1,arg2); resultobj = SWIG_From_int((int)(result)); return resultobj; fail: return NULL; } static PyMethodDef SwigMethods[] = { { (char *)"SWIG_PyInstanceMethod_New", (PyCFunction)SWIG_PyInstanceMethod_New, METH_O, NULL}, { (char *)"gl2psBeginPage", _wrap_gl2psBeginPage, METH_VARARGS, NULL}, { (char *)"gl2psEndPage", _wrap_gl2psEndPage, METH_VARARGS, NULL}, { (char *)"gl2psSetOptions", _wrap_gl2psSetOptions, METH_VARARGS, NULL}, { (char *)"gl2psGetOptions", _wrap_gl2psGetOptions, METH_VARARGS, NULL}, { (char *)"gl2psBeginViewport", _wrap_gl2psBeginViewport, METH_VARARGS, NULL}, { (char *)"gl2psEndViewport", _wrap_gl2psEndViewport, METH_VARARGS, NULL}, { (char *)"gl2psText", _wrap_gl2psText, METH_VARARGS, NULL}, { (char *)"gl2psTextOpt", _wrap_gl2psTextOpt, METH_VARARGS, NULL}, { (char *)"gl2psSpecial", _wrap_gl2psSpecial, METH_VARARGS, NULL}, { (char *)"gl2psDrawPixels", _wrap_gl2psDrawPixels, METH_VARARGS, NULL}, { (char *)"gl2psEnable", _wrap_gl2psEnable, METH_VARARGS, NULL}, { (char *)"gl2psDisable", _wrap_gl2psDisable, METH_VARARGS, NULL}, { (char *)"gl2psPointSize", _wrap_gl2psPointSize, METH_VARARGS, NULL}, { (char *)"gl2psLineWidth", _wrap_gl2psLineWidth, METH_VARARGS, NULL}, { (char *)"gl2psBlendFunc", _wrap_gl2psBlendFunc, METH_VARARGS, NULL}, { NULL, NULL, 0, NULL } }; /* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ static swig_type_info _swigt__p_FILE = {"_p_FILE", "FILE *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_GLenum = {"_p_GLenum", "GLenum *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_GLsizei = {"_p_GLsizei", "GLsizei *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_a_4__float = {"_p_a_4__float", "GL2PSrgba *|float (*)[4]", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_float = {"_p_float", "float *|GLfloat *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_int = {"_p_int", "int *|GLint *", 0, 0, (void*)0, 0}; static swig_type_info _swigt__p_short = {"_p_short", "short *|GLshort *", 0, 0, (void*)0, 0}; static swig_type_info *swig_type_initial[] = { &_swigt__p_FILE, &_swigt__p_GLenum, &_swigt__p_GLsizei, &_swigt__p_a_4__float, &_swigt__p_char, &_swigt__p_float, &_swigt__p_int, &_swigt__p_short, }; static swig_cast_info _swigc__p_FILE[] = { {&_swigt__p_FILE, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_GLenum[] = { {&_swigt__p_GLenum, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_GLsizei[] = { {&_swigt__p_GLsizei, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_a_4__float[] = { {&_swigt__p_a_4__float, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_float[] = { {&_swigt__p_float, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_int[] = { {&_swigt__p_int, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info _swigc__p_short[] = { {&_swigt__p_short, 0, 0, 0},{0, 0, 0, 0}}; static swig_cast_info *swig_cast_initial[] = { _swigc__p_FILE, _swigc__p_GLenum, _swigc__p_GLsizei, _swigc__p_a_4__float, _swigc__p_char, _swigc__p_float, _swigc__p_int, _swigc__p_short, }; /* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ static swig_const_info swig_const_table[] = { {0, 0, 0, 0.0, 0, 0}}; #ifdef __cplusplus } #endif /* ----------------------------------------------------------------------------- * Type initialization: * This problem is tough by the requirement that no dynamic * memory is used. Also, since swig_type_info structures store pointers to * swig_cast_info structures and swig_cast_info structures store pointers back * to swig_type_info structures, we need some lookup code at initialization. * The idea is that swig generates all the structures that are needed. * The runtime then collects these partially filled structures. * The SWIG_InitializeModule function takes these initial arrays out of * swig_module, and does all the lookup, filling in the swig_module.types * array with the correct data and linking the correct swig_cast_info * structures together. * * The generated swig_type_info structures are assigned staticly to an initial * array. We just loop through that array, and handle each type individually. * First we lookup if this type has been already loaded, and if so, use the * loaded structure instead of the generated one. Then we have to fill in the * cast linked list. The cast data is initially stored in something like a * two-dimensional array. Each row corresponds to a type (there are the same * number of rows as there are in the swig_type_initial array). Each entry in * a column is one of the swig_cast_info structures for that type. * The cast_initial array is actually an array of arrays, because each row has * a variable number of columns. So to actually build the cast linked list, * we find the array of casts associated with the type, and loop through it * adding the casts to the list. The one last trick we need to do is making * sure the type pointer in the swig_cast_info struct is correct. * * First off, we lookup the cast->type name to see if it is already loaded. * There are three cases to handle: * 1) If the cast->type has already been loaded AND the type we are adding * casting info to has not been loaded (it is in this module), THEN we * replace the cast->type pointer with the type pointer that has already * been loaded. * 2) If BOTH types (the one we are adding casting info to, and the * cast->type) are loaded, THEN the cast info has already been loaded by * the previous module so we just ignore it. * 3) Finally, if cast->type has not already been loaded, then we add that * swig_cast_info to the linked list (because the cast->type) pointer will * be correct. * ----------------------------------------------------------------------------- */ #ifdef __cplusplus extern "C" { #if 0 } /* c-mode */ #endif #endif #if 0 #define SWIGRUNTIME_DEBUG #endif SWIGRUNTIME void SWIG_InitializeModule(void *clientdata) { size_t i; swig_module_info *module_head, *iter; int found, init; clientdata = clientdata; /* check to see if the circular list has been setup, if not, set it up */ if (swig_module.next==0) { /* Initialize the swig_module */ swig_module.type_initial = swig_type_initial; swig_module.cast_initial = swig_cast_initial; swig_module.next = &swig_module; init = 1; } else { init = 0; } /* Try and load any already created modules */ module_head = SWIG_GetModule(clientdata); if (!module_head) { /* This is the first module loaded for this interpreter */ /* so set the swig module into the interpreter */ SWIG_SetModule(clientdata, &swig_module); module_head = &swig_module; } else { /* the interpreter has loaded a SWIG module, but has it loaded this one? */ found=0; iter=module_head; do { if (iter==&swig_module) { found=1; break; } iter=iter->next; } while (iter!= module_head); /* if the is found in the list, then all is done and we may leave */ if (found) return; /* otherwise we must add out module into the list */ swig_module.next = module_head->next; module_head->next = &swig_module; } /* When multiple interpeters are used, a module could have already been initialized in a different interpreter, but not yet have a pointer in this interpreter. In this case, we do not want to continue adding types... everything should be set up already */ if (init == 0) return; /* Now work on filling in swig_module.types */ #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: size %d\n", swig_module.size); #endif for (i = 0; i < swig_module.size; ++i) { swig_type_info *type = 0; swig_type_info *ret; swig_cast_info *cast; #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); #endif /* if there is another module already loaded */ if (swig_module.next != &swig_module) { type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name); } if (type) { /* Overwrite clientdata field */ #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: found type %s\n", type->name); #endif if (swig_module.type_initial[i]->clientdata) { type->clientdata = swig_module.type_initial[i]->clientdata; #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name); #endif } } else { type = swig_module.type_initial[i]; } /* Insert casting types */ cast = swig_module.cast_initial[i]; while (cast->type) { /* Don't need to add information already in the list */ ret = 0; #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: look cast %s\n", cast->type->name); #endif if (swig_module.next != &swig_module) { ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name); #ifdef SWIGRUNTIME_DEBUG if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name); #endif } if (ret) { if (type == swig_module.type_initial[i]) { #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: skip old type %s\n", ret->name); #endif cast->type = ret; ret = 0; } else { /* Check for casting already in the list */ swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type); #ifdef SWIGRUNTIME_DEBUG if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name); #endif if (!ocast) ret = 0; } } if (!ret) { #ifdef SWIGRUNTIME_DEBUG printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name); #endif if (type->cast) { type->cast->prev = cast; cast->next = type->cast; } type->cast = cast; } cast++; } /* Set entry in modules->types array equal to the type */ swig_module.types[i] = type; } swig_module.types[i] = 0; #ifdef SWIGRUNTIME_DEBUG printf("**** SWIG_InitializeModule: Cast List ******\n"); for (i = 0; i < swig_module.size; ++i) { int j = 0; swig_cast_info *cast = swig_module.cast_initial[i]; printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); while (cast->type) { printf("SWIG_InitializeModule: cast type %s\n", cast->type->name); cast++; ++j; } printf("---- Total casts: %d\n",j); } printf("**** SWIG_InitializeModule: Cast List ******\n"); #endif } /* This function will propagate the clientdata field of type to * any new swig_type_info structures that have been added into the list * of equivalent types. It is like calling * SWIG_TypeClientData(type, clientdata) a second time. */ SWIGRUNTIME void SWIG_PropagateClientData(void) { size_t i; swig_cast_info *equiv; static int init_run = 0; if (init_run) return; init_run = 1; for (i = 0; i < swig_module.size; i++) { if (swig_module.types[i]->clientdata) { equiv = swig_module.types[i]->cast; while (equiv) { if (!equiv->converter) { if (equiv->type && !equiv->type->clientdata) SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata); } equiv = equiv->next; } } } } #ifdef __cplusplus #if 0 { /* c-mode */ #endif } #endif #ifdef __cplusplus extern "C" { #endif /* Python-specific SWIG API */ #define SWIG_newvarlink() SWIG_Python_newvarlink() #define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr) #define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants) /* ----------------------------------------------------------------------------- * global variable support code. * ----------------------------------------------------------------------------- */ typedef struct swig_globalvar { char *name; /* Name of global variable */ PyObject *(*get_attr)(void); /* Return the current value */ int (*set_attr)(PyObject *); /* Set the value */ struct swig_globalvar *next; } swig_globalvar; typedef struct swig_varlinkobject { PyObject_HEAD swig_globalvar *vars; } swig_varlinkobject; SWIGINTERN PyObject * swig_varlink_repr(swig_varlinkobject *SWIGUNUSEDPARM(v)) { #if PY_VERSION_HEX >= 0x03000000 return PyUnicode_InternFromString(""); #else return PyString_FromString(""); #endif } SWIGINTERN PyObject * swig_varlink_str(swig_varlinkobject *v) { #if PY_VERSION_HEX >= 0x03000000 PyObject *str = PyUnicode_InternFromString("("); PyObject *tail; PyObject *joined; swig_globalvar *var; for (var = v->vars; var; var=var->next) { tail = PyUnicode_FromString(var->name); joined = PyUnicode_Concat(str, tail); Py_DecRef(str); Py_DecRef(tail); str = joined; if (var->next) { tail = PyUnicode_InternFromString(", "); joined = PyUnicode_Concat(str, tail); Py_DecRef(str); Py_DecRef(tail); str = joined; } } tail = PyUnicode_InternFromString(")"); joined = PyUnicode_Concat(str, tail); Py_DecRef(str); Py_DecRef(tail); str = joined; #else PyObject *str = PyString_FromString("("); swig_globalvar *var; for (var = v->vars; var; var=var->next) { PyString_ConcatAndDel(&str,PyString_FromString(var->name)); if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", ")); } PyString_ConcatAndDel(&str,PyString_FromString(")")); #endif return str; } SWIGINTERN int swig_varlink_print(swig_varlinkobject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) { char *tmp; PyObject *str = swig_varlink_str(v); fprintf(fp,"Swig global variables "); fprintf(fp,"%s\n", tmp = SWIG_Python_str_AsChar(str)); SWIG_Python_str_DelForPy3(tmp); Py_DECREF(str); return 0; } SWIGINTERN void swig_varlink_dealloc(swig_varlinkobject *v) { swig_globalvar *var = v->vars; while (var) { swig_globalvar *n = var->next; free(var->name); free(var); var = n; } } SWIGINTERN PyObject * swig_varlink_getattr(swig_varlinkobject *v, char *n) { PyObject *res = NULL; swig_globalvar *var = v->vars; while (var) { if (strcmp(var->name,n) == 0) { res = (*var->get_attr)(); break; } var = var->next; } if (res == NULL && !PyErr_Occurred()) { PyErr_SetString(PyExc_NameError,"Unknown C global variable"); } return res; } SWIGINTERN int swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) { int res = 1; swig_globalvar *var = v->vars; while (var) { if (strcmp(var->name,n) == 0) { res = (*var->set_attr)(p); break; } var = var->next; } if (res == 1 && !PyErr_Occurred()) { PyErr_SetString(PyExc_NameError,"Unknown C global variable"); } return res; } SWIGINTERN PyTypeObject* swig_varlink_type(void) { static char varlink__doc__[] = "Swig var link object"; static PyTypeObject varlink_type; static int type_init = 0; if (!type_init) { const PyTypeObject tmp = { /* PyObject header changed in Python 3 */ #if PY_VERSION_HEX >= 0x03000000 PyVarObject_HEAD_INIT(NULL, 0) #else PyObject_HEAD_INIT(NULL) 0, /* ob_size */ #endif (char *)"swigvarlink", /* tp_name */ sizeof(swig_varlinkobject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) swig_varlink_dealloc, /* tp_dealloc */ (printfunc) swig_varlink_print, /* tp_print */ (getattrfunc) swig_varlink_getattr, /* tp_getattr */ (setattrfunc) swig_varlink_setattr, /* tp_setattr */ 0, /* tp_compare */ (reprfunc) swig_varlink_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ (reprfunc) swig_varlink_str, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ 0, /* tp_flags */ varlink__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ #if PY_VERSION_HEX >= 0x02020000 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */ #endif #if PY_VERSION_HEX >= 0x02030000 0, /* tp_del */ #endif #if PY_VERSION_HEX >= 0x02060000 0, /* tp_version */ #endif #ifdef COUNT_ALLOCS 0,0,0,0 /* tp_alloc -> tp_next */ #endif }; varlink_type = tmp; type_init = 1; #if PY_VERSION_HEX < 0x02020000 varlink_type.ob_type = &PyType_Type; #else if (PyType_Ready(&varlink_type) < 0) return NULL; #endif } return &varlink_type; } /* Create a variable linking object for use later */ SWIGINTERN PyObject * SWIG_Python_newvarlink(void) { swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type()); if (result) { result->vars = 0; } return ((PyObject*) result); } SWIGINTERN void SWIG_Python_addvarlink(PyObject *p, char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) { swig_varlinkobject *v = (swig_varlinkobject *) p; swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar)); if (gv) { size_t size = strlen(name)+1; gv->name = (char *)malloc(size); if (gv->name) { strncpy(gv->name,name,size); gv->get_attr = get_attr; gv->set_attr = set_attr; gv->next = v->vars; } } v->vars = gv; } SWIGINTERN PyObject * SWIG_globals(void) { static PyObject *_SWIG_globals = 0; if (!_SWIG_globals) _SWIG_globals = SWIG_newvarlink(); return _SWIG_globals; } /* ----------------------------------------------------------------------------- * constants/methods manipulation * ----------------------------------------------------------------------------- */ /* Install Constants */ SWIGINTERN void SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) { PyObject *obj = 0; size_t i; for (i = 0; constants[i].type; ++i) { switch(constants[i].type) { case SWIG_PY_POINTER: obj = SWIG_InternalNewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0); break; case SWIG_PY_BINARY: obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype)); break; default: obj = 0; break; } if (obj) { PyDict_SetItemString(d, constants[i].name, obj); Py_DECREF(obj); } } } /* -----------------------------------------------------------------------------*/ /* Fix SwigMethods to carry the callback ptrs when needed */ /* -----------------------------------------------------------------------------*/ SWIGINTERN void SWIG_Python_FixMethods(PyMethodDef *methods, swig_const_info *const_table, swig_type_info **types, swig_type_info **types_initial) { size_t i; for (i = 0; methods[i].ml_name; ++i) { const char *c = methods[i].ml_doc; if (c && (c = strstr(c, "swig_ptr: "))) { int j; swig_const_info *ci = 0; const char *name = c + 10; for (j = 0; const_table[j].type; ++j) { if (strncmp(const_table[j].name, name, strlen(const_table[j].name)) == 0) { ci = &(const_table[j]); break; } } if (ci) { void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0; if (ptr) { size_t shift = (ci->ptype) - types; swig_type_info *ty = types_initial[shift]; size_t ldoc = (c - methods[i].ml_doc); size_t lptr = strlen(ty->name)+2*sizeof(void*)+2; char *ndoc = (char*)malloc(ldoc + lptr + 10); if (ndoc) { char *buff = ndoc; strncpy(buff, methods[i].ml_doc, ldoc); buff += ldoc; strncpy(buff, "swig_ptr: ", 10); buff += 10; SWIG_PackVoidPtr(buff, ptr, ty->name, lptr); methods[i].ml_doc = ndoc; } } } } } } #ifdef __cplusplus } #endif /* -----------------------------------------------------------------------------* * Partial Init method * -----------------------------------------------------------------------------*/ #ifdef __cplusplus extern "C" #endif SWIGEXPORT #if PY_VERSION_HEX >= 0x03000000 PyObject* #else void #endif SWIG_init(void) { PyObject *m, *d, *md; #if PY_VERSION_HEX >= 0x03000000 static struct PyModuleDef SWIG_module = { # if PY_VERSION_HEX >= 0x03020000 PyModuleDef_HEAD_INIT, # else { PyObject_HEAD_INIT(NULL) NULL, /* m_init */ 0, /* m_index */ NULL, /* m_copy */ }, # endif (char *) SWIG_name, NULL, -1, SwigMethods, NULL, NULL, NULL, NULL }; #endif #if defined(SWIGPYTHON_BUILTIN) static SwigPyClientData SwigPyObject_clientdata = { 0, 0, 0, 0, 0, 0, 0 }; static PyGetSetDef this_getset_def = { (char *)"this", &SwigPyBuiltin_ThisClosure, NULL, NULL, NULL }; static SwigPyGetSet thisown_getset_closure = { (PyCFunction) SwigPyObject_own, (PyCFunction) SwigPyObject_own }; static PyGetSetDef thisown_getset_def = { (char *)"thisown", SwigPyBuiltin_GetterClosure, SwigPyBuiltin_SetterClosure, NULL, &thisown_getset_closure }; PyObject *metatype_args; PyTypeObject *builtin_pytype; int builtin_base_count; swig_type_info *builtin_basetype; PyObject *tuple; PyGetSetDescrObject *static_getset; PyTypeObject *metatype; SwigPyClientData *cd; PyObject *public_interface, *public_symbol; PyObject *this_descr; PyObject *thisown_descr; int i; (void)builtin_pytype; (void)builtin_base_count; (void)builtin_basetype; (void)tuple; (void)static_getset; /* metatype is used to implement static member variables. */ metatype_args = Py_BuildValue("(s(O){})", "SwigPyObjectType", &PyType_Type); assert(metatype_args); metatype = (PyTypeObject *) PyType_Type.tp_call((PyObject *) &PyType_Type, metatype_args, NULL); assert(metatype); Py_DECREF(metatype_args); metatype->tp_setattro = (setattrofunc) &SwigPyObjectType_setattro; assert(PyType_Ready(metatype) >= 0); #endif /* Fix SwigMethods to carry the callback ptrs when needed */ SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial); #if PY_VERSION_HEX >= 0x03000000 m = PyModule_Create(&SWIG_module); #else m = Py_InitModule((char *) SWIG_name, SwigMethods); #endif md = d = PyModule_GetDict(m); SWIG_InitializeModule(0); #ifdef SWIGPYTHON_BUILTIN SwigPyObject_stype = SWIG_MangledTypeQuery("_p_SwigPyObject"); assert(SwigPyObject_stype); cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; if (!cd) { SwigPyObject_stype->clientdata = &SwigPyObject_clientdata; SwigPyObject_clientdata.pytype = SwigPyObject_TypeOnce(); } else if (SwigPyObject_TypeOnce()->tp_basicsize != cd->pytype->tp_basicsize) { PyErr_SetString(PyExc_RuntimeError, "Import error: attempted to load two incompatible swig-generated modules."); # if PY_VERSION_HEX >= 0x03000000 return NULL; # else return; # endif } /* All objects have a 'this' attribute */ this_descr = PyDescr_NewGetSet(SwigPyObject_type(), &this_getset_def); (void)this_descr; /* All objects have a 'thisown' attribute */ thisown_descr = PyDescr_NewGetSet(SwigPyObject_type(), &thisown_getset_def); (void)thisown_descr; public_interface = PyList_New(0); public_symbol = 0; (void)public_symbol; PyDict_SetItemString(md, "__all__", public_interface); Py_DECREF(public_interface); for (i = 0; SwigMethods[i].ml_name != NULL; ++i) SwigPyBuiltin_AddPublicSymbol(public_interface, SwigMethods[i].ml_name); for (i = 0; swig_const_table[i].name != 0; ++i) SwigPyBuiltin_AddPublicSymbol(public_interface, swig_const_table[i].name); #endif SWIG_InstallConstants(d,swig_const_table); SWIG_Python_SetConstant(d, "GL2PS_MAJOR_VERSION",SWIG_From_int((int)(1))); SWIG_Python_SetConstant(d, "GL2PS_MINOR_VERSION",SWIG_From_int((int)(3))); SWIG_Python_SetConstant(d, "GL2PS_PATCH_VERSION",SWIG_From_int((int)(3))); SWIG_Python_SetConstant(d, "GL2PS_EXTRA_VERSION",SWIG_FromCharPtr("")); SWIG_Python_SetConstant(d, "GL2PS_VERSION",SWIG_From_double((double)((1+0.01*3+0.0001*3)))); SWIG_Python_SetConstant(d, "GL2PS_COPYRIGHT",SWIG_FromCharPtr("(C) 1999-2009 C. Geuzaine")); SWIG_Python_SetConstant(d, "GL2PS_PS",SWIG_From_int((int)(0))); SWIG_Python_SetConstant(d, "GL2PS_EPS",SWIG_From_int((int)(1))); SWIG_Python_SetConstant(d, "GL2PS_TEX",SWIG_From_int((int)(2))); SWIG_Python_SetConstant(d, "GL2PS_PDF",SWIG_From_int((int)(3))); SWIG_Python_SetConstant(d, "GL2PS_SVG",SWIG_From_int((int)(4))); SWIG_Python_SetConstant(d, "GL2PS_PGF",SWIG_From_int((int)(5))); SWIG_Python_SetConstant(d, "GL2PS_NO_SORT",SWIG_From_int((int)(1))); SWIG_Python_SetConstant(d, "GL2PS_SIMPLE_SORT",SWIG_From_int((int)(2))); SWIG_Python_SetConstant(d, "GL2PS_BSP_SORT",SWIG_From_int((int)(3))); SWIG_Python_SetConstant(d, "GL2PS_SUCCESS",SWIG_From_int((int)(0))); SWIG_Python_SetConstant(d, "GL2PS_INFO",SWIG_From_int((int)(1))); SWIG_Python_SetConstant(d, "GL2PS_WARNING",SWIG_From_int((int)(2))); SWIG_Python_SetConstant(d, "GL2PS_ERROR",SWIG_From_int((int)(3))); SWIG_Python_SetConstant(d, "GL2PS_NO_FEEDBACK",SWIG_From_int((int)(4))); SWIG_Python_SetConstant(d, "GL2PS_OVERFLOW",SWIG_From_int((int)(5))); SWIG_Python_SetConstant(d, "GL2PS_UNINITIALIZED",SWIG_From_int((int)(6))); SWIG_Python_SetConstant(d, "GL2PS_NONE",SWIG_From_int((int)(0))); SWIG_Python_SetConstant(d, "GL2PS_DRAW_BACKGROUND",SWIG_From_int((int)((1 << 0)))); SWIG_Python_SetConstant(d, "GL2PS_SIMPLE_LINE_OFFSET",SWIG_From_int((int)((1 << 1)))); SWIG_Python_SetConstant(d, "GL2PS_SILENT",SWIG_From_int((int)((1 << 2)))); SWIG_Python_SetConstant(d, "GL2PS_BEST_ROOT",SWIG_From_int((int)((1 << 3)))); SWIG_Python_SetConstant(d, "GL2PS_OCCLUSION_CULL",SWIG_From_int((int)((1 << 4)))); SWIG_Python_SetConstant(d, "GL2PS_NO_TEXT",SWIG_From_int((int)((1 << 5)))); SWIG_Python_SetConstant(d, "GL2PS_LANDSCAPE",SWIG_From_int((int)((1 << 6)))); SWIG_Python_SetConstant(d, "GL2PS_NO_PS3_SHADING",SWIG_From_int((int)((1 << 7)))); SWIG_Python_SetConstant(d, "GL2PS_NO_PIXMAP",SWIG_From_int((int)((1 << 8)))); SWIG_Python_SetConstant(d, "GL2PS_USE_CURRENT_VIEWPORT",SWIG_From_int((int)((1 << 9)))); SWIG_Python_SetConstant(d, "GL2PS_COMPRESS",SWIG_From_int((int)((1 << 10)))); SWIG_Python_SetConstant(d, "GL2PS_NO_BLENDING",SWIG_From_int((int)((1 << 11)))); SWIG_Python_SetConstant(d, "GL2PS_TIGHT_BOUNDING_BOX",SWIG_From_int((int)((1 << 12)))); SWIG_Python_SetConstant(d, "GL2PS_POLYGON_OFFSET_FILL",SWIG_From_int((int)(1))); SWIG_Python_SetConstant(d, "GL2PS_POLYGON_BOUNDARY",SWIG_From_int((int)(2))); SWIG_Python_SetConstant(d, "GL2PS_LINE_STIPPLE",SWIG_From_int((int)(3))); SWIG_Python_SetConstant(d, "GL2PS_BLEND",SWIG_From_int((int)(4))); SWIG_Python_SetConstant(d, "GL2PS_TEXT_C",SWIG_From_int((int)(1))); SWIG_Python_SetConstant(d, "GL2PS_TEXT_CL",SWIG_From_int((int)(2))); SWIG_Python_SetConstant(d, "GL2PS_TEXT_CR",SWIG_From_int((int)(3))); SWIG_Python_SetConstant(d, "GL2PS_TEXT_B",SWIG_From_int((int)(4))); SWIG_Python_SetConstant(d, "GL2PS_TEXT_BL",SWIG_From_int((int)(5))); SWIG_Python_SetConstant(d, "GL2PS_TEXT_BR",SWIG_From_int((int)(6))); SWIG_Python_SetConstant(d, "GL2PS_TEXT_T",SWIG_From_int((int)(7))); SWIG_Python_SetConstant(d, "GL2PS_TEXT_TL",SWIG_From_int((int)(8))); SWIG_Python_SetConstant(d, "GL2PS_TEXT_TR",SWIG_From_int((int)(9))); #if PY_VERSION_HEX >= 0x03000000 return m; #else return; #endif } pyformex-0.8.6/pyformex/extra/pygl2ps/README0000644000211500021150000000354711700622767020527 0ustar benebene00000000000000# $Id$ ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # Python wrapper for gl2ps ======================== This is a Python wrapper for GL2PS, an OpenGL to PostScript Printing Library by Christophe Geuzaine. See http://www.geuz.org/gl2ps/. The wrapper was created by Benedict Verhegghe, and any questions about it should be addressed to Benedict.Verhegghe@ugent.be Installation in a pyFormex SVN tree ----------------------------------- - Go to the external/pygl2ps folder and enter the command (as root) ./pygl2ps.install all Standalone installation ----------------------- - download gl2ps - unpack it - copy the files gl2ps.i and setup.py to the created gl2ps-* directory - go to the gl2ps-* directory and do the following: swig -python gl2ps.i python setup.py build (as root) python setup.py install pyformex-0.8.6/pyformex/extra/pygl2ps/install.sh0000755000211500021150000000320711700635600021633 0ustar benebene00000000000000#!/bin/bash # $Id: install.sh 2140 2012-01-03 17:31:43Z bverheg $ ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # # Install Python wrapper for gl2ps # If you change version, you need to adapt this file, gl2ps.i, setup.py # and remove the gl2ps_wrap.c and gl2ps.py # [ "$1" = "all" ] || exit GL2PS=gl2ps-1.3.3 GL2PS_TGZ=$GL2PS.tgz GL2PS_URL=http://geuz.org/gl2ps/src/$GL2PS_TGZ [ -f $GL2PS_TGZ ] || wget $GL2PS_URL [ -f gl2ps_wrap.c ] || swig -python gl2ps.i rm -rf $GL2PS tar xvzf $GL2PS_TGZ cp *.c *.py $GL2PS pushd $GL2PS python setup.py build python setup.py install popd rm -rf $GL2PS pyformex-0.8.6/pyformex/extra/pygl2ps/gl2ps.i0000644000211500021150000001136111700635600021027 0ustar benebene00000000000000/* $Id: gl2ps.i 2140 2012-01-03 17:31:43Z bverheg $ -*- C -*- */ // // This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) // pyFormex is a tool for generating, manipulating and transforming 3D // geometrical models by sequences of mathematical operations. // Home page: http://pyformex.org // Project page: http://savannah.nongnu.org/projects/pyformex/ // Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) // Distributed under the GNU General Public License version 3 or later. // // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/. // /* Adapted for gl2ps-1.3.2 by Benedict Verhegghe Original by Toby White and Lothar Birk (Jan 2004) */ %module gl2ps %{ #include "gl2ps.h" %} typedef int GLint; typedef short GLshort; typedef float GLfloat; typedef GLfloat GL2PSrgba[4]; %typemap(in) FILE* { $1 = (FILE *) PyFile_AsFile($input); printf("BV: Received a file\n"); } /* Version number */ #define GL2PS_MAJOR_VERSION 1 #define GL2PS_MINOR_VERSION 3 #define GL2PS_PATCH_VERSION 3 #define GL2PS_EXTRA_VERSION "" #define GL2PS_VERSION (GL2PS_MAJOR_VERSION + \ 0.01 * GL2PS_MINOR_VERSION + \ 0.0001 * GL2PS_PATCH_VERSION) #define GL2PS_COPYRIGHT "(C) 1999-2009 C. Geuzaine" /* Output file formats (the values and the ordering are important!) */ #define GL2PS_PS 0 #define GL2PS_EPS 1 #define GL2PS_TEX 2 #define GL2PS_PDF 3 #define GL2PS_SVG 4 #define GL2PS_PGF 5 /* Sorting algorithms */ #define GL2PS_NO_SORT 1 #define GL2PS_SIMPLE_SORT 2 #define GL2PS_BSP_SORT 3 /* Message levels and error codes */ #define GL2PS_SUCCESS 0 #define GL2PS_INFO 1 #define GL2PS_WARNING 2 #define GL2PS_ERROR 3 #define GL2PS_NO_FEEDBACK 4 #define GL2PS_OVERFLOW 5 #define GL2PS_UNINITIALIZED 6 /* Options for gl2psBeginPage */ #define GL2PS_NONE 0 #define GL2PS_DRAW_BACKGROUND (1<<0) #define GL2PS_SIMPLE_LINE_OFFSET (1<<1) #define GL2PS_SILENT (1<<2) #define GL2PS_BEST_ROOT (1<<3) #define GL2PS_OCCLUSION_CULL (1<<4) #define GL2PS_NO_TEXT (1<<5) #define GL2PS_LANDSCAPE (1<<6) #define GL2PS_NO_PS3_SHADING (1<<7) #define GL2PS_NO_PIXMAP (1<<8) #define GL2PS_USE_CURRENT_VIEWPORT (1<<9) #define GL2PS_COMPRESS (1<<10) #define GL2PS_NO_BLENDING (1<<11) #define GL2PS_TIGHT_BOUNDING_BOX (1<<12) /* Arguments for gl2psEnable/gl2psDisable */ #define GL2PS_POLYGON_OFFSET_FILL 1 #define GL2PS_POLYGON_BOUNDARY 2 #define GL2PS_LINE_STIPPLE 3 #define GL2PS_BLEND 4 /* Text alignment (o=raster position; default mode is BL): +---+ +---+ +---+ +---+ +---+ +---+ +-o-+ o---+ +---o | o | o | | o | | | | | | | | | | | | +---+ +---+ +---+ +-o-+ o---+ +---o +---+ +---+ +---+ C CL CR B BL BR T TL TR */ #define GL2PS_TEXT_C 1 #define GL2PS_TEXT_CL 2 #define GL2PS_TEXT_CR 3 #define GL2PS_TEXT_B 4 #define GL2PS_TEXT_BL 5 #define GL2PS_TEXT_BR 6 #define GL2PS_TEXT_T 7 #define GL2PS_TEXT_TL 8 #define GL2PS_TEXT_TR 9 GLint gl2psBeginPage(const char *title, const char *producer, GLint viewport[4], GLint format, GLint sort, GLint options, GLint colormode, GLint colorsize, GL2PSrgba *colormap, GLint nr, GLint ng, GLint nb, GLint buffersize, FILE* stream, const char *filename); GLint gl2psEndPage(void); GLint gl2psSetOptions(GLint options); GLint gl2psGetOptions(GLint *options); GLint gl2psBeginViewport(GLint viewport[4]); GLint gl2psEndViewport(void); GLint gl2psText(const char *str, const char *fontname, GLshort fontsize); GLint gl2psTextOpt(const char *str, const char *fontname, GLshort fontsize, GLint align, GLfloat angle); GLint gl2psSpecial(GLint format, const char *str); GLint gl2psDrawPixels(GLsizei width, GLsizei height, GLint xorig, GLint yorig, GLenum format, GLenum type, const void *pixels); GLint gl2psEnable(GLint mode); GLint gl2psDisable(GLint mode); GLint gl2psPointSize(GLfloat value); GLint gl2psLineWidth(GLfloat value); GLint gl2psBlendFunc(GLenum sfactor, GLenum dfactor); pyformex-0.8.6/pyformex/extra/pygl2ps/gl2ps.py0000644000211500021150000001522311700622767021242 0ustar benebene00000000000000# This file was automatically generated by SWIG (http://www.swig.org). ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # Version 2.0.4 # # Do not make changes to this file unless you know what you are doing--modify # the SWIG interface file instead. from sys import version_info if version_info >= (2,6,0): def swig_import_helper(): from os.path import dirname import imp fp = None try: fp, pathname, description = imp.find_module('_gl2ps', [dirname(__file__)]) except ImportError: import _gl2ps return _gl2ps if fp is not None: try: _mod = imp.load_module('_gl2ps', fp, pathname, description) finally: fp.close() return _mod _gl2ps = swig_import_helper() del swig_import_helper else: import _gl2ps del version_info try: _swig_property = property except NameError: pass # Python < 2.2 doesn't have 'property'. def _swig_setattr_nondynamic(self,class_type,name,value,static=1): if (name == "thisown"): return self.this.own(value) if (name == "this"): if type(value).__name__ == 'SwigPyObject': self.__dict__[name] = value return method = class_type.__swig_setmethods__.get(name,None) if method: return method(self,value) if (not static): self.__dict__[name] = value else: raise AttributeError("You cannot add attributes to %s" % self) def _swig_setattr(self,class_type,name,value): return _swig_setattr_nondynamic(self,class_type,name,value,0) def _swig_getattr(self,class_type,name): if (name == "thisown"): return self.this.own() method = class_type.__swig_getmethods__.get(name,None) if method: return method(self) raise AttributeError(name) def _swig_repr(self): try: strthis = "proxy of " + self.this.__repr__() except: strthis = "" return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) try: _object = object _newclass = 1 except AttributeError: class _object : pass _newclass = 0 GL2PS_MAJOR_VERSION = _gl2ps.GL2PS_MAJOR_VERSION GL2PS_MINOR_VERSION = _gl2ps.GL2PS_MINOR_VERSION GL2PS_PATCH_VERSION = _gl2ps.GL2PS_PATCH_VERSION GL2PS_EXTRA_VERSION = _gl2ps.GL2PS_EXTRA_VERSION GL2PS_VERSION = _gl2ps.GL2PS_VERSION GL2PS_COPYRIGHT = _gl2ps.GL2PS_COPYRIGHT GL2PS_PS = _gl2ps.GL2PS_PS GL2PS_EPS = _gl2ps.GL2PS_EPS GL2PS_TEX = _gl2ps.GL2PS_TEX GL2PS_PDF = _gl2ps.GL2PS_PDF GL2PS_SVG = _gl2ps.GL2PS_SVG GL2PS_PGF = _gl2ps.GL2PS_PGF GL2PS_NO_SORT = _gl2ps.GL2PS_NO_SORT GL2PS_SIMPLE_SORT = _gl2ps.GL2PS_SIMPLE_SORT GL2PS_BSP_SORT = _gl2ps.GL2PS_BSP_SORT GL2PS_SUCCESS = _gl2ps.GL2PS_SUCCESS GL2PS_INFO = _gl2ps.GL2PS_INFO GL2PS_WARNING = _gl2ps.GL2PS_WARNING GL2PS_ERROR = _gl2ps.GL2PS_ERROR GL2PS_NO_FEEDBACK = _gl2ps.GL2PS_NO_FEEDBACK GL2PS_OVERFLOW = _gl2ps.GL2PS_OVERFLOW GL2PS_UNINITIALIZED = _gl2ps.GL2PS_UNINITIALIZED GL2PS_NONE = _gl2ps.GL2PS_NONE GL2PS_DRAW_BACKGROUND = _gl2ps.GL2PS_DRAW_BACKGROUND GL2PS_SIMPLE_LINE_OFFSET = _gl2ps.GL2PS_SIMPLE_LINE_OFFSET GL2PS_SILENT = _gl2ps.GL2PS_SILENT GL2PS_BEST_ROOT = _gl2ps.GL2PS_BEST_ROOT GL2PS_OCCLUSION_CULL = _gl2ps.GL2PS_OCCLUSION_CULL GL2PS_NO_TEXT = _gl2ps.GL2PS_NO_TEXT GL2PS_LANDSCAPE = _gl2ps.GL2PS_LANDSCAPE GL2PS_NO_PS3_SHADING = _gl2ps.GL2PS_NO_PS3_SHADING GL2PS_NO_PIXMAP = _gl2ps.GL2PS_NO_PIXMAP GL2PS_USE_CURRENT_VIEWPORT = _gl2ps.GL2PS_USE_CURRENT_VIEWPORT GL2PS_COMPRESS = _gl2ps.GL2PS_COMPRESS GL2PS_NO_BLENDING = _gl2ps.GL2PS_NO_BLENDING GL2PS_TIGHT_BOUNDING_BOX = _gl2ps.GL2PS_TIGHT_BOUNDING_BOX GL2PS_POLYGON_OFFSET_FILL = _gl2ps.GL2PS_POLYGON_OFFSET_FILL GL2PS_POLYGON_BOUNDARY = _gl2ps.GL2PS_POLYGON_BOUNDARY GL2PS_LINE_STIPPLE = _gl2ps.GL2PS_LINE_STIPPLE GL2PS_BLEND = _gl2ps.GL2PS_BLEND GL2PS_TEXT_C = _gl2ps.GL2PS_TEXT_C GL2PS_TEXT_CL = _gl2ps.GL2PS_TEXT_CL GL2PS_TEXT_CR = _gl2ps.GL2PS_TEXT_CR GL2PS_TEXT_B = _gl2ps.GL2PS_TEXT_B GL2PS_TEXT_BL = _gl2ps.GL2PS_TEXT_BL GL2PS_TEXT_BR = _gl2ps.GL2PS_TEXT_BR GL2PS_TEXT_T = _gl2ps.GL2PS_TEXT_T GL2PS_TEXT_TL = _gl2ps.GL2PS_TEXT_TL GL2PS_TEXT_TR = _gl2ps.GL2PS_TEXT_TR def gl2psBeginPage(*args): return _gl2ps.gl2psBeginPage(*args) gl2psBeginPage = _gl2ps.gl2psBeginPage def gl2psEndPage(): return _gl2ps.gl2psEndPage() gl2psEndPage = _gl2ps.gl2psEndPage def gl2psSetOptions(*args): return _gl2ps.gl2psSetOptions(*args) gl2psSetOptions = _gl2ps.gl2psSetOptions def gl2psGetOptions(*args): return _gl2ps.gl2psGetOptions(*args) gl2psGetOptions = _gl2ps.gl2psGetOptions def gl2psBeginViewport(*args): return _gl2ps.gl2psBeginViewport(*args) gl2psBeginViewport = _gl2ps.gl2psBeginViewport def gl2psEndViewport(): return _gl2ps.gl2psEndViewport() gl2psEndViewport = _gl2ps.gl2psEndViewport def gl2psText(*args): return _gl2ps.gl2psText(*args) gl2psText = _gl2ps.gl2psText def gl2psTextOpt(*args): return _gl2ps.gl2psTextOpt(*args) gl2psTextOpt = _gl2ps.gl2psTextOpt def gl2psSpecial(*args): return _gl2ps.gl2psSpecial(*args) gl2psSpecial = _gl2ps.gl2psSpecial def gl2psDrawPixels(*args): return _gl2ps.gl2psDrawPixels(*args) gl2psDrawPixels = _gl2ps.gl2psDrawPixels def gl2psEnable(*args): return _gl2ps.gl2psEnable(*args) gl2psEnable = _gl2ps.gl2psEnable def gl2psDisable(*args): return _gl2ps.gl2psDisable(*args) gl2psDisable = _gl2ps.gl2psDisable def gl2psPointSize(*args): return _gl2ps.gl2psPointSize(*args) gl2psPointSize = _gl2ps.gl2psPointSize def gl2psLineWidth(*args): return _gl2ps.gl2psLineWidth(*args) gl2psLineWidth = _gl2ps.gl2psLineWidth def gl2psBlendFunc(*args): return _gl2ps.gl2psBlendFunc(*args) gl2psBlendFunc = _gl2ps.gl2psBlendFunc # This file is compatible with both classic and new-style classes. pyformex-0.8.6/pyformex/extra/pygl2ps/setup.py0000644000211500021150000000343211700635600021340 0ustar benebene00000000000000# $Id: setup.py 2140 2012-01-03 17:31:43Z bverheg $ ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## from distutils.core import setup, Extension setup(name="pygl2ps", version="1.3.3", description="Wrapper for GL2PS, an OpenGL to PostScript Printing Library", author="Benedict Verhegghe", author_email="benedict.verhegghe@ugent.be", url="http://pyformex.org", long_description=""" Python wrapper for GL2PS library by Christophe Geuzaine. See http://www.geuz.org/gl2ps/ """, license="GNU LGPL (Library General Public License)", py_modules=["gl2ps"], ext_modules=[Extension("_gl2ps", ["gl2ps.c","gl2ps_wrap.c"], libraries=["GL"])]) pyformex-0.8.6/pyformex/extra/calpy/0000755000211500021150000000000011705105304017332 5ustar benebene00000000000000pyformex-0.8.6/pyformex/extra/calpy/README0000644000211500021150000000322111700622767020224 0ustar benebene00000000000000# ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # Installation procedure for calpy ================================ Calpy is a simple Finite Element framework with a Python interface. It can easily be interfaced with pyFormex. Some of the examples included with pyFormex use calpy. calpy.install will download, compile and install calpy and all that is needed to let it cooperate with pyFormex. By default the installation is done under /usr/local and you will need root privileges to do so. The full installation can be done with a single command: (sudo) ./install.sh all Dependencies: gfortran pyformex-0.8.6/pyformex/extra/calpy/install.sh0000755000211500021150000000372111700622767021356 0ustar benebene00000000000000#!/bin/bash ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # # This script helps with installing calpy from source: # # ./calpy_install get unpack make # sudo ./calpy_install install # ./calpy_install clean # # Use at your own risk if you do not understand what is happening! # VERSION=0.4-a5 NAME=calpy-$VERSION ARCHIVE=$NAME.tar.gz URI=ftp://bumps.ugent.be/pub/calpy/$ARCHIVE _get() { [ -f $ARCHIVE ] || wget $URI } _unpack() { rm -rf $NAME tar xvzf $ARCHIVE } _make() { pushd $NAME make popd } _install() { [ "$EUID" == "0" ] || { echo "install should be done as root!" return } pushd $NAME make install popd } _clean() { rm -rf $NAME rm -f $ARCHIVE } for cmd in "$@"; do case $cmd in get | unpack | patch | make | install | rename | clean ) _$cmd;; all ) _get;_unpack;_make;_install;_clean;; * ) echo "UNKNOWN command $cmd";; esac done pyformex-0.8.6/pyformex/extra/pyftgl/0000755000211500021150000000000011705105304017527 5ustar benebene00000000000000pyformex-0.8.6/pyformex/extra/pyftgl/README0000644000211500021150000000215111700622767020422 0ustar benebene00000000000000# ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # pyformex-0.8.6/pyformex/extra/pyftgl/install.sh0000755000211500021150000000275411700622767021560 0ustar benebene00000000000000#!/bin/bash # $Id: install.sh 1583 2010-10-11 16:49:51Z bverheg $ ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # # Install Python wrapper for ftgl # [ "$1" = "all" ] || exit PYFTGL=PyFTGL-0.4b PYFTGL_TGZ=$PYFTGL.tar.gz PYFTGL_URL=http://pyftgl.googlecode.com/files/$PYFTGL_TGZ [ -f $PYFTGL_TGZ ] || wget $PYFTGL_URL rm -rf $PYFTGL tar xvzf $PYFTGL_TGZ pushd $PYFTGL python setup.py build python setup.py install popd #rm -rf $PYFTGL pyformex-0.8.6/pyformex/extra/tetgen/0000755000211500021150000000000011705105304017510 5ustar benebene00000000000000pyformex-0.8.6/pyformex/extra/tetgen/README0000644000211500021150000000341011704570261020375 0ustar benebene00000000000000# ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # Installation procedure for tetgen ================================= Tetgen is a quality tetrahedral mesh generator and three-dimensional Delaunay triangulator. Website: http://tetgen.org pyFormex has a number of functions that interface with tetgen. To help our users with installing the tetgen program, we provide the 'tetgen.install' procedure in this directory. tetgen.install will download, compile and install tetgen and all that is needed to let it cooperate with pyFormex. By default the installation is done under /usr/local and you will need root privileges to do so. The full installation can be done with a single command (as root): ./install.sh all pyformex-0.8.6/pyformex/extra/tetgen/install.sh0000755000211500021150000000417111704575206021533 0ustar benebene00000000000000#!/bin/bash # $Id: install.sh 1583 2010-10-11 16:49:51Z bverheg $ ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # _usage() { cat < #include #include #include char* copyright = "postabq 0.1 (C) 2008 Benedict Verhegghe"; FILE * fil; /* Blocks and records A block consists of : - lead : 4 byte word with value 4096 (RECSIZE in bytes) - data : RECSIZE double words (512 * 8 = 4096 bytes) - tail : as lead A record consists of - NW (1) : number of (double) words - KEY (1) : record type - DATA (NW-2) : the data !!! Records may span the block boundary !!! Reading from file is done block by block. If we want to process records as a whole, we need to buffer at least 2 blocks. */ #define RECSIZE 512 #define BUFSIZE 2*RECSIZE int recnr = 0; int blknr = 0; int err = 0; int32_t lead,tail; union { double d[BUFSIZE]; int64_t i[BUFSIZE]; char c[8*BUFSIZE]; } data; int64_t nw,key; int64_t j, /* Pointer to current data */ jend, /* Pointer behind currently record */ jmax; /* Pointer behind currently filled buffer */ #define STRINGBUFSIZE 256 char s[STRINGBUFSIZE]; int explicit = 0; /* assume standard unless specified/detected */ int verbose = 0; int fake = 0; char* stripn(int64_t k,int64_t n,int strip) { s[0] = '\0'; int64_t m = 8*n; if (m > STRINGBUFSIZE) m = STRINGBUFSIZE; memmove(s,data.c + (8*k),m); while (s[--m] == ' ') {} s[++m] = '\0'; char* p = s; if (strip) { while (*p==' ') p++; } return p; } char* strn(int64_t k,int64_t n) { return stripn(k,n,0); } char* str(int64_t k) { return strn(k,1); } void do_element() { printf("D.Element(%d,",data.i[j++]); printf("'%s',[",str(j++)); while (j < jend) printf("%d,",data.i[j++]); printf("])\n"); } void do_node() { printf("D.Node(%d,[",data.i[j++]); int64_t j3 = j+3; if (j3 > jend) j3 = jend; while (j < j3) printf("%e,",data.d[j++]); if (j < jend) { printf("],normal=["); while (j < jend) printf("%e,",data.d[j++]); } printf("])\n"); } void do_dofs() { printf("D.Dofs(["); while (j < jend) printf("%d,",data.i[j++]); printf("])\n"); } void do_outreq() { int64_t * ip = data.i + j++; int flag = ip[0]; printf("D.OutputRequest(flag=%d,set='%s'",flag,str(j++)); if (flag==0) printf(",eltyp='%s',",str(j++)); printf(")\n"); } void do_abqver() { printf("D.Abqver('%s')\n",str(j++)); /* BEWARE ! Do not call str() multiple times in the same output instruction */ printf("D.Date('%s',",strn(j,2)); j += 2; printf("'%s')\n",str(j++)); printf("D.Size(nelems=%lld,nnodes=%lld,length=%f)\n",data.i[j],data.i[j+1],data.d[j+2]); } void do_heading() { printf("D.Heading('%s')\n",strn(j,jend-j)); } void do_nodeset() { printf("D.Nodeset('%s',[",stripn(j++,1,1)); while (j jmax) { /* record spans block boundary */ if (verbose) fprintf(stderr,"Record exceeds block boundary\n"); break; } jend = j+nw; if (jend > jmax) { fprintf(stderr,"ERROR: record seems to span more than 2 blocks\n"); return 1; } key = data.i[j+1]; recnr++; j += 2; if (process_data()) return 1; j = jend; /* in case the process_data did not process everything */ } } printf("D.Export()\n"); printf("# End\n"); fclose(fil); return 0; } void print_copyright() { fprintf(stderr,"%s\n",copyright); } void print_usage() { fprintf(stderr,"\nUsage: postabq [options] output.fil\n\ Converts an ABAQUS output file (.fil) into a Python script.\n\ The output goes to stdout.\n\ \n\ Options:\n\ -v : Be verbose (mostly for debugging)\n\ -e : Force EXPLICIT from the start (default is to autodetect)\n\ -n : Dry run: run through the file but do not produce conversion\n\ -h : Print this help text\n\ -V : Print version and exit\n\ \n"); } /* The main program loops over the files specified in the command line */ int main(int argc, char *argv[]) { int i,nerr,res,nfiles; char c; print_copyright(); /* Process command line options */ for (i=1; i 56vӖIXjFZ( _ UgD6> \$g Tc<ݯig$TD,^i-EZ>jFf>=ֈ1˲ǯ;DZ="(RYKg2/Ư?pZ&e` >o~T|1߻ ~*C3ѡ&`Ɩv(R׈}H +5 <|xT7EÏP=F5\j{@>z1{3!:K$'(5|(>jb&M)YRjQ Y}qZ`V8K |p乇P%,Rd1MޅHhk%RWle /AE{#E٬iL7f"CV""L`% n@O}~;&HZC E}QaֵDFЬxy )d4h4z-wZ~҆ ,7ÆdlQ 8RO3(% >kw[y_ , 7h=h Ҟ_TˤU䴯Dϯ ͘s-RnXL0 4 AέK!0fazT3<DyEfb#3 !NR8i߆I[cQze{P81^\]j$fk@%! (ZC pJߏ 1P >T-$5Wdw /ZnGIU"QrQQB3KrtyQx ~rߢ $ިCTV{V!`QP7fv>݉*·aH6 lT#%@'sT?`'*D`90U6n+Yz%;ڟ*K( J+,zP!R9kyΫh| \_G, o9I#ew7 4L=oۑFuJ\D0דȬl&]H`"Q%m%/=yq.~͇6; :(ɖ{C\Cj?O @@Tz0ݤ͌(GtD~at0fej 5ڱ\/i!4N2ON oU pt26K'I.HHbSe-- IjnݏodwoUQ0|# G+1NFx>][kk,Nn ըvLn>n":A75# Wc6Utpatage2Vf=vc=+*D ۯҼY8ejU C^T!?O|32;n OTd z Q [\(>I98]FuS&-@ZiOy)T FY`lxkt! L;΢+]>mhi@Dr)RNl= ec$NtΣ|bamP ~?i6ѷc_.UŁSG@iv)Ca2IENDB`pyformex-0.8.6/pyformex/icons/rotleft.xpm0000644000211500021150000000133110474005063020420 0ustar benebene00000000000000/* XPM */ static char *rotleft[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXXX XXXXXXXXXX", "XXX XX XX XXXXXX", "XXXX XX X XXXXX", "XXXXX XX XXX XXXX", "XXXX XX XXXX XXXX", "XXX X XX XXXXX XXX", "XXX XXX XX XXXXX XXX", "XXX XXXXXX XXXXX XXX", "XXXX XXXXX XXXX XXXX", "XXXX XXXXXXXX XXXX", "XXXXX XXXXX", "XXXXXXX XXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/point.xpm0000644000211500021150000000131711117433757020110 0ustar benebene00000000000000/* XPM */ static char * near_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " ", " ", " ", " ", " ", " ", " ....... ", " ....... ", " ....... ", " ....... ", " ....... ", " ....... ", " ....... ", " ", " ", " ", " ", " ", " ", " "}; pyformex-0.8.6/pyformex/icons/dist-angle.xpm0000644000211500021150000000132511607302705020775 0ustar benebene00000000000000/* XPM */ static char * dist_angle_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " .. ", " .. ", " .. ", " .. ", " ... ", " .. . ", " .. . ", " .. .. ", " .. . ", " .. . ", " ................ ", " ................ ", " ", " ", " . . ", " . . ", " ................ ", " ................ ", " . . ", " . . ", " "}; pyformex-0.8.6/pyformex/icons/view-xl-yd.xpm0000644000211500021150000000132510761615361020760 0ustar benebene00000000000000/* XPM */ static char * view_xl_yd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " ............... ", " .. . ", " . . ", " . ", " .. .. . ", " ... . ", " . . ", " ... . ", " .. .. . . . ", " . . . ", " .. . . ", " ... ..... ", " . ... ", " . . ", " . . ", " .. ", " ", " "}; pyformex-0.8.6/pyformex/icons/zoomall.xpm0000644000211500021150000000121710534353105020421 0ustar benebene00000000000000/* XPM */ static char * zoomall_xpm[] = { "22 22 2 1", " c None", ". c #000000", " ", " .................... ", " . . ", " . ..... ..... . ", " . ... ... . ", " . .... .... . ", " . . ... ... . . ", " . . ... . . . ", " . . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . . .. . . ", " . . ... ... . . ", " . .... .... . ", " . ... ... . ", " . ..... ..... . ", " . . ", " .................... ", " "}; pyformex-0.8.6/pyformex/icons/view-xl-zd.xpm0000644000211500021150000000132510730450407020753 0ustar benebene00000000000000/* XPM */ static char * view_xl_zd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " ............... ", " .. . ", " . . ", " . ", " .. .. . ", " ... . ", " . . ", " ... . ", " .. .. . ", " . ", " ..... . ", " .. ..... ", " . ... ", " .. . ", " ..... ", " ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-xl-yu.xpm0000644000211500021150000000132510761615361021001 0ustar benebene00000000000000/* XPM */ static char * view_xl_yu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . . ", " . . ", " .. . . ", " ... ... ", " . ..... ", " . . ", " . . . ", " .. .. .. . ", " ... . ", " . . ", " ... . ", " .. .. . ", " . ", " . . ", " .. . ", " ............... ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/happy.xpm0000644000211500021150000000132710474005063020067 0ustar benebene00000000000000/* XPM */ static char *happy[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXX XXXXXX XXXXXX", "XXXXX XX XXXX XX XXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXX XXXXXX XXXXXX", "XXXXXX XXXXXX XXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXX XXXXXXXXXX XXXX", "XXXXX XXXXXX XXXXX", "XXXXXX . . XXXXXX", "XXXXXX ........ XXXXXX", "XXXXXXX .... XXXXXXX", "XXXXXXXXX XXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/view-xl-zu.xpm0000644000211500021150000000132510730450407020774 0ustar benebene00000000000000/* XPM */ static char * view_xl_zu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " ", " ..... ", " .. . ", " . ... ", " .. ..... ", " ..... . ", " . ", " .. .. . ", " ... . ", " . . ", " ... . ", " .. .. . ", " . ", " . . ", " .. . ", " ............... ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/perspect.xpm0000644000211500021150000000122010540073371020564 0ustar benebene00000000000000/* XPM */ static char * perspect_xpm[] = { "22 22 2 1", " c None", ". c #000000", " ", " .. .. ", " .. .. ", " .......... ", " ... ... ", " .. .. ", " .. .. ", " ............ ", " .. .. ", " ... ... ", " ... ... ", " .. .. ", " .............. ", " ... ... ", " ... ... ", " ... ... ", " ... ... ", " ................ ", " ... ... ", " ... ... ", " ... ... ", " "}; pyformex-0.8.6/pyformex/icons/README0000644000211500021150000000213211655551533017105 0ustar benebene00000000000000# ## ## The files in this directory are part of the pyFormex project. ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: https://savannah.nongnu.org/projects/pyformex/ ## Copyright (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # pyformex-0.8.6/pyformex/icons/down.xpm0000644000211500021150000000132610474005063017714 0ustar benebene00000000000000/* XPM */ static char *down[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXX XXXXXX", "XXXXXXX XXXXXXX", "XXXXXXXX XXXXXXXX", "XXXXXXXXX XXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/wireframe.xpm0000644000211500021150000000240610654036551020734 0ustar benebene00000000000000/* XPM */ static char *dummy[]={ "32 32 6 1", "d c #0000ff", "# c #008000", "a c #00ffff", ". c None", "b c #ff0000", "c c #ff00ff", "................................", "...............#a...............", ".............###aaa.............", "...........####aaaaaa...........", ".........####..aa..aaaa.........", "......#####....aa....aaaaa......", "....#####......aa......aaaaa....", "..####.........aa.........aaaa..", ".###...........aa...........aa#.", ".#bb...........aa...........###.", ".bbbb..........aa..........###a.", ".bb.bb.........aa.........##.aa.", "..bb.bbb.......aa.......###.aa..", "..bb..bbb......aa......###..aa..", "..bb....bb....ccaa....##....aa..", "...bb....bb..cccaaa..##....aa...", "...bb.....bbbc....a###.....aa...", "...bb.....cbbb....###a.....aa...", "....bb..ccc..bb..##..aaa..aa....", "....bb.ccc....bb##....aaa.aa....", "....bbcc.......b#.......aaaa....", ".....cc........bb........ca.....", "......cc.......bb.......cc......", ".......cc......bb......cc.......", "........cb.....bb.....cc........", ".........bb....bb....cc.........", "..........bb...bb...dc..........", "...........bb..bb..dd...........", "............bb.bb.dd............", ".............bbbbdd.............", "..............bbbd..............", "...............bb..............."}; pyformex-0.8.6/pyformex/icons/userview.xpm0000644000211500021150000000133210474005063020613 0ustar benebene00000000000000/* XPM */ static char *userview[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXX XXX", "XXX XXXXXX XXX", "XXXX ..... XXXX", "XXXXXX ......... XXXX", "XXXXXX ..... XXXXX", "XXXXXX .... XXXXXX", "XXXXXXX .. XXXXXX", "XXXXXXX .. XXXXXXX", "XXXXXXX . XXXXXXX", "XXXXXXXX . XXXXXXXX", "XXXXXXXX .. XXXXXXXX", "XXXXXXXXX . XXXXXXXX", "XXXXXXXXXX XXXXXXXXX", "XXXXXXXXXXXX XXXXXXXXX", "XXXXXXXXXXXX XXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/view-zl-xd.xpm0000644000211500021150000000132510761615361020761 0ustar benebene00000000000000/* XPM */ static char * view_zl_xd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " .............. ", " .. . ", " . . ", " . ", " ..... . ", " .. . ", " . . ", " .. . ", " ..... . ", " . ", " . ", " .. .. . ", " ... ..... ", " . ... ", " ... . ", " .. .. ", " ", " "}; pyformex-0.8.6/pyformex/icons/project.xpm0000644000211500021150000000121710540073371020413 0ustar benebene00000000000000/* XPM */ static char * project_xpm[] = { "22 22 2 1", " c None", ". c #000000", " ", " ... ... ", " ... ... ", " ................ ", " ... ... ", " ... ... ", " ... ... ", " ................ ", " ... ... ", " ... ... ", " ... ... ", " ... ... ", " ................ ", " ... ... ", " ... ... ", " ... ... ", " ... ... ", " ................ ", " ... ... ", " ... ... ", " ... ... ", " "}; pyformex-0.8.6/pyformex/icons/view-zl-yd.xpm0000644000211500021150000000132510761615361020762 0ustar benebene00000000000000/* XPM */ static char * view_zl_yd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " ............... ", " .. . ", " . . ", " . ", " ..... . ", " .. . ", " . . ", " .. . ", " ..... . . . ", " . . . ", " .. . . ", " ... ..... ", " . ... ", " . . ", " . . ", " .. ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-zl-xu.xpm0000644000211500021150000000132510761615361021002 0ustar benebene00000000000000/* XPM */ static char * view_zl_xu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " .. .. ", " ... . ", " . ... ", " ... ..... ", " .. .. . ", " . ", " . ", " ..... . ", " .. . ", " . . ", " .. . ", " ..... . ", " . ", " . . ", " .. . ", " .............. ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/pyformex-scallop.png0000644000211500021150000004451710755606074022255 0ustar benebene00000000000000PNG  IHDRK pHYs› IDATx[ '{yB7hx;S[Kd1@ @=e dDQϺDPH@  s-PbzZ)2qϿ4~>秴`Bֵ0N]Կ0K[tO!  x3ZkŔr2# ,w`weqy ӌC׽+]=DpWwWţ? ĵ8,`A)Bȶc6󇃞Eh;[;d%| 0cttc6k ǀw@`oFpHs'b Ŭ^M޽oOY%0-B  4C~gVVL 㗎.sY(Tz^G 1(fvʼ `LԷ-/i}YT @ΪT6cڤWeULT70;L LHp& '`Y>?k?[rS9W,0_(-pʟe# IJSoE]LGϪ= /25+b J 0 fCTѲؗ)x[D kpDG^y ^/ @ qbyJ}ùNGď?=%c@QZmM nPS2?5ocYu ~?Z! ާu ?b7>կ;@cj;KWG\g%WMK:J+.zg=`ZfwQ+0 ut {h$F@w9OC͏T+|OK&qkS]$@nx'VN`Z~)4L*{ V!'|mi4@7C=~\hU_앳G: ??Ig N _t72Ϡ M=dȬ,"SʔMfׂ} p" l9i^^ 7&khi]3e-Z'LܻrJy %6Y8?w=sZ7H 1. a.kra[4S>PNV5KO] ڧt/6bєiU9Ϡ02vtղagPqJZ[kp  aK;3GL/R8^!ĕ[6FW@SZ L[ְ.@]@u%j> )`њfx{πf qxe >@iZkQbA ;z Nx2W~yy2S%&+I?%S~M0ю)|KeSq^cbڤ6ˇGT=uѡM)G{;ʕw/sE {SY +wK~]S;L= m{JVZipŒⰩ7yZ dsw d]V7/W<]\Snl*YwCE6a_ӋF[4׋,;uL ,z&he9sݢz%JL*2,X$d>E.Y [яQ᩾k7h`[tw"4TE%J#'f8 Jb_@ײdjhIGVdz%SІw_9,36[y."]B eS12tcQ鐞O/Q5?e)YvlPkSp ڊ`g)K0V)5) lE #Vڳ_u^qˊ^sӕqdl>`谒Oċ?rl3 `?ͮ9_zno**7}謩wc.Q8Re^J+p%d ϔY$fiD9t0UxSA:i.d%3 WLs u?6/#eԟ)׽uoY reZ8z7/梛qV8uaMqSYV3‡UnϻYԲIP P8 9,b|L{)w^Kn;welMlv"~_s|`X9i:™rbԥq=*dr CYrwS͏޶/ww D}yo$nq$=/,4n| O=ׅvσ [tGAݿސI-QŽz3~6baqpjK7习7]PL ,TDsg 4XH&.1ƛ10TW$맆ˢ^q4Br:Y]@Z@PQv2)̒9QpL '(U>xF} `qώYVRZr:x- Le:[ƚì̈́ٺO-DɲnX"D)V1|zL-wf&ջt-@_]XT_v_9sm^I6/HxoaL=wemF¶{toL9I,G;"pb,v1WW׌}vLƢV|^5ڜF/G<A"`ԓy0W?iT"Ŗ8-9sN6*7*֓Ubmh-?`sfEj4gP76'AͱY83N[I:`wvԜG୻>|hL}QQrZ1c)~9{~̜g\.0sϰ3Uˎ@Wɓ|~R/]S#TgQ3O߳)U>]9&ND3)MR%$j)SЫ,.߫8-_q)MlpڀJv4fCdY!D(.[̜X\qF/^,ڽGJ='he:C4>1V C%q*,ȡQi8'θUDxqO_g$,pﹾLk0L2[%{VU|ZN/˔]hE\EXkNg}X7K.s@W=d1|{_"3Z-aÛ]KiSL\:\-V!vZJ,(jepzl ;8`9z)Gn]YBֱooaerH(q}:YEߟ<* rG~mo:Nnvrq̩M_:jk<&.6]Df3Y:6 8%-E5kT<2 TUmpςhCaqzY '[kA3_\3{%(א22~x_`c4z׏n{63"sX]hDW̬N u7c~lo7G@ "`!OʉWV#0Ѳ~ҵ>OQuхtP9Ui TqiXh[s{e&t'v뽮9dYMfЭW]lD} ,ˀKōFf^.Hd)y%VA~R-i(.+H=ZX,?SsY(961 E=a )0F5U%%ҋ2W֒,l7@9pc w@;ĕW`lwu}2|`{fP~Z'[܋  ~P߿{ O=Ǻ8DU9 Ql7 .ѳY^3Gx|%~ձ@ sz-12K2Xq@,K|}ӉVRZ`-*&l\׃.id:;P~H7TR ͚않)_Bu"-6Mb Fɦkܺ.]xXAEqEhnWYiOjy{c|n;ũw1DB:Rz MrޱB,a40jǙڼ/n6w4{L u1q}}}=vRu.ST9L355$9,GsQ|->9@9gS@_XȦ T'zlC+0>Lpvٕ4uE>lr NXT 8ųʼq*`ݕUi*#y48 hk-#cJNxb ?J88&.p b~ j=ZuX~ؓX ίS.,:u|:  c|6Q8V-VdG5F꛳$|I"]@0֧eC x[#טEYk˛cZ/F{Vw&dr V+}OlkU߻j>?+'n;_iu@ɖmO*2c+9Pq6Y^C}-l靕轾 CZ^£tYʜ~d%.!stF9E"`oɼ</3V*/Dޱ,qiWg)So?oI--x >y4X[;f50eMXL9b|-9/뺚+`+M hDy/},|HVKX'WV_|~OPb!`Cx gYLx6כ.~̽ĞJ "m bv =1H#\B7R_OpWE˚iBz2P@V9^z_Ou2??|{}Ӌ|AYP6zPvÇ<, 5xCO V"ghrL}Sl>JȖ;2K;8=([/m?'/}0 yR*{'`!v<~[ ac@%xT̪^!iz'9%dV-W_4()9p28}CL`YKvJ*ho1G{Ji߻Y]u=] \s+EqkZ~kuG)Cv!dr=omw?T+J9,b|>6$^_k1N*J|t\fKH7 O\Ŧ B,es0y0Сj89& 4 Bp ߧl=Ǜ 댂OSa&(j%N> # ".A{,j ߇=naꨧ+7 XY5SV+ϢtЉ`]{kYKxp|CQOWԢ>sJMo$ =Ŏ:z>8)3F=]]b]3**l!]H/C gw̪l}#xM IDAT]5sjހH[zƻI5u]w5 0gEEn;,]qh˱w|fAT*dyWЂg&b6]N OD4풆bˌ:]Z"d,VkK={~~m@3-Sg"S<("d*\XQzt(jkFҮi"&QOd/_8 Q)_ALNu7>Bq L\ڔIw@@Y-]襽}E+5 u Y W<9A!x*񩛦gL; a;ҫ^RLzgvٕ/v建OgbxcԴ_%g5ӬѨҕ4s _{x6ٸd]MIW*%6eJMm珞 n*"juȺƢzSyԣ"+&͊ 4`f!ħϹk=fJ/3 !!d- %^J`0p0imgYW3\{  i ơ ??} 26Ph#ڞfj$9q]HAǮfVj/ wõy0%*+0?r\3+_q8;gz{W!@b tyA ]]cڧ{'W-gǵL'CpAhV5m WdJ>^*̔uqWq={)xϩ] ݬU9;#.w7q{\%Zg$]fzV,>64w4<^o!߇{ԅ$~I@uxΙ<^,^~>yO ; g'Z;[cY/2UI]S&HASz"őZv~Fhx{cuz=};N3W-nHM %3Gsl`^Wx/+2l ϒ]">`י-?/x?D8рrHzVVֲ2{.rr 8'qf>]֡7aNxeMbiYo^z2"'ӳ`S^LI^L:/`1Y-G7zml V2pGY]x߻Ph.=f15Ti;ji$hveSoXVIS0GR=:T-#9vh {eu%Y|Zf ÏGUh‡Gf7`Sf +VlnF.It4gEZŕ]jf-YL2s6T\_Ū}~87?rPۈ>0] ӱoϭ5 'ـ-Іiz#y}|g!t>Rn]{=SMɺ}} e!yo 3^֗H 73EidVwfGɐK*Ķjl.Z\@pP?;:8q/`WqAzes\$Q< >=o.^-T{Eg]z@ Zx~["x<<*^<1#+gEJlEԧ60ezE;3w=͐]##%'<{YPu3-> !?7}ud? ;0TV:DFSkO7y|8y,q~p%6fY,gS+Y!z{/߱{d,zYhVղl3F"lѥ9LrҪ"AbYkmqIw+;h@dUF3m09D^zg0DBWBgx%qp#6mjN ;{ h$dwF x&@0R R=Q@7Ѵp/@xITSp nojdHkGv$fb`Z}̮{왺%vjGoe!3<mne_,=Md\xVȳ <3kC`΀E~^E35V{TAv16pе uehX#2DM<"|x?_Jı/OANB*}Ta$~<7HKA_kn\K\?ȣI-*ngTunu 0ـzK/ѭעdB CW l!uYdIW7V *Mv_$U5rX-)C&֟4<$
    osV70ڒ[>V'|&=%ʖ֫CSǭvbY$IUk<'ٖZSl}tx A:bb? 2?$o2Ήu[(96T\zƏJ{8GyI9{&w.jsF:*ٶ[G1^W'JQl͇Ɨ8jrH|tlÂYzj#3Assn2tLFJls>y"/'IF%a'4馶b,?\<]Xpf6-<zf`PpN|v(1Urȁv$$cȞ1*d $;뗓!nd$X>;/2XΖ$q&Z{Mpb`k1`xjqȩ( ۞wxg>eV,I(cf; 0Zi̦10+U#Jsstha#1p EW8e^|ڹn[$}k I2i,Fw8T,1,>lԾ.*6In X0\[͊]vk;IjtJsr7T&? ,v]+CvYQO!a͙yGnt[RPZH>f#/Xoiq3q'C+ldXXude8Y-RO'1~"9ls*=8\(?v]9 a#,8\;dh1"Ϟ]h<%,9_ _%7!hES&j/<}oXZd/=n%&S/gi!B\^'!llIO|>?*2,\lX36ϿC|C^Y%𗞺d C٫:m8粫g q4_߆e'CΞʰT҅6Ry|#άRVyJZ 40&l\2X9zcz IV𖌚IxW|E&0f=\p5iStcw-pm6l ݎ'dPSc6ڞ$p*=j+po;搈.;o+h&$9F&D N<ɯL~6r&{ K Cx1pK<~-'S[A^@gH찏^;_*M)tfi]Q>5 qm}`+gݰqiv ̰}Lgԑ:,*ql*k3(7D 4iXS5uN!ZK;UW=OL9;eeJ3`8 ;v( mg;vFtC[QSfo)h=/)/ɩMUlXj[(6J2-ٓw)xhqh1ϯ㋤Ο|QS* >Q6{gyriϐ*BZbV]kz`%lgQ>fT78x853^>l @ `xҼˎE#e5Td~$" 7yd83{Fg5w)ɁA#04/ܞg#Ee ֛Un;ۺ,k qXQ8{ٷ O2 ~YZ{\d' ]?Uu޷Ze-PZj j؍l_Sγ&ȖU/pg AԶIϧ钭o—}پq$I MZB)p dn(GG` L*Ȗo;>Kkb(Zm!{٫4Nx1:M [ NݤWi>c/@)SBquX]cVɛ+#NhRy^ 6l@=‡׬W/wSU4;\nNmqr XY -)Z#6/2;^P2 ^ZsfoKa>N}6d@2Y7//JhRZ \r3֞+wp|u;3=tŶ;}<[wXHșξ#t]yjZi*vgmXhL#) 馳X[?I<[:Er7]]TL 5 xצW$gAUCG>^7-,[%ɓ]ЕmXM qonzT1Fzv6eElg]TdœfN*+|ع(%|V6ҡ Fu*Dƨ1ޠ2 fSu S"OnmSאk?"C؃ia!MXrg?֫؞$dٺ?dӎ& w`KҐ<;k2X)`9>nVs&m%hyCШR~QXOf>"+bjAWD 3X#5$9R׼>bm[,6 g]K1hBNi. ^%c:_瘊NnTXuc I94kaa3/93mہCXPdVjY?C;_;=m6/oF o&zU *>_sU _..CrbGBqFvxܕ\[V`/ǑϤ H:U?85@}7  I 5yJ|S G|H%d^<( SfX+'/?VV aaLqcrªJi8Z7;uMs b{Y23ka{lHs?-ҰB嗣}.>w8.{P_E kb*m{ϑIC S5q^t-(vv}eCi%6,Pb/=XApUsFos4=`n6h^l4Hvԇ3uL >;5`][Twww]^3m@Annk][RU%n{<還&L2ۭ< H: MMNHf9U<ݭ֣DZ@}`n  i"pP6R# B΄€ `;`[L؁8D톦ΙmX"!svdž 9&Us M4 C3޶axWu^!{g!}:naF) 9i 骼hp3a:'GV섮;=mO>w#aB>FbwA7뺱b)[5]p~@H& 9U3I3#[iAud #+*B0jS1u}o!t}-Dx-8HNT v u{L-‡5[?dv?66p2/eysϯ@e!Oï+C@b38p"َfYVu}: yl$ X dXX~ΦU Pl@s攻f]Ѱ]%w׵9wE*dSZgC6HAoCI5t.oHNx6Qi)SX;,iڿ ކ 3P (ygf6  eO| CNds>'r= Dwͯ oFN7PQξQo~[%.T8er7`LekkRVf3` KHa+ 3ҽ-_ # ?# 7`# NF 4AuyY9n1pLY"`P"~=$@ ~ 73h0 @ @` 0 @ @J9IENDB`pyformex-0.8.6/pyformex/icons/right.xpm0000644000211500021150000000132710474005063020063 0ustar benebene00000000000000/* XPM */ static char *right[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXX XXXXXXX", "XXXXXXXXXXXXXX XXXXXX", "XXXXXXXXXXXXXX XXXXX", "XXXXXXXXXXXXXX XXXX", "XXX XXX", "XXX XXX", "XXXXXXXXXXXXXX XXXX", "XXXXXXXXXXXXXX XXXXX", "XXXXXXXXXXXXXX XXXXXX", "XXXXXXXXXXXXXX XXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/view-zl-yu.xpm0000644000211500021150000000132510761615361021003 0ustar benebene00000000000000/* XPM */ static char * view_zl_yu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . . ", " . . ", " .. . . ", " ... ... ", " . ..... ", " . . ", " . . . ", " ..... .. . ", " .. . ", " . . ", " .. . ", " ..... . ", " . ", " . . ", " .. . ", " ............... ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/zoomout.xpm0000644000211500021150000000133110474005063020455 0ustar benebene00000000000000/* XPM */ static char *zoomout[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXX XXXXXX XXX", "XXX XXXXXXXXXX XXX", "XXX XXXXXXXX XXX", "XXX X XXXXXX X XXX", "XXX XX XXXX XX XXX", "XXXXXXX XXXX XXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXX XXXXXXXXX", "XXXXXXXXX XXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXX XXXX XXXXXXX", "XXX XX XXXX XX XXX", "XXX X XXXXXX X XXX", "XXX XXXXXXXX XXX", "XXX XXXXXXXXXX XXX", "XXX XXXXXX XXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/far.xpm0000644000211500021150000000131611104342703017510 0ustar benebene00000000000000/* XPM */ static char * far_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " ", " ", " ..... ", " ......... ", " ... ... ", " .. .. ", " .. .. ", " .. . . .. ", " .. . . .. ", " .. . .. ", " .. . . .. ", " .. . . .. ", " .. .. ", " .. .. ", " ... ... ", " ......... ", " ..... ", " ", " ", " "}; pyformex-0.8.6/pyformex/icons/unhappy.xpm0000644000211500021150000000133110655054537020440 0ustar benebene00000000000000/* XPM */ static char *unhappy[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXX XXXXXX XXXXXX", "XXXXX XX XXXX XX XXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXX XXXXXX XXXXXX", "XXXXXX XXXXXX XXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXX XXXXXXXX", "XXXXXX XXXX XXXXXX", "XXXXX XXXXXXXX XXXXX", "XXXXX XXXXXXXXXX XXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/left.xpm0000644000211500021150000000132610474005063017677 0ustar benebene00000000000000/* XPM */ static char *left[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXX XXXXXXXXXXXXXX", "XXXXXX XXXXXXXXXXXXXX", "XXXXX XXXXXXXXXXXXXX", "XXXX XXXXXXXXXXXXXX", "XXX XXX", "XXX XXX", "XXXX XXXXXXXXXXXXXX", "XXXXX XXXXXXXXXXXXXX", "XXXXXX XXXXXXXXXXXXXX", "XXXXXXX XXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/view-yr-xd.xpm0000644000211500021150000000132510761615361020766 0ustar benebene00000000000000/* XPM */ static char * view_yr_xd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " .............. ", " . .. ", " . . ", " . ", " . . . ", " . . . ", " . .. . ", " . ... ", " . . ", " . . ", " . . . ", " . .. .. .. ", " ..... ... ", " ... . ", " . ... ", " .. .. ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-yr-xu.xpm0000644000211500021150000000132510761615361021007 0ustar benebene00000000000000/* XPM */ static char * view_yr_xu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " .. .. ", " . ... ", " ... . ", " ..... ... ", " . .. .. ", " . . . ", " . . . ", " . .. . ", " . ... ", " . . ", " . . ", " . . . ", " . .. ", " . . ", " . .. ", " .............. ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-yr-zd.xpm0000644000211500021150000000132510761615361020770 0ustar benebene00000000000000/* XPM */ static char * view_yl_zd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " .............. ", " . .. ", " . . ", " . ", " . . . ", " . . . ", " . .. . ", " . ... ", " . . ", " . . ", " . . . ", " . ..... .. ", " ..... .. ", " ... . ", " . .. ", " ..... ", " ", " "}; pyformex-0.8.6/pyformex/icons/smooth.xpm0000644000211500021150000000235010654036551020262 0ustar benebene00000000000000/* XPM */ static char *dummy[]={ "32 32 4 1", "b c #3232ff", "# c #39ed39", ". c None", "a c #ff1919", "................................", "................................", "...............##...............", ".............######.............", "...........##########...........", ".........##############.........", "......####################......", "....########################....", "..############################..", "..############################..", "..aa########################bb..", "..aaa######################bbb..", "...aaa####################bbb...", "...aaaaa################bbbbb...", "...aaaaaa##############bbbbbb...", "....aaaaaa############bbbbbb....", "....aaaaaaa##########bbbbbbb....", "....aaaaaaaaa######bbbbbbbbb....", ".....aaaaaaaaa####bbbbbbbbb.....", ".....aaaaaaaaaa##bbbbbbbbbb.....", ".....aaaaaaaaaaabbbbbbbbbbb.....", "......aaaaaaaaaabbbbbbbbbb......", ".......aaaaaaaaabbbbbbbbb.......", "........aaaaaaaabbbbbbbb........", ".........aaaaaaabbbbbbb.........", "..........aaaaaabbbbbb..........", "...........aaaaabbbbb...........", "............aaaabbbb............", ".............aaabbb.............", "..............aabb..............", "...............ab...............", "................................"}; pyformex-0.8.6/pyformex/icons/view-iso1.xpm0000644000211500021150000000132010761615361020571 0ustar benebene00000000000000/* XPM */ static char *view_iso1[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " .. ", " .... ", " ...... ", " .. ", " .. ", " .. ", " .. ", " .. ", " .. ", " .... ", " .. .. ", " ... ... ", " . .. .. . ", " .... .... ", " ... ... ", " .... .... ", " ", " ", " ", " " }; pyformex-0.8.6/pyformex/icons/view-iso2.xpm0000644000211500021150000000132411140332743020566 0ustar benebene00000000000000/* XPM */ static char * view_iso2_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " ", " ", " . ", " ... ", " ..... ", " . ", " . .... ", " . ... ", " . .... ", " . .. . ", " . .. ", " . .. ", " . .. ", " . .. . ", " ... .. ", " ............... ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-iso3.xpm0000644000211500021150000000132411140332743020567 0ustar benebene00000000000000/* XPM */ static char * view_iso3_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " .... ", " ... ", " ... ", " .. . ", " .. ", " . ", " . .. ", " .. .. ", " .......... ", " .......... ", " .. .. ", " . .. ", " . ", " .. ", " .. . ", " ... ", " ... ", " .... ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-yr-zu.xpm0000644000211500021150000000132510761615361021011 0ustar benebene00000000000000/* XPM */ static char * view_yr_zu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " ..... ", " . .. ", " ... . ", " ..... .. ", " . ..... ", " . . . ", " . . . ", " . .. . ", " . ... ", " . . ", " . . ", " . . . ", " . .. ", " . . ", " . .. ", " .............. ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/matrix.xpm0000644000211500021150000000121411104342703020241 0ustar benebene00000000000000/* XPM */ static char * matrix_xpm[] = { "22 22 2 1", " c None", ". c black", " ", " ", " ", " .... .... ", " .... .... ", " .. .. ", " .. .. ", " .. .. ", " .. .. ", " .. .. ", " .. .. ", " .. .. ", " .. .. ", " .. .. ", " .. .. ", " .. .. ", " .. .. ", " .... .... ", " .... .... ", " ", " ", " "}; pyformex-0.8.6/pyformex/icons/twistleft.xpm0000644000211500021150000000133310474005063020770 0ustar benebene00000000000000/* XPM */ static char *twistleft[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXX XXXXXXXXXXXX", "XXXXXX XXXXXX XXXXX", "XXXXX XXXXXXX XXXX", "XXXX XXXXXXXXX XXX", "XXXX XXXXXXXX XX", "XXX XXXXXXXX XXXX", "XXX XXXX XXXX XXXX", "XXX XXXX XXXX XXXX", "XXX XXXX XXXX XXXX", "XXX XXXXXXXXXXX XXXX", "XXXX XXXXXXXXX XXXXX", "XXXX XXXXXXXXX XXXXX", "XXXXX XXXXX XXXXXX", "XXXXXX XXXXXXX", "XXXXXXXX XXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/near.xpm0000644000211500021150000000131711104342703017666 0ustar benebene00000000000000/* XPM */ static char * near_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " ", " ", " ..... ", " ......... ", " ... ... ", " .. .. ", " .. .. ", " .. .. ", " .. ... .. ", " .. ... .. ", " .. ... .. ", " .. .. ", " .. .. ", " .. .. ", " ... ... ", " ......... ", " ..... ", " ", " ", " "}; pyformex-0.8.6/pyformex/icons/home.xpm0000644000211500021150000000132610474005063017675 0ustar benebene00000000000000/* XPM */ static char *home[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXX XXXXXXXXXXXXXXX", "XXXXX XXX XXXXXXXXXX", "XXXXX X XXXXXXXX", "XXXXX XXXXXX", "XXXX XXXX", "XXX XXX", "XXXXX .......... XXXXX", "XXXXX ...... . XXXXX", "XXXXX ...... . . XXXXX", "XXXXX ...... . XXXXX", "XXXXX .......... XXXXX", "XXXXX ... .... XXXXX", "XXXXX ... X .... XXXXX", "XXXXX ... X .... XXXXX", "XXXXX ... X .... XXXXX", "XXXXX ... X .... XXXXX", "XXXXX X XXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/pyformex-logo-small.png0000644000211500021150000011666311162224431022652 0ustar benebene00000000000000PNG  IHDR^wpbKGD X pHYs   vpAg^IDATx}w@S ! I 5 {(kݵkok[ .ւ]^?͙3gfBhРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA 4hРA thˮ},2B>ms"&掳e=4hРA@ 84'}9>d.YDNjw(˄UHW̶U dJK~jX}B6'*oL{b14hРAA 84+1jRdALi׬dHZ=i_7C>ik\fDUǂU}\VɺA 4-Ȇi9ѧs햺vῆOǫW?|2^ENy5!5}Zirz@A d ~ 4 h7l{d*NX< 9xuz?_4hРC? J%d9ӯAm?9m8a+a%Z6Y;V!$hwA7&,y34m|fNs%Gwyy]S<2ޘ7y?=4hР ZCoEtn8 4߭GmͲ^y?^i^_ʬҮhn(VV$ȔlrǝzHBj7Ruy"o hfLV:yX62v ^Ṧw_˭P^}PtmnAʗq[;Z,]oc9~dDž 4@ 84ԩŤΦ@3d pΫ~/l7xyO{B./NPTA~dCO%oAGt[Y2o|UVc??٩qpyv\tn$պ:f?@-{Cj)w oɨ>>UŦ2D,-S15+kc7Ǫ&/q( ~޴|v6߫! X5fdv#0lj 4W@ 84D!!(Ojiyh)MCuY򟵂A|$U'r:aOg6*O:\D<~U0JCAxRU֐2[d{BHsiq;9ms OiNޭ)TabN ?+ !A~Xύ'8gFAj< Rc?J7R6jnϼ <4'3S7V[;}A{ VGΗ8u"ϺCY_+fc*r/4ho3mw Et2!;[hz̰O9!ȃ6t(nzE~]s1c7+2_~Y,z. 潛_Ue3^I0gx~Z9Qߏ&Ҙ_GT$nFsgٶ:W!2o/$Ȓ^vnU&PO=Uo*WH!i*m<ΐ@ʃf.u#{6_zᨸ|WFӾca5] x&I 1Ž8gkQ=kۆ I[ZXQHpڶwYt+kfkAV{mHC0CX~E}xʢ}tݡ5{ˤ߼Wd͈ϟ _gݜQꌰϬSOևG4:MϜ8͓W7KϫퟯŷH,l' im)ǯM>Lb :%Uz'7N9Z'pBgj,[C. 㞰Z^RffFWqMY eB."γ)0 JZY,guB-yUWe JKW|=f|<+uF575_bW朾lݼ9&_tw}gnY̸7"?ލ{z Zr4#*n~ ǡwJ;AZQi HH+طZ`EHݰ@TS BꀁlTwoWeyl=ȠYl 0[|̳oj]XꄈLc7+2XγYvXْ[0GM.QCTҷK黦&WY0v0-Q 8y_5C?Bj@$}1|ƅȼaRz H=`=&@p2?s>bߺ U WAh#Z3d4pi6ƫbK.p=to  ۟W|dO,μ.I5)=,6Zǿp$:5%YtRz-dԡrm+BH->r1> g Xߣn:[dحnU!Sщ=b.-| SzjrwpJ!ʵ_ '~CMelljӛNM8Z] v~NDu=Q0>1q[$0{ Дz췊="dQ,E"<xiֿ(vA i5 4zOLa&a 5C"?Hl R[{Z:z Ьr"P  6L;v0mmA?0`Wb{T0oQnM!H`! i'[JOZ_\phwAINMYU AOĔ8 ~r }Rኞڞo횁i$GQr b8֧'hj%?.7n:q9ԣ ԣ"BwM!$0氼Iiz3E"W5#dR=?uul?&l'|UޅCj}u >|6}}]Z{|5W@M u=cqʓ_7"/jcb&ɤcG#>I ǂwAh~p M'3*6eU.jPQt-P`ZBj5R~X?M*HUIXc'XnoAڶVESl#jMl'[;eOM~Sﵫ_.MVc;S5ϧ,=g˳oU[[܈O_.pzarO#sҤ[|Y'rhM v{[YKܶx52E(rdhe8txU2{bg ).8#bX#~<<\}bSE@'v@P}Bp &Wh7V]1/CY莋l>u޹eqc}4@V@6฾m wҷз_%jIa&xWu(W]Yԧe48AXa?TV˥\9q k K\U;hĉJ&tx\}Iй́%X/UoY() ( œ jnj5Ohou]|C`w6v+ ]CMƖnTi?j~Te d͊#g*/: ~m 3^rU|i.=p[ɋ$VUZ#-9/$]נ=k4؜֯Nd)EY(ݚ2yy]uŃ\K!2GmJm\wuc~ymm.2$\3p7QA_[lxȸȟB?R.~O4R{ԸK!LT@?nk[]~?ym貖?)4 ὼF~r[|~Qq2[\&J"X~؎];it,*6/馘]Lk@ߕ FoS[C=KmcZg)'q3G@y>CuWzKvNo@8 7mh+/2G Opjbַ7&\ 'YЧ`XmUєznP/xބtb@>bt^g!}3A}Ԟ0Oxw_b(thWARW˴؞L=E@Xy21?xgF%r3=\^B|ؠ׆t~1֩&ٕl=Ԇ$ 6Vz-C~Xu|S S(' |È},QSo|7T_=5S'/f}W:f)йvn=CYqԕDKa=3. X&߯{dC;P#qkin>P'fTꁎ1?Gì1guׄfQp)c/䊧+mzS#{/@;Z^#A+äz/;h^$a$T.l,u=`"r^:8QXw"ک&ݺ[\Fo˜MYyN= Zf[uiQE?j/O:_LovwNvJU-T\0 *4Foڑ##2N5wp[BF)zZdѱNXqDQ~oΘi&34Uyh(ƅ& gdpf5x3b\ĸ}B-555NOKm;H?}c5!o3s}Dܐ {maWxI߷9E=5"{#f:g%P/7weUhR zt[o= +7o$<<軀uDnVL.Oovo9A9BCXJxP8T/VZmqLy^**4t'Te$)ISշ$6jYyMNլ:ROwwړ6B)p 'xQ"ZtJz-dnUPg^0O;L&G. ;sw{jD #nb{zQzҮ;fR2n=L(Q@JTlЕnwQ]뜕\[N+x^V5OBւS;G[ىjm.kw(+[0.%/g F~R|5$?B?x 4@s1ttկJZ{~̉؜ithnDqkjh2x)MblNS/PζOTeL56<] ijbrѴB <Zi LqGo6zip( 5ρwtڴƸ<O3} 5K4 hwU0b;=^^&x豕ʢҪڻ%}ojPPiƝ)-q|Z]>eM?w۬2lq| mnN3]ritLv* O5׏qU8Qq}*j6oIhoiSwyitMW_mp!#uHǪ.0o ,;`~PgPS=ma~;JPazQue+j?-ҟ  K?}Ĭ>UB 0$ ]0уCټ ORu }/k.@ON}uBã zN)_3Zdl/0Y lWf.~,Z@~d+ Id$_w`#j[i,86QUeGX<e-נJ!h?=|C/Q-|E" !Dyqlyۚr܅zi9YW*F']ZꟉwiP`W4H9+#bo=GD~~(dk~e =v e'Wohymgϖ?%bO@V4z,n?( l'?N+ݖ4޿w p~>S;gfL|86@LwЃJՀ'&Mb"wrCʓ&~&) 3xhE%[ )X) XD. MN/D;9!UZ/Ms.i1X /M?13<*5rr[~a}'<(k$W- wT#"fΊ/8,`05a_6rLX(w17!*"h|o!r !ݏ udv,ݞ,6tm^B s+vE)8t/^k7V¯o9JscrcZ,eWvZwR#P!Uu+㲅&}jۙV\Y 4,B%U 18N7s}GRPʱFqI?}1;>;kWQ]i=ܺ^*e;!4hm *'fO@Ū UV(4jB#kܴkPn$PLd(ek a<&DZ;Jj7:VĠ Qca(_G++-R3Wز?uvBqMY,kÜB~ q!0 lcy׶[x[o_;0fBz쎁rhxvi.{"5'-Ϲ樫Wja#)1!mẃfnY,ݮ Pmi~xACQpEm f6'f~\jB%^O?Ar:6fIC- PB49E!z_ڸEĬp0h…"Ĺ?˖JdA^P^ ln=&6AG 59(pQcjS`ԭ'2x,Zh CdY3Lº?(0«`x ?pA]P7^ j8=(5߱A~#}ũbJm7e~~6.v2Zsc`ܷ̂qS i_9^ڭt=+/OO/]>v}EՁ)ппaH!u?o0=n;Wr(U(Oq Mt]H/Co݂}9o.:%<+߮1K=|Ͼ~fa-dhi!Ȓkʣi4~)gr\6{N'QȠ. k%dLZ`zCJxJٖlIPx;Ζ'Oٽ=9`!wߠt[.Niv*rs>2vy^Kx{˻ta}oQEӋ }~Nظs⏷9t_l;g?0zgKMN wPMs n 7&#=~DH_7)?OEԲi[2xć_%C,Kr۴S&) 'Կ-G:b{ӞRo1 Rg|.3%ālɕ43%)'/qk,{_j稲7cwntG nk5*? \T())1I M3 R+Tri 5/x)=?9_$;<.q17vjw#\nm!?< Efy]o%<"C: VXk*BU Mxjj|Q5 @B ~H]0}Δ(tLn/vj+\v`<-ү5ZxҪ.Gd4a<IC"ӥEP7;GezQS7E3n<{N}]hV7k-vnݫ+WȽpw,!!}Y5 D* ݀&V%߄6OoFc'#7o4OӡKތgnΫte5!4d|͵!uylz8үhn(quuqɋzVU@?Q< "R5*t&/~`>ƹ"wmCOjzhjN 0߭WSLG õB!j mp||Jg괦!=:qgN#+Ki'y{{1p*{ϫ%ϱFE! `@?=a6_z̻ h;s")u쓹o7w=øG}yv7m ºn2} ]~e;C}8A? j?5+6 a6e?LPڡ TN%&dj-M3>Bn '?]_z "o3[j6M+ RzL˘ͭUR4ٲ+l'~vٖ1_swɲYƪEĞWCېߤUĎ^)AUqZKUhsաCy./@&p()k_PTZge[5gvj)fk={nw iSaBZ;mwp535].T V2@'wV=}줭e`|bpҳq)."<~@軎ς)w\E] x\W.Fxؿ5T~'7:y}Ϻլ#gGB`}Őd+%_h蕬7*8Xeh m}yFwƄl3~_}dC/KN## ȐtHdV8@lT9. MD.x|2s=0׹֞ ^'.fYweQLCqHLAnj@V@xkj\W*+M {9uD3ul}gB랕֋NJ;yk3VrGe3ma +d6n'5$<1y[:7 -.ߕ6s7o= tyqݐ?5 gC8 RQq2עR CkіM]IyCz ;jбU+_v+zupWXifOAV⢘;ympnʬd-re>`liR(t~ :{ۣzޙSy~?IkwGo*bl]]-I DO)ŨiT˭hnqGa@OrTFJ_ 0Oq,UE8V.  ^lQz+cx 5c3>hWB+`7ֹI]M,8l#!$8SۨCb_aUao7˻ϕŌ{-9JNIZ}ʰTS8CHSY`EM#74tdŲ4Fu Grpm†K;r\ϹHxjGL  Wsl(XGm\ l5`-{jYכNZow1`r#Q(J|0Gn~ϼU)GSF''wNlԮѯ _@ {hh}H/m滿6j\׿ބPVy |EˑFs]s䛱`hrersIf7NYH:Ɨ2&9Z30ϖxszXtޏpxN"4c\tE f.\EHx -}*\>PYNP2h?e.WO{ ]/D8gp^zF|a9ґ;WEy5TkG(@7`<@}!yo_< P 7XE8sTJjP37h )vc\\Cޥ7hrN9\<=Vν[<n=r{ףM}s_KVGoBrxl5htl;q &x ҁ\HƷ=nl]QDA[^&rW<|\|\T|UPGGm^|長@涁.hfܳn9|Pd`&G:6ɻ娪?j\F+\_1?oνW73uH3=eSRNvc6 ܒ&D)9\&+'6J8J'\!R12 -@ɝDT oEiF%R5H/m E\6g}wr5la5k{#3ݕ\4)+Ή&Nh0'iAIMjzh-o6gOƾ=f+iɒɅ&(r̜9vty3vI˺Fu~vsgZ,q-ZŹ '}2xzx7gX[ܦ/@MEQ3UJ3Yq_ĻlZ14f9\4H+QcŴ M ,GoQArl>w3mNZ˛W_<եk LJ8zyb,moՄ8[ҁ. aC64zFEVSi`g7"bFtj\f$meS,|r+y \Ge) 69Okj g/Dɽ:3<^htܿZ֤Xe^ P"#SypC߮ 5#wF _㡠a)q"8K9u5CO3^JQSJE|l|}^`E ta'#j0w r&-ww6z0ҿ5Mo]b\ef O:ټ;Tf>o %&3ףS lH^COL~Þg̬\V/Hsؐ mQdFS|Y+ǃAYK'tU&9+t=.γʆī";(_(ͧg0{u&ğ# ! ]Qf)'$ݽ Υ}3#kHBJ49 uʓ'ٵMaZjpʑ"T;$ NZtG FܱUT~RNXʹwB<td£vJAzjɮÃ%S3Lq:{eiZ`4BPG0E:.3ECjjngދɉ'8L @FǠnc)7 !D;ZUeTŮQ~RNw_j7Ty1xq: BIHTyS DS0? "H~t&}OJ`+CA e8y2x+oygg gOKUY^ML߁;ڨQ݄~@ EgܠTm_ MxQ D ONH` J!p{{bb BBH ٦5V#?BP9jvPs$;N86O!.yi cxx;o3lH O2]tQe5 >kjesƍM$  ew0_`XႮ@}eبQb='SKSp`BlWAWHiLB33U9eB _lYB:°V:o8rV"F61q 9l? i:A!\4a2C}PwkBԜآHE>oa&Q0k~33U:HBLMPI @YY|,W^X Ύޮ>|_A& f | ! g`ʆ\2\ J>ًuR) ,KJts!ȰDhce*G© "GU"'P6YT]jzQ5( MHFg(Ņ['j'w\¢Ӆ+%pʼni6 KWq"h~:E e6ݴҔ'$ԥ8C㎯yo,)5 X|5Hȸ(z臢'һS􍂆+pgϧҵ;\=&DFF \()Ub }g@{&(@Q姡 nHX-ڸpW↥5! J -,ǘwMj' "⽽Sqm :%.`#p|PSTjp'섂/?|h|7TJߢG Vx9=jާM(wӻQ9^z5jUcFG]86qWO! oux]Z<-Ǎ 5S ]lRCb .MӲzxr&?tt+*ͱQ/=CvlBSh* Ps9 8LآS2>'dFjw4J9!䪓ڂ~U9ssÉd %%[H+p`rHmQቿq!kZ˼\%?v$ *, fOdͬTTKWD?wE|ר;pUQR+FpaQFz>jyR(B}/5 Ujz))%qpf 7ҝ} ,QE&j-AF*pk/61uh%20|2JEmP]흸 k=!h o5֓jjTyW~C'\jտ/؟K7tX,.s!;Q }쩻(@٠LilљMWNOAPSSUa{8r԰YcR! ^"iQ>(H7w eHGF|+UÒAdٵ{T/6I!q!ʪD[qRu Fry.!O|~/ɎYg@Ki&B?/Ym7# hRrnFRJûVq]r#z ʗG>ߛ&;.j4&$$& e-ihz༐AEZgGSH}ρ`w-5F) JjD'B ;j%#޿7Q0ΰ޷*:xib׶NJ! !9!!|fY[3.J%;ހǝm R{d&$|"cNL;j*.jŨ O/eM=63(/cY?R7&hAN_]rQ.՚<"lSQh;E `(PJ$QkN|jJco^$;7T?BȨC50hB0Vد>zW5RN&.mW ]=iS;9K鑑JP1. }[,*5{^cIư6g\GvXײ|PaVW[ LKpKɃ{1S6ո`t Ndgc7&6BM MrZ8YB5: x Jli̯wZw8l4-QmauHiw)(n Q"} CE'THB<-Bm#nlrV!A,C%4瑆OW"k~uUnZ@G9qNMsn6.@vJUfFQZj*9ҏn;5)yhl↛gBPhNuRN0 u .5 jK臖=.Xq>4HcBHw)Bp#ꛙ|_-sM78 [vv3w*@>u j,ޞ嬎0c2"ȩ!Ϝ# "aLIߖٜ"4E:3,"t)ވ*H!$|1XkK@%g" MDNԀ-z``+ *KULQ5nD_C1mpc$/}X8a?L/6!*+&[s+wO-zm^i >:qqqpC]nC Ur~PJpS)O)ԩ:32`5.T`lP#׀ KTjp)VjQa/zTkPի wLV8>Dߦڤܟ%{F fA7 v,+ !}ڠB> UOGPPYU[>(M;j\M8T#V`t~ǻ9~7\_F"B=ѧOUuD#G'?kƺTޞVeN)uW\ZQ@@ Ee~ %ues@?W)&3#}ܺ/fG(Aaw4oj>y:Q091A6IiBLh@SM/ƈ'kA]тcmu&F:6v4q!]0=C͎{B,KSQw}Jn#@sYz[oiĭwH-һc-& eNqp,yH єhE|8kS!-FB4;Pymй) 6䛃M5^8l&qvWrY 3o(M0CUSܔ?PCqp,-, Bq# {;fJe*4-dd@I!DBg;)=5Bbw3k3,!z؎taӠ{ZYMkFAA;OPVc=Q4vﶋA`Fw^fQ0DH=QV{\е(h)UHO4j),ܙ}/ μU[6g\FNvH]LvN?ݒ.Gm[ߣOGOՇU_T{9bS%Xir- )C8GU7&Lc:20>dԣ?j9OY\~xvw+o >&\ȯ109K[B18- $Ɵ'!] nHBi?O )#OHqA9OCE׫M"W㆖ ljT4yoD q<=P,v/ .(|h]F ‹ R?wS{6P3hUvFb2an8hya_,z[7!VU ڟp>,DI7J~5w*[' U^f w`EIcRûl؍ Rvg_W^78o&!AYTG\ką*xܫY'c24HQvGzB w|'>geLh4) oDN!},|.I?K~`#Y4j\NѦGՏ/U[*|ߦǑ/׍yw_?  x΄ޭׂ |(:-FSOQոA͇C.'|~ԺCu7kB+`E63ϰcI9%(( BMwէNc 1Q맼 G fL%W+抚GYG;gC>N$#8@{W7䏁4;܅u&[ۏ}"N~s'/`mi Ԭ[} |\PE2~e}w:M@JtR<}hŨ 8 ʻBv|tvJ7{t˪vŞW6# `CRF[²9|_P.#3Z>_>DT }.$8Qux:5ex?gL&`@聢x'_){f~YS4-5hrt(t ۝]F޸ayI_1Q}+Єqq|wxl,_sM6a>g/GT .s_w޽w-ςA7`Qo~FAJ> xhnP]tm Bsy ){Ȫ~{*rhO<7G/1qj<qiIKq_6!RJ u ng~EA/~gV2nruYx-r0~__c:%ŷ޳w~f>Yo&yCKGzOX}J_.6B\$ Bu_͂L6hB|-Pe ƚ0k8@ 5},4* c:h`O)1Q 3+Meg;N^>sO_uP+V`C$mq>˔aW!Đ) Nͨ5UY}g⎹5BJ`L9,M.|<7dC7&t \xu!7hbF[M_uSՇ48G'=țݷ udG?+nweBH t+ L%1~!RmH!|W'@J(C' GZLQ@t#F~inLrLMQ JI]nϔ%#D1zrqN#TK?=k?cB J(XO°~!61BՇ4:SWS`tqQDJBBEzg+7޻uQz#)% WFG ?SB [8 Mt=ud;9Q!& G1&}j[fP;M!e2LׅwOwpyyzVc5 }X;+D%1~!jQG Q_FEƍ hء 1:6hoD)45o+ЕlԠ3/PS׉Y^eq7sl/{-]0>11u 8S'քW.e Q(T*?UPn } ݻ 8=!DH1 pPZάrԼX /DA@jx(OWŬsd@}Hq}*+=]:eŅD/QFM f%Wܟe"!']cjFE1G$$d&.T*>: !AA5*Pŧߦ *G;{!Ȕ4zȞ ߏ|#<ehf\'xsF-hi( 7\(9gx^V?qq 4 ƝEi؟ Jpz))ǣηO+S4qMOY ՋU~G;*&DDn,)a=7Bj>@z面'Gď|-\לEˮ<4}պw_f^92$yRz\3Pnp87|:7IB_RRԼ0Ca0JLQfixHv ϒ]&Đ "xi&LM٭"zF ɸ+k,1 $f=M 4m5p>4?ue@J Sv:U;x?9le^xL v4<9h" XoK6guYXM Ph<)`>Da܆TQKJ;KPP1sT\lOğ[ɟF/+}Ƨ2lۻ[vW.g@|]QyJAf68Vvh(66AT?^Y0>1vʠh" &uSH^4*M9u74 ='<}%W*}ާo| 2l;dB4 CZ S"rѨmѐ2nNg}JY02?V.uTp7](rf=mM$6EgV jxlP;dJwVE;z,@" !sM!񤱈ˎ _9t%B19 -uΎ9QrKÒ ]/49\SߜY+)vvcy+o x=V]ε|xiM\ E9e߷0J,/`?mqKƍՄ)AK˂AEv༨!> fP8F@j }ak#g6:p{{Nݹ6UiQ^ UryA#4 yOP 8*"@8`7S)5{vGN]F wR]뮻%4y[ɋta 2ڊ}mmGGP> 5 { YeȀz{t-:C'@jx%>_>43g~d\g<>=TyTT}Q忀,m B>bDݾuSTwv:]{+a O;hZIC 6Z>;!H5:U(J<@AMGR0T"=@͇J pJ!}@ }a OdIJpSCr^qĉA T\67~2CGEhF&a>2. 7G!a,A}dxbSSߣOG>}홊Wdz@ ̈o*Ou5߅jFl޲e0V-6JJb4Ք. ɴ|bh76:w5++љ Ui)ǝ͖f%^uP6FsC!6^yoɡuB)oq;x\ǁҾB磠B]R*ĉ*M5}4<9S Tq/cO%00uɤbKx9~]QՅΐ-E''Am4=!0.K&994 枹K^\:Ưc <5COK[1!?Rt O`;W=j]PrD#J1rwާG}**O;7hOVlNhoglTo*iPt:_ ]^M@7S5+6^r cSKqL9]ЩRgpG1RRu*+Jㆳ5G#R|P02g_{g&[ -UpCg?3pw.'c LOHPR'vDžC +n0yܳ)2nUٲI".NWŽv'u+Ӹ muUe;鿆jZτow5ue o415jXE }-QP?7ȖhsNpQ4  w3 _bnvƑqO9;KdNZhԝc¢S%`\*a=TyOmԿfȟUABw-^kW5|l|Ǡ茢OÿVއrzZ;떧#YJCȿ"j*o\_) M݆!+_S^Wtw@A5,4r\ɘ2|Otą#Rir Vu'3wumkD498'j֞mե Fz>SuF6dط~ 2t Ӥ~@tzypچg@9;~]]>/l9F**mr>$w.xZJqaxx 8{\=g)=5BDzsvjW? Ǒn@LJlξ_:s{_ɘ+>?OO/?fSΟlqgOjmt/yPz7/P-ƍ[7 %R"]JcQ8[/~RK+Bb؄^M5S~fcqpx) `(n>/ղ$C&A*rUVְsLap'=cE̘e&2fI1~o?ʒu1b{k5s,yBYf>DWese :] tD—*ir>$qop䯾sY,!@+87"TɪIO; .l\$U\'"%%Os y @ _p+p>KPI˗s6t%yźשJ1M'mQ0};ӰBhjdO-pT\||sϊw'X?q !HM eiXw:}3ћ Uu@LO/oߩiXyBJi>"$o/}6B%ws$<4=v}EvNq6n!*=ƴ"_;DN;'^'U 5Ki jlrL0?E>\^~bLKV QN9 6NC,!??er?wO}qùO|Vg匱4n&x +dNR*G ?/y`#i}K, Ic-[KeRC/δ'Vxgbj(\tO\XӸli|UK\CZ쥆ưSc{o5VM J_ɟ+d菰 g˷[(kHr /^k;?ggaV{ބUkfpH"M#y-5?\qU#|_nq0,eU(]b7=xx:u\ne(xg,&?e#dYtOIšs!]؎qҌʅy'L~*8nօT RO^@IԐ})zw 8.+t!۸p_HEX*fp,u( ~յKf˞ G^7Yg9_h!ȔХx; G}EaV? &kw֏*A4:uNߺZPT>lg/Oy/.2kN˒ʅy:MԚѺ"0_˹:ú`찈QRB'/q( ~2@NA_svC/Fx2߱C]dcܾruulG18[f6z_Ѫ+3mޣŶned &tP0nUFYC }?xaVks>\qw]I"֮^_%WL7_\WI5LIPm7| dɇ A|* z滑qv$]WvWZ`4Q1jIɌYl;R)שּ~$]:Wzr'4]|mlnk򺄐x2 _ɳ﷮~Y2 {1F;0zǪ1uO;e5w~BZgLV޺3-[+ I>w;ʪ0& ErTHOisc*쌵ݿx%S>>ŝi[|9?b縛PhF }9 V?^iuxiD Fj~VwJ7LBwΚ*?luЎץ^շ&C.vM.MԫFrh}Ea#!DE>? f J$e7OR~ƃ&U)yNfVu2_7:G؋=#u'- !Is |z} rFGE3};ݭ g~"&pg č;~9NG&/q( >=4(iO u? F7 5IfcN@}Z= "=4qsƥ:E/u@:pHzBHx:n O@xd $Qݳh7.i`!$|C oסT%JC2ikU5s*KjŖröYg:G[mJ~!A]qjMwVRbF\ٸdoS˺ޔܝ[$ch8/ۘXr娂QDJB@8\FJ*vãG)/$;sJJ]\F*?uK{!|CHU8qo$\3^{ !#<&m-c4q#$~XW<:n'H깧B+.f$n͋iiv7*׍ژ3#ڵ\CnJ3gz>S.k,p}n}7k^p I:WhGsԬqA戰:(ؘt ,zI@|jMY7[>^+sMXKdt˼ijߘu9LJraI#3a< f$MZJ K;hu\kU^O8pJ&OͺN[OʺlXgخ%_稐f}C3\tc6zndR7}Jrdb>G{ز7}5y46#F5·e}s3޼i#2Thtb~EW~:|L'kҸk ǼvC)I7:=]t٨ۏ7-Ci\gofI^G(hGs-Լ.],ٚ8^cUmw]MftWbX7; !ȑק6;uwD[Ww9m`~HE;JwB'{|=eiA.P'82[nq^c≐/_: o+%: )FTIWŢi@Q۠_F= :Cn # 8w'2IDATMaw5&u25w7'HrɌ}ES]9yoCh$tx!Ȓ^Bվ%I4AC"%Mֺ3rBUsag\m!l}GA1fiWn&.w%Ϟu tHgvF';~YZ'$rh&%Kּp>뱐S힢 A44F/o=֝W l gw+e~ %z{|v4#Yx}{'_yfɍ{~3aT"WUL߾5s*,?siwv'oflpuc76joYT/ }/=2ޑ/L]O } 9$ɅMLBi7cY}f(=мw+c%m<۾Ϯ/w田tmܮ4m|}ɳ;egJ isF. ox, {G9eQnFBH<[5IFrO89;}zhwa{&3֢S,%F\ 쿸^ V ǴUS JKٻ9A+4n1ڕ{tϥNSm(J<7?Cy wwS1PXpDq1.5w%Ѓ:#@ud98 &!5UYɡo}-l6JcBڵ(Y=_5As%ȈBb%.EY9K9{eR<ބ08m~wW{1Uw|ĦlIsVi]NUl2N"-w%ޭ'uU t"Dm^7L)9rfi|hH;`٦ԸM {&}:Բ$=/vQ %t+mq ,!+Ɲ A>c…#Gc> E,ϱpl[U>bo93ZsvTys ޼Ig2,"5.f6)~D)5S3du W+njBgtiDaK}96+% Y}=jd+ wwچXos&0u91e:n%7! u:>\0ԋÙ`4^ od)Kapg K Qv,\eF5iĻbgkSP⿹gPC^ω.0-"3{ď5g zaBһ}jrfH,#D!)\%oNcүES/IBH8!y DZ_?EXζXgB~(.:xvu m( Ld$9\,H{HRɫܰ'Վ}D=: ;/X)uMمdRu}mFH~׆Xg>7pLjך8˘hV63FhWƷx̏}qVK$)+40tBx/X=je"G$Ivfg| /64߀jzaGQEu"BX}r)z z ЃC/3λsޚdMoB&Ͷ6:9aK gqbxsHCs$%xBwiFW{jGj"Um"[OBI!hM֛1 _Jÿ5hㄐ@n;cSxNwf)Д@"ES꽐1$ 2{Q4U1%u.>p>D9\oA:O^aa,GUkw_i1@"iVi/aێ[]"R |suM3V֪+A,<_G$sd0k)O<.T\zQ揝A[EHY(ZMhC#4/GBH խS*KƎ n0?2TUe4jgFv<]sovH =kٻ*cUkAZSx/Ob{Lّ[ljrr*Z{K+gTR3sA0Lr+ͳm'3}؝IeQwa{zqīluBEuq)˒>W;nSE;"iP]PHBj<.Q>J3hn #ڋ>29g?"F?]`g7-`Ba ֞An,1u"k3y^"C?{|+LߙK3\ۏ0e" 鞢_5Ѓj*CV0ЃNX![%Ƅ)݌BH=zc}JPnUrI[WO֕3lND[3b_?(I yI̷`@uGK_ЙypMb^4%]yvLNJ̵NHlNktuFDޱĆHE&oANbF 7xS^1~zeޘ{x7#:R%77#͏1]7=ЫgW_|(|rߧr<Ѓy>CM2pj/ 3/pƫnqhLyٵg4c_F*Vyl2(N!d?]hYb+ ƋPxB*GLVWЫQx.KX7KD1! ?z떝"8ͼƺi+^] ?s7Ṵ:\435|]Fo7{TWB?x'bTYהM2BOYg:'+zYvx4.x8XUoO8Z]Ё8k/:%:ߣ[^mb^T>1/nDIk`}퍤3_mpH5$s6HMkyk-T dJK~jX}^iNɩj[L{rYm!cϭwonB̻h&In (|r?F_ iQ ӧotppM&}Uџ{y- s~T ׺0>NƘ"tڳG0"ƍpsG+rD wN"1ab眊&qGV V#D5F.\(I/;j'ϰ 6ԔM،Ѧ+;Hy_t QGL^搐-<߶g:@CV{K(@z})OW*5B\yҴO*,Hj?ǃUYe$/hlY^ϕ/|e>[?K}ѮS&b*G ~2Pn[!c(r6#xL-D ޺+DAFEjuxsNլr^ NnlqC_;A~:~~\-eFs=M 4r2Vk\LM IG|Y-a$!dU'ogJHQ[ߗ[`qiԄ~Ut n_or64hR55 kw_"ūx1?~_W]\|_٤I ШEڜ>%- }LFR#Dfe]{ޢ 鵐a%|CaSKCΟzypyvzzDOT'\>+,;gE=YGCf=KɃiРAԐc"b\*#ڪ" !1d۸8m".wK#||vn#^t;iR"`P"Ɇp=>K+LǻYf`9]])ҸիM'}t+?uطi&KT<]'ZI-1r)Bɽ|]ᒴGΖ=%fѾ#C]^^)cV>m.-їvVHம;ſ5 Z:W?ӠAbG;e4'B1aBFa-!ąo_ҔSry*nu,i ̰bRH !^SXN#uVb#/Vdnpd\qP_#u1Lz_RwXsqŜ;-_7aŊO_M?ѢGX8EܹFeHrwf)hL;ʹEYvqFYى8]4ƭ"\R rW@q3 ?9a ["8/P{ut[s'!6ʅ߃MxU7!Y#6Bd7VyvxZx*4>bNxnuLM oVyt{%kԙ*%_}c4hРA߂<m#u !9oqTq'=e vhx=_az>|tb.Y!yV*:M]q6ޢ?@,z/ y Gsٵ-Z0gE1yȴ27ܩ 03 %V:[洨C(M?/ Bv?ҩ6(Su%z@{m S/Y=Hŧ)ȯEvew؏_2?4hРAOQc,'J}w =葞~}GEWܾH~FݪFUO:68E|Hgr?;]]т9ۼ{RE ٬y gRo?tbfIh `9PVfD'2߯ʌ0D#x^x[$Z Y`!Oy:Xlf䦀䦇kRQ.>p \O^%&$/ ` K9's X`1I[!IXXXXXcBۑܔX~v&)sKRC.rMP e iFg-g1P.eƵpM"N7g ,ɱb3#7SPz]7qPq.$ 9X\k eEq7$CIe=Q `K*F$&8? `wXXXXXcy!PO'k8$BBU@YzArjKs33$0-SNu $Ru\~ߒ Kp,6cr8 PVyqWHD*ݏ<K :1P]$: yB2}NwZcaaaa %7ܔ8 *~CN?uCYjn0ˈم{tn$-,0/Cx|d{ UtWz,[j;o$M8 RB960Kw^p`O_-G';*bo& VT`Pn.^{#ӹMN^1TB q:%d 4P9qʧGxf t5p9ԑ n9ϐdXl䦀ftBrS 3\ \ ? 26IIhBNy%PAN£b7Zk4U| $gKr,,,,,8Iu2i, ū9M-h|浶pTp$6Ԙ<a%'< l'e[>rEN7% 9m>*?a]TyDAhDI+NK"7AnN >$fTdgx5 *?`|FE|[sBRzp۹kC׫v}7(m 땲pPّ569EB F_}C';|UPe),a,)P' Cn:V Vb$5J b#,<ªSBQ$Wm͹ y<3.|muܽ[Ce;^y &ߛGڞ+2r]P3vօЯ1*#S Ezb́$:?17 r' ]P; 6-1 Moe$1)Btjj&O9Cr.`LPy{Z$l. <80O뚃4:e'IRͣqҞ|tݘtVgsg#Z7Aݔx'@j<<-k_"qqV&tRc] K <Nuk>ZY~O^Uil$ioXjaaEP XoJbT!~VRů]XZӄ,cou\Q }ݴU~a3_') e4 b&H$4򹥋J>;IN :l;$Ak#z2tCN[<wTy?(?s嫜хBDB=k9wu?UЇ>8[R6 um*mOw sZ)kOBˍnG觢>CFvQ_+n<@xeEFzg FgᄮR(~BK)`tݿ\ujs%"x[n91H]K|ɢ *̯ ۣPUMrcZmZjAib%ƯGp SZoYsj%YG;`x= "l|i) Q>d;|wgl]&Ǿ*l|AAxHUrlũm2)FD1mÅ l.}?d_2H_ύ΍zf[3ͷwohEƂ ZRhwHUݯV>h'lH8 68s %y'/gopnrcZmeX0:6aw[Ug!9C .)yJ{ﯕ\_(].vwWkqs壂&sZKms<ÜXX&Rk0^*ha!zCmS5-OøtKJާ]hGXHhڥlG%QaX s#I $,DtQ΍sݜs͍17\Qw'b3ksvu @ pcB_kJ̋T,#oz7r·FpKM0JdK7#? & 'o{4'C; }9ȍiiڂӛT˗&8UX%Ă-T)N^(W 0&E_WZxssp wԣ`ūC,c9ڒT6`jxvv6PNzŴ2o!ƤjށZHsAv{E)S9q` FW>.O-;^q 6C7 :Pr؃KbS@>FwTQgP!лyB&g TuMΠq\ EM_0$u-Q2fqzA}'b\T変#7զ(K\mI6q'jɝlW6 'VQ|UV\Ϧg-j(*c.R VJZ,{1ρtNz#.nXpN~֋{N3dx_/VX,ɶlABB7wI]돡9ޑ |mř똬FԳ+ ٨ҚyGQkhmQ;FD1QI~~97S͹%eP̩gc&7U$7Ld.a0 ei"icrNjAl;˜B?ކNd Uxq>Tc5B 9s3p 7* vω pNEDo:&8z#IrlXmз`sݶq7)68ù [y}-;.&S)[A3w+; " ^@_N\0&"G*]N|~ ;U9ߜkxvx?;_)*7tmlv;~rT֥ؑŢJ5#xCF p#qϱ|n~fM}%9fV+ջLT@ځDVylCh~Mt87bͶ$75P59ϱ)}sݩ=$9 "}HCSh+Kno:i2~^։/S/jÄ~kNC,$_*?d J.xm1؇{ R1w y y^Kkۗozx c6WŸyr*F9xT!bW_EB|>}LL({Ӳ\[?|>bpGEsk\_1M vIOD(Jz9|lʑ-\r~✭ &9#i)w `?Dkm1`ƥRXb{Š KPY{g |hG7۱$i5E.WN#e+>t q^4sJEr%kW@rX .?3/FDKP(_ ]NǘornnRc$à_"A[' B ?Vf:p-"oH#;zTw9T@eڝ&vs=SXatQ M4vAߚ} ,7yҧhu<:},K$\S4f 7U{)ɑj@Xj10 >ȝ*$CX* jWfDQw2 r%?["(mD,T&̋!7:c¡OA 5aPE\gL! Z-\\bUNY %iQCQw[m$9cb" -QH+nl$O>3UiIm˱}c3o-F;ƛ$Bye:V <kE_B]VKlے,?*w FHU;s>݊' Va2ܟM-@}]XcCENXCSbfwʤxZp.= oc$>RCWq |)x< }s7.$3'rMiNU$*Nu^,>4Wqy $7Cf4tm)4NwcpəwȰ0C9XCeM[qY9d!7Фٗ;Sz!5@Ø,}Ћ(n"/\ȱ$x:ߒv%^$ po2{p|V8}#9e0^NM`)G|؃3ѩ,me6uPyqW ӐbXUmgA>m/#BH9֧a64䕜sI x0۩sC&" ~0Nx1AXuԿnPkM|{Y5-ĐI%}L0ϐhbokUΉY!:`J:qϭ|$)*>ݩToI|*OkTzurwYwX8yzJH .&5WQrSt۴u T 瑼8453IrmO `a`'Q仕)*iI(Adft.溔Xܼ|ANDb8|X?x =OҲIaZUG# uPBӴIIH=*stu x$Xwv ~ ?vep9Nߝ' ɉ!7sSD?;8c]JӣN+-МIh y]nt]Of,]sn}`;}|@p#xBۓ4 ֤6$@n"G|O.v$mTC9@Zi]dza9**巘DA܉ʻ\}/ݚĈIiNf2q %*\oGSN2 h,'1@T&tΑj' &a/ 5t-bĹBn$Q6efLǿ^IIAK䦊}؉߯-6\XyʣTBP5. AP"bi(nps)=HNBrSyu:T_2!5ˡC:ps?>9.y g~Q]\ZI[t-E'Q>؊`*x4?=t#IV]S .ek~,}/{P> D[Io*CLgW"@Ŷ#뫃:5r92?Xv>OnH++6㡂Wws iٟ: N:@wͤtF'@; _T١ӟ͘qMknqCCd!7|#9O"siAnO#g$'q~(EM NUT{]4{=NH).(rc|Qe3~ ^*ԳC׻/sXoLT-x5YM ᘵp:*KC O7;if$S6Op/{9TFǝqIE])` gO"A;c0(߱$'N2?NYѱ5> X.4Њs8<CлϩKݥo?4֑Or[HL~l̶\wPi fI6=םJBKo(EiHX$ d{syvaHX8shr17fDX \w22HN qezg75P 9p36' N7lW!T0}Kz%ZT"8)vg ,PIV2ͰPQ1pww~S*lmNW;|-t_Lkj[ ?CכI?ŠWIB8><N3^"q֝NnܫQIPIt26iuIt=Mr*/jn 8g֙i$hƋ>1pkHߣ=y8,tmNϥ3^<Mv * \hݿ墆M[x*N'<^l8p@ze ʑ.h IDAT|/ggѢO<!YtPN?EJpr2B%z3w[ b['4Tf1"@wz+w9g#wPqލ4%/ ]ZnuN!9iц o!wB{ .KVDG S,'1AV@Yr#vI-p0OV'\]C%w5 Ӿ$7\׉ . EI:$cZD_-̘2v]<=jsHz?wE*A1:fG;$;:}m4/]~)zbKY\mZ:WX΃: &Q>$] xH#{(kH?(٠n$)PLY;2 Y9HO]o>-~IM/6t痐u1~Za )sn4`"dH |CC)7$gPc`P 7 *58oysQaB1XꘁݏB{qO=w9B`z?flw/_Ǹ}E[L'='qu.w9e©3e(gp<*urIHHLSI9ȍ^Я6X8vR>28w(_POmzPƲ)H@n\; 4.d$M 8۵/Y&sLx r9fLf9k;̶L'2t3)ǐ)'g7xf8Tu3Tl@/ ىQ\=sOϰp&}4B׻ Np>~͡]$g:ǿ* 9*0y9ޙpg9yelW\c1 ]I9x+T;'idwzђIΥ<$q;uh'w+}U$͖n~ INIA*I$_[rAHL$gfb CEQ 7~\T Q"n>-XY;-Bt8C׻_XƝoBۙA7qa19[3]WVhmg!J~iNw+?k8KنC9Li r΂B׻*}8%}v3Oy ]o-X7r\4ɩPHVN˫*$7+],e= я~h@2_A~"H2H(~!Mv6cҔC|ctiܜLvjXn>CEw5IT?G &/l䄜g#TVҥboͅ CK&7_"=T.dE'v*@A lvm2kH0x!tq"﷠_Ўu9ڢQ o}JP1VW͘;3 g+3Y d PAw ʆ[Q|\ǦuW}lGXnzh}ѧt`?G\$H\\ >Ʉo1W2:p u_ 81Lhi߯[ī֕:5߄i:}ޖ"QI$1Py JV7ޔ@%eBJSKsQPʹGӯ?jyZę~y9wZ J\S@CdQkv65j=\ObG]oRz?iӶ#[[Zv/t}.>stOe) N,jܜA+R9nʿWg 9g@Dئ4p.syjUd]u߯4dmЦT0 a&نJn(0z nU b#/еxVS6$adu}#34WޮTVBMhr?T.R.d{]@/7 Hr:_v T=oO(*2"BZo#x*mCy"qCC ;}5TLcԒ]1c`|>HYta_ڑ--Q ulpgpps(7:$a׈Hn*s$;cA:7> +UKqM /C+r>-¥P6%?1`AdHXV;厽LP @Ϝ.~^ C_mtp5Yɽy)}Y0OB˶Q!>Q4gߖP@lG |wВ@' ]<5tYq[rZZ:1ke;.%P_ǰ\L]Z,VUgw{Ft\>_=*f>n]\ѝ0B{m$/:?$|}W8N=~{kt!X]]bleШ\0M;lȶdpvʕs Ĉ_ tw 'ߦer.Anʶ;>ߵ57r^e̵XH;7:Gϧ ~aC~1,YMPǙ?υdT*xӣ{2yib ȵk9l ];_ e|qPPu@kQW44Puu|OaБet?2utavmi4#2uPt2W#/$B]oW/1~Mo Lp Zv)!tC ?:[ ; g,ti"~l@\)Aɶ˝Nd5LF9I[*pWZQ1^*>G]Rп@ntK >B{x ]n*5P1\ȖButrqe$PIdi'EVr]U!d}<y@.|.OUUU*JW unzn1s-ܺ mqIBZ΍N7Ǩ[bD5 CJB.2,P QoMV&A' Ѽ{08PI1iKؖ:+?%Sfʷ.Pca9ưT Mu+7mtI]LFQǔ YmC$Z #\]o)MPà!UB+̥ON1ɮI7k+Lmgc/*WK '_bpiܧw 9?dT)3OQw@-AHD~N5)[!6\KٮO)rsV@O($i;AR ( .r#95 7z[D?;ڃ}=||nLa`nj]{3s6X\̣ϙt!lL CLYe~]O:I5s$A4IC[BTN}Pm]W_ ܸ1t,Zz9"Qy&2L 6ňҤ7 ?fa @(ht.}IvM1Q%͔E0^dq( vn18.^\J-v8BU*SI>N%^g3ސZ!eƮrNsB6wP)XLg0oNm?, 菸ĦVGE0~I %Dž;al4$T=gĹ/IjR^]c=:'Nb2]~TZegPt oK97z3JTf_Sbn Ju̿&Z׬^ DR:(Q]RR .b&,PEEbDi+IpJ(M .E!϶k0OR1pQ,T0 K|q-L\<ꄕ<ǠO,5 T\ZZkEslDY]Z) "Np,ߛ`=| TbFgs7X'dnlzYxd#^)& 7qǾ% ծi -T~!]Ϝq:mSjۦ][$7).S*2NE )')H17>b]KYڅsPAfpm^*%Fwx2>Nm&YJ&k:w@fzZt"KP d](*=ͮ*MziO+a/6;˱~'TKP@uz>Z1]/9xIrT[$%AȸP$8r_XkRR^!|CnP+bB{VxW*F,ŀ:DzcuReːXp& uRPqٵ[ie2UȤżn?r\.dzL{~5"Fn.-X`6v(ɮvON9Wm&QZѐ#8|P}a0c!*Ԗd J'OJ&9MR߷Rз4$|F~e|ΙrY P DP'rY&3&Lr]5zb+QFW\wJrb-:LznG_IyNwHV=te՝Ef}O}vAlc{7uK$.+Hrg8wVmܐR*Nl~AגtP3P>f@ ۛ: q~N\ZG\[2l,8GB%0*p i<4KS(g%r$\Z7 }CbH^X+N1s]v*;A~ Bȓ,l{&r}IJ)&G[]P'O7Uf3:*ͿNjelaI6}P(d{,((3$XtdsBD4eZJ) 9Lހd| žkɿeD&O/Ni/RC*:phMkw~lrݳy21-\d:B`?ͤ996RzD$,p(2), $EP#֌$;S[*R|~sNAU 0 }&oEw+MEe=|Ypv9P)K|@t]T8p;3W!8\]eqmL84 n*UlB#-iYƾ? Ůi >wPhJ[aLɲ, $ dSUiMaݝg>hP6Жta$7RֻŤ/ L+yu.ցx ]}*ӗ!,w&}LFܫ|BъTyf ,L1^\e*^PӐ;U"$Ey*P&+a$N3Rl>BAνޛևIrHYq-kLxh.e"~̾g -tN ]&X|Fz_J8!L,C.t^ĴP',;hҢ줵- ICBɨ QE$;4{.ciuzX!!Ax3t(x;P:IM;VW1ae5m3w@V-1HIǛMˡż"|p8'Cm]KvBכIe_B[T8Z9HDݨ^T.+)0hNGcԺ|%٢z:PJ;@,+NiBڠ㥱T$ĕb^a|97vMII$>aФ枬bD,8 2x<^۱_m>pLn2fZ80Ԝ L;q$J' w0},r٘]-;ui.#S!c7ew8; ^BR5hi͇1\,':Q:eWtLNs GnbG a@HmX?W1_%C;(o=(P!:*tr։{zxV:(S Na7s_mXu$8Nf T׍* _\4&VN"? QC^lk I9=N?OMP $7p1]AE.⵮N2l#o$qr?O85~V4;KI.^ec4PʀiPnO7֓RC>anXd .KjdIlLFK[:^v¹YffY\Y`žP1]PO#*U1OnEyR4,G,3./;rw| |YlTA̿lIe'ϳ w$yNOsȊEm-J:4Nѧ9tG$15Cp׼b%7PF@C[N@(Sq+yXoCmL$% V0E+&$AjC/gB \|{zg90t$ b3 z< $Annp-:)Mf0 !CpjK`t=#ف&7}*%/9^zB+9JjE>a)+ T#dx-'3~ ؕVːDu6?5엏t|]zJ=2Ǣ_hD{ڙdn$%8=9,J’4: ~ֽ1}"j:TɒcN#RNL Cٞ]C{Rkt@"$)Bv~Jn{DU [U$HZ>yy ؘP)&r t]@mM}= PE0>~f[ _a'/.~,c0]y;G Ȭ]&X%ޯD*zi InslOxA$'MU{YY^ns#MHj*\NwAŻiae,PV'Jb>lo3? ɯ/n^IǷyC^cݕi;D]R{D1wM1JB*qy$; v-L7$օ._,x MEⰑP2]lWD4exZ$sc1b<͗: vMAGfCnvZ ćߏp J:r}k%)Q8Tӿ]U7*l_:WjWB(*8 ]*f.;0Oʹ]Z>LR>~$)9PS7R6$d~t!O vPWY*I$@U) N?&6P18%=@uM ZuE@9v17N%έ?gX6$Ѻ_[& ߝkHnڄibK\ɒ(g憮}P<)Bf{UOQ {u:_Gӻ6k"Ēa>i|`$Ou7O 3[EKQ(몞kNu˳TҾ"tPI~=$=5Pȟ߇rURE XXg,zn% H4/@k , sax- y2߁r-ҢS9aiBÅ[\24F7ɏAbzOGP'@YBګ,oV!>)rJ-o> N0 VeؠtvVZ NRWlߐ'W4C5I8*|.Ixqc*D^&2{T br#3Svq J|k (0YGlT已XLn'%tmxpNl*r@qN9sLp&4Ȝu8i:ʷ4 wYT4 и0 <{3y",Bڍ2&OpBН$c?+-7Jgޖc'Qqѭ9ї`֣T^Gɹ |];̍d\se͵cǂ#,T6ˠrl J ɓF$9w<DS3^ ֦$qoe)[gLp sXa6ސi*VSzD$nn:3|WxɁgUW^=H^D-ni;0o9GQƊ2c%8)ړ~Ʌ}9* c\8&3ؠ+mkh?haI+ ׬dφ*8`$-rob/CQ ,pٚAUs;~w<&~񝡂(Lݍ̅myaDTV`@# XXqQ>0bhyt=bfZB$i>Sl7y\oil_=nPn/#0;bd |/Etbi^{,,"C\MnkFW߆Nm"B{Yʍb30xZn&}&;L;y!>]lneώl=C[ f!>JQd&ƜH<^ah3`mPٕEF ӟ''uP'" pa>Pيt7ZO艹}30 ]oCx]cTCv ޙ]٥}U̵a1c2َL%-81.s)P^p? NR gRvFz3 +B{*5= 'ߥn*p/T8w?/cVxQPq+qVsߓ䐹={'T\d.cp(?;z*>G5mf,W0$"x$t!d@2ܙp$l{*"tqPNBAkU-{I> ]o9'wFV2+XbU-pD5~LbӔE-Bl-|?R[AeaC{QW]bno҂pkzU~`l |L0w5 ׶$.w ȁҢDc,:z?1ߛbtnM@p>B(4(c$'$|v/^!I _- Ngµ< i;$ɎgrrmӆBˑpGGBәcw߫܈,`~t =BjXɴFKl*lV A xIIkGn㫳V$﹈?뿐|5rYΤ|XaіArB)giysbӶ{C׻. ]= AG~]/zCpb&1I&dZc̀RRDmn] .Bݬfw dNzD!* 2и:sߍda>0IM l @F?KAx4Ŧ1t$8Be#ގN ]JvUbw %VźWDYIKZw+s %)By>bt $8O:TDF(h@58 -'eJSϋ]qN?h8ʭ7'؎y0E_*iz9gSv%v`Gp6\KX zHCfa Tt"J)H3rcPI`Ak (Y<#K7 [ ㏊B׫t43A.TP߅ZS͝Is9s7}!9$2)ߋ}bagzrmz/AY y*/s#gݷ$5fl&FG!t:'-pAz9?r }zMPp:`9+{苤HZ\b 7Q|⭋yՓ7f!7U wW9^-T!B+9TH(ilhw#>&VlR]o8-7ʹ+h6ĠE>Vt`n; AJ cȟ S~'|Ǒ}N)@ C A.s@l5Rƕ o*N_\ ', ȮQg ]oW5:a_ɓ,_dn`c;-MR(0飀O ө X>DՑŢ#bNM.ӆE% ͵u5-- : 7y}Scy/,ǿ$YHČie 7!%$fN v;QB-,49\9$Tka:Mn(M) 8 Q`q f!.dP,,4&7zV/Dt"花pD'KA\':rJ *tj杺w*"}PYm LMr#w Z(|`Dg5_(=bL8e}I0@&d hO17r `ow?:-:Tl딣˘l.T`sL3(ZCG Dզ I1_$$2m< ;vxePe uBktUWz\ŋ@E)7ڏϹx-,IDޤ_BŵVn.䌄:>^6_%" ~)B۞ϹC%81bʿg_ǔ?)Dzd_É-'9v ]o;.WĹ4j^IVy겼d?oCnqLr\hˍAnSԛt\'28wKȡk~U"D Dtҡ(5dWy?8| _^a,ӡ2N嗈ܸ3RWen͸@eleCUP2Ϡ^x*U9it[3 (8 W%r+&l2.M$M-`5G)GkB] |'Ǡ~8 zWAZUGOd s9qƅ;9q#af5l[r#KoHr3` 7yZ Tda\V^ZqtU)Zf]_Ra-<:Ih"rfXe ;d.FL^ߥ| Z&wr#tC}:THnm~Ô~ Ehz#Os)^ NN<<&ig? h NdX*:ˡ<[ENU'*VP 8~̅o*t'  .G /3:.8GEegdqf^^yE *2*"ftzrvNs>}=uWߊ3UU-'mn(SNs5yr3Ң(&Ϟv<Q$5`lrbr.GܧL֍9Ŋ 3 p#GFqe=j_9Ȍ@g+(e;T|l]Pw!9_I _S}#s+ h.Lm PjTg$5"7>d[x I:\nIdP IDAT WR;*Swt8R -'Cmn{h+$6W4oŶւ6d^{C NJވu ?58ym}h-X4tC- cn#-5T"7_fr&j#eF .ə6\e7A6+S Uki: Nڛi??6y[*!O/v?WF\7piJ?"M:&2U}Wa>V|"CԛG2}1ɳumnSm"? ÝL-/$=X? *sbݺx OJWL$=g: :E*5HnjѶx(/}4 >O$g]M?3ye|iۆ)G(F %R-m1Hpm0PPMK\uHLwSvJeAR Nj^NE[ GЫܦTB_ z5ob=5 hoI;׋KAT,3 ə,!_Ikw_Z0贼ɳ_kV,Hn6[Lua+zm3\$O)#J 3uP}.$6;WA1QmȄ*Ʃi JJr:\|n@n|>q~-p~OJX9ֽ&&$(6I?g)gWZ)jZ3|F|o*Ԯ!i,b].i3!ۀM҅z#m>)8ę"97y&8Se6I3K)`68gKL=}{<@n:GIZszefH-Оgz| 8d76I.Tlec5~+j:')l\+noZ#zখ ?[ElL_…;$(cuWZ96IhlMҿEeyeg$ N63?~ QE_mV"c&<=Ci 8U&e"w$gC\ -5j)O 0W$72͵na i$7HgJgw<{L\s|C6E>^Ey,,*"^U:tZFB$78/%2068inV*! Fn?W{];tmom{sɳ7y{P}KA/>[l7yHBq0~=% $|IvMˀ\+mu/.U>;8un¹}g[ '̯™.5y.Y_q$7#I!9_IzP]m).IaMMq &?Jlb$: <#)E^Zۀ3I?o2Hpj6ۮU#z?U˧==J|GmpjgEnIWiIɳU6Io>^\6I.S[pE'8[r?.1+z_H@'8Ses`^S%9 W<{ iI(pɳǕ7đ^/B؆+7W?OR|Y H^%.lHnCQgxL{uBrεIzɳL=l,*OMߚ<[+Z9 .ZzDPiɳe˾h}[}xDeƤdl65xB9R:|v6E`AA-hW#7Q*.%$N .g79G>JQvS1Z@,~ɳzX4A~8 U8 Wa/gl~M\}(8o"7%\{b+}i"[knYYjo窫Mc{c㒯Z~Sc[%܄s8&黀[b2?+ 澸u6I^<%mztg6~?.Wf NIzGXپ[p5W&ώ\:suل_kbn($vaw`\XC \t˭A%,fx͍<Q&#%|zH:d㬲 >wdV>о=@@jx. }gMҹ&ѝ+,kK [ 9O<&\4Lwc6IN[K{m,%N[@TgA}] s oLfq?U 's֫ }[b ףgwר]rZe.gYþxTV=yzƫjx9a }^5Nsp"5is4y6g=Hk㱚.ZMgE—H6K6: &\ {KV5hz?~# ޜ{c@n\tYm?m5qGu>`G<;Fi }0P XFt; $eEUηq7&լ-p<'FjspL)Iq{/4Y\N:=}1Rt7ݔ*vOzSgugi!:Lо:44u?8/֒ٚCҚ"IɳJX/r>ɳ >VSugv]P_JZ~4 +5K>i5VO| uONyaHmg_Vck?Qg0ZS-sbz4T 'rD7DQaɳ_?\0!IzT`h Ǔy<+O= Q MW@tzj<=tLW6PIj2$URqEyH mҲ,\^EdUU{I7L]e&ך/ ڃ3??!K|]D~zVvs%[U@'sae?g:om<[Q4Qr7S亃w4MybG RF6p[))rHµA9CWZVڥ;/䦭 Mgצ<{N?Hk(/E{4GW+ΚSӀ嗲R i?R'I:q`޷`n8,o@a_^z|o9-&~g&%߽&gZmKٸqס]<} uy'7JEY"|ǎIٛ2&1[Dkg{6VK>i>wyRDڧI.|I)"ѓeftvG?&3&V<&98] N/pM5&ϾQy?dGhjM̈́$86IСp68K".~r\Xb^ݹ4| ~o#<͸ת>jg/JlEX!|#pѥ"iB{3E6fh0Wk-.Xcڪ M -lO kIXdt@>Snav64}3CsICZ3Ε&&~ߒ&&L-1yvMR MOԱmupIzbSs+$7juxrZO"gk}#or_$kuxUfEP-fʯ,{q>xO<[e&ir[6`,Qۀ ȱ@ϲJ}eHR4wh:7@J0GKV,p O6I.އs[<uGogbCrfZ@fk2S8.hm{zBJ"tШy lINۋ8*?7tkyh?CZU>@YNi\u`QFg-MN32X$%YWI:l`2U.`M"k홪'ɤG䢷B; @d'fK>:X/ʑpєO&|8BkPMg-I\NO}R*<u +`p C;ʼM* 7< k=GE@|հcڊ.pYk@t6h1_u[|(/m .a݆'"q.ڱQk]j/e_o׺BjW7&[J.Ldb^ay6 ߛSkd@Bӯ6p,Ɔ1$RCDS=ؽ<`Hl)ڪ%7V;S:isk.A;IF*d>]wP89 \ թGmwe7tm݄v49s>'y/;jb8m=}#5}hUmL)w~7FoSb,Ihkbl9 VM8<)x3"9 z8h|o>7u*vN"]vh*-)%(c-624b35}66zx3WN"·" ?hF8LWtNt:܄;M?d=\>+)< Rln&i2QǕw4;ο"'doP$vejjb\ٜO2Z\-ߛmp% qI:qԵC5:nca7~\JYQ_]e R~|sp,HpThX;-Clb36WIn64y&8^AJSkpi%B_At_4{n[͛ c-~NDb  bM S\F\2+9W{W-;ډK,"""""j^`>{KĦEDn,pɳ{dn Y|Mz䙏ڟR8TLC5ldOh#CԯHnM&; M͗38r~՗Q 1'ç/W#N@`#6 ɳߊ\s .5y[ɳ.[PyT;P툈Hp"{4qr=q|b"W&)b \w ENd[q"""""&fxJ?d~H'4&P{WMmPkqʑuNe4"7pHETDDDDD#8 p&ɔ14 A<oQ Co&,Cn0yPXC#('W֔ jN3!(Gl_na6I? |fpB+vuzDDDDDLM]Hv2Icp";o7"~6.{(rMDDDDD"8 Mh,%7O'Q0 _Ւ o4Iw.>Y 7pɳ-jHn""""""Ɠ/3?|&)&Ͼ+lSӀw3|y\لGUݖ39ϴsq^{vJ٫DDDDDDS>yDhzo%;Mҙ="';eֺGZgeO׬3K$=e"Hn"""""&*y{3y 5xP#4\6'$mT9OR6Iic^<[(*C$ca;)Ea<8 |̼竳N{.`$75_%2X3f6}|R}c*DY߾ǥy9 8AuMgG=řkfCpp B)R{&99TDl'?6Ip)DDDDDl ?,=&ϮU6۳Izq 57=zLguO?FZPc46$}TTx&~ <<`t\pfQg*V=*i`I[ɳ8K@]d?&ϖ=۪2%"؜ NcMۗ Mg3yvM~#le N_Hnl~[' 4ESwìV? z&#j$ȁWO]zJ%"""""Sبw`ɳ~ Η#ܐk"ܜkmc)hSMu_KL ; \o`gH珳 _L MVЯQke39o HIlO=N6ME&) 93E[qV#L{M tU"%2C 1Y5""""""X ) p)%{v<v9 8IHZɳQVmp&(%Ħh55i |ƙ8\tkcHNƢ@s3^*pp'g3%X Y(i|㿯Xz16i <9 !̣ ,[Bo[DxR }g~Z;}㈈ Mpmf3pMƠ^a_oJZ`R}-""""""ͷq3bZ&?/l$6DԛڛMK_C?HpF!79M;""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""b  >IENDB`pyformex-0.8.6/pyformex/icons/clock.xpm0000644000211500021150000000235010674466554020060 0ustar benebene00000000000000/* XPM */ static char *dummy[]={ "32 32 4 1", "a c #000000", "b c #ff0000", "# c #000000", ". c None", "................................", "..............####..............", "..........############..........", ".........#####.aa.#####.........", ".......###.....aa.....###.......", "......####.....aa.....####......", "......#...#..........#...#......", "....##....................##....", "....##.....bb.............##....", "...###.....bb..bb.........###...", "..##..#.....bb.bb........#..##..", "..##........bb.bb...........##..", "..##.........bbbb...........##..", "..##..........bbb...........##..", ".##...........bbb............##.", ".##aaa.........bb.........aaa##.", ".##aaa....................aaa##.", ".##..........................##.", "..##........................##..", "..##........................##..", "..##........................##..", "..##..#..................#..##..", "...###....................###...", "....##....................##....", "....##....................##....", "......#...#..........#...#......", "......####.....aa.....####......", ".......###.....aa.....###.......", ".........#####.aa.#####.........", "..........############..........", "..............####..............", "................................"}; pyformex-0.8.6/pyformex/icons/zoomrect.xpm0000644000211500021150000000231211166446736020622 0ustar benebene00000000000000/* XPM */ static char *dummy[]={ "32 32 2 1", "# c #000000", ". c None", "................................", "................................", "................................", "................................", "................................", "................................", "................................", ".........######################.", ".........######################.", ".........##..................##.", ".........##..................##.", ".........##..................##.", ".........##..................##.", ".........##..................##.", ".........##..................##.", ".##################..........##.", ".#.......##.......#..........##.", ".#.......##.......#..........##.", ".#.......##.......#..........##.", ".#.......######################.", ".#.......######################.", ".#................#.............", ".#................#.............", ".#................#.............", ".##################.............", "................................", "................................", "................................", "................................", "................................", "................................", "................................"}; pyformex-0.8.6/pyformex/icons/pyformex-splash.png0000777000211500017510000000000011161775513024561 2pyformex-logo.pngustar bene00000000000000pyformex-0.8.6/pyformex/icons/refplanes.xpm0000644000211500021150000000135311117433757020736 0ustar benebene00000000000000/* XPM */ static char * refplanes_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 4 1", /* colors */ " c None", ". c green", "X c blue", "o c red", /* pixels */ " . ", " .. ", " ... ", " XXXXXXX.XXXXXXXX ", " XXXXXXX..XXXXXXX ", " XXXXXXX...XXXXXX ", " XXXXXXX...XXXXXX ", "oooXXXXXXX...XXXXXX ", " ooXXXXXXX...XXXXXX ", " oXXXXXXX...XXXXXX ", " ooooooooooooooooo ", " Xooooooooooooooooo ", " XXooooooooooooooooo", " XXXXXXX...XXXXXX ", " XXXXXXX...XXXXXX ", " XXXXXXX...XXXXXX ", " XXXXXXX...XXXXXX ", " XXXXXXX...XXXXXX ", " XXXXXXX...XXXXXX ", " ... ", " .. ", " . "}; pyformex-0.8.6/pyformex/icons/ok.xpm0000644000211500021150000000131510743117252017357 0ustar benebene00000000000000/* XPM */ static char * ok_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c green", /* pixels */ " ", " ", " ", " ", " ", " . ", " ....... ", " ......... ", " ......... ", " ......... ", " ........... ", " ......... ", " ......... ", " ......... ", " ....... ", " . ", " ", " ", " ", " ", " ", " "}; pyformex-0.8.6/pyformex/icons/pencil.xpm0000644000211500021150000000136711117433757020236 0ustar benebene00000000000000/* XPM */ static char * pencil_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 5 1", /* colors */ " c None", ". c red", "X c black", "o c yellow", "O c brown", /* pixels */ " ", " ... ", " Xo... ", " Xooo... ", " Xooooo.. ", " Xooooooo. ", " XoooooooX ", " XoooooooX ", " XoooooooX ", " XoooooooX ", " XoooooooX ", " XoooooooX ", " oooooooX ", " OOoooooX ", " OOOoooX ", " OOOOOoX ", " OOOOO ", " XXOO ", " XX ", " X ", " ", " "}; pyformex-0.8.6/pyformex/icons/twistright.xpm0000644000211500021150000000133410474005063021154 0ustar benebene00000000000000/* XPM */ static char *twistright[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXX XXXXXXXXX", "XXXXXX XXXXXXX", "XXXXX XXXXX XXXXXX", "XXXX XXXXXXXXX XXXXX", "XXXX XXXXXXXXX XXXXX", "XXX XXXXXXXXXXX XXXX", "XXX XXXX XXXX XXXX", "XXX XXXX XXXX XXXX", "XXX XXXX XXXX XXXX", "XXX XXXXXXXX XXXX", "XXXX XXXXXXXX XX", "XXXX XXXXXXXXX XXX", "XXXXX XXXXXXX XXXX", "XXXXXX XXXXXX XXXXX", "XXXXXXXX XXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/lamp-on.xpm0000644000211500021150000000134011607302705020306 0ustar benebene00000000000000/* XPM */ static char * lamp_on_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c none", ". c black", "X c yellow", /* pixels */ " . . ", " . . ", " . ", " . ...X... . ", " . .XXXXXXX. . ", " .XXXXXXXXX. ", " .XXXXXXXXX. ", " ... .XXXXXXXXX. ... ", " .XXXXXXXXXXX. ", " .XXXXXXXXX. ", " .XXXXXXXXX. ", " .. .XXXXXXX. .. ", " . .XXXXX. . ", " ..XXX.. ", " . .XXX. . ", " . ..... . ", " . .. . ", " . .. ", " .. ", " . .. ", " ", " "}; pyformex-0.8.6/pyformex/icons/flat.xpm0000644000211500021150000000235010654036551017677 0ustar benebene00000000000000/* XPM */ static char *dummy[]={ "32 32 4 1", "b c #0000ff", "# c #008000", ". c None", "a c #ff0000", "................................", "................................", "...............##...............", ".............######.............", "...........##########...........", ".........##############.........", "......####################......", "....########################....", "..############################..", "..############################..", "..aa########################bb..", "..aaa######################bbb..", "...aaa####################bbb...", "...aaaaa################bbbbb...", "...aaaaaa##############bbbbbb...", "....aaaaaa############bbbbbb....", "....aaaaaaa##########bbbbbbb....", "....aaaaaaaaa######bbbbbbbbb....", ".....aaaaaaaaa####bbbbbbbbb.....", ".....aaaaaaaaaa##bbbbbbbbbb.....", ".....aaaaaaaaaaabbbbbbbbbbb.....", "......aaaaaaaaaabbbbbbbbbb......", ".......aaaaaaaaabbbbbbbbb.......", "........aaaaaaaabbbbbbbb........", ".........aaaaaaabbbbbbb.........", "..........aaaaaabbbbbb..........", "...........aaaaabbbbb...........", "............aaaabbbb............", ".............aaabbb.............", "..............aabb..............", "...............ab...............", "................................"}; pyformex-0.8.6/pyformex/icons/view-yl-xd.xpm0000644000211500021150000000132510761615361020760 0ustar benebene00000000000000/* XPM */ static char * view_yl_xd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " .............. ", " .. . ", " . . ", " . ", " . . . ", " . . . ", " .. . . ", " ... . ", " . . ", " . . ", " . . . ", " .. .. .. . ", " ... ..... ", " . ... ", " ... . ", " .. .. ", " ", " "}; pyformex-0.8.6/pyformex/icons/prev.xpm0000644000211500021150000000132610474005063017721 0ustar benebene00000000000000/* XPM */ static char *prev[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXX XXXXXXX", "XXXXXXXXXXXXX XXXXXXX", "XXXXXXXXXXXX XXXXXXX", "XXXXXXXXXXX XXXXXXX", "XXXXXXXXXX XXXXXXX", "XXXXXXXXX XXXXXXX", "XXXXXXXX XXXXXXX", "XXXXXXX XXXXXXX", "XXXXXXX XXXXXXX", "XXXXXXXX XXXXXXX", "XXXXXXXXX XXXXXXX", "XXXXXXXXXX XXXXXXX", "XXXXXXXXXXX XXXXXXX", "XXXXXXXXXXXX XXXXXXX", "XXXXXXXXXXXXX XXXXXXX", "XXXXXXXXXXXXXX XXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/nextstop.xpm0000644000211500021150000000133210474005063020626 0ustar benebene00000000000000/* XPM */ static char *nextstop[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXX XXXXXXX XXXXX", "XXXXXXX XXXXXX XXXXX", "XXXXXXX XXXXX XXXXX", "XXXXXXX XXXX XXXXX", "XXXXXXX XXX XXXXX", "XXXXXXX XX XXXXX", "XXXXXXX X XXXXX", "XXXXXXX XXXXX", "XXXXXXX XXXXX", "XXXXXXX X XXXXX", "XXXXXXX XX XXXXX", "XXXXXXX XXX XXXXX", "XXXXXXX XXXX XXXXX", "XXXXXXX XXXXX XXXXX", "XXXXXXX XXXXXX XXXXX", "XXXXXXX XXXXXXX XXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/transparent.xpm0000644000211500021150000000267310654036551021322 0ustar benebene00000000000000/* XPM */ static char *dummy[]={ "32 32 18 1", "i c #1a1a1d", "h c #1d1d1a", "j c #1a1a28", "d c #1a231a", "a c #252525", "m c #281a28", "c c #28281a", "p c #313131", "k c #1a1a53", "e c #1a411a", "# c #484848", "n c #531a53", "b c #53531a", "l c #1a1aff", "g c #1bb61b", ". c None", "o c #ff1bff", "f c #ffff1a", "................................", "...............##...............", "............###aaa##............", "..........###bbcdde###..........", "........###bbbbbedeee###........", "......###bbbfffbeeggeee###......", "....###bbbfffffbeeggggeee###....", "..###bbbfffffffbeeggggggeee###..", "..abbbfffffffffbeeggggggggeeea..", "##hcfffffffffffbeeggggggggggdi##", ".#ccbbfffffffffbeeggggggggeejj#.", ".##cbbbffffffffbeegggggggeekj##.", ".##bbbbbfffffffbeeggggggeekkk##.", "..#bbfbbbbfffffcdeggggeekklkk#..", "..#bbffbbbbffbbbeeeggeekkllkk#..", "..##bfffbbbbbbeeeeeeeekklllk##..", "...#bbffffccceeggeedjjllllkk#...", "...#bbfffbbmmeeggeejjkklllkk#...", "...##bfbbbnnnneeeekkkkkkklk##...", "....#bcbnnooonneekklllkkkjk#....", "....#bcnnoooooomjklllllkkjk#....", "......moooooooonkklllllllj......", "......#nooooooonkkllllllk#......", "......##noooooonkklllllk##......", ".......##nooooonkkllllk##.......", "........##noooonkklllk##........", ".........##nooonkkllk##.........", "..........##noonkklk##..........", "...........##nonkkk##...........", "............##nnkj##............", ".............##mja#.............", "..............#ppp.............."}; pyformex-0.8.6/pyformex/icons/up.xpm0000644000211500021150000000132410474005063017367 0ustar benebene00000000000000/* XPM */ static char *up[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXX XXXXXXXXX", "XXXXXXXX XXXXXXXX", "XXXXXXX XXXXXXX", "XXXXXX XXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/view-yl-xu.xpm0000644000211500021150000000132510761615361021001 0ustar benebene00000000000000/* XPM */ static char * view_yl_xu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " .. .. ", " ... . ", " . ... ", " ... ..... ", " .. .. . ", " . . . ", " . . . ", " .. . . ", " ... . ", " . . ", " . . ", " . . . ", " .. . ", " . . ", " .. . ", " .............. ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-yl-zd.xpm0000644000211500021150000000132510761615361020762 0ustar benebene00000000000000/* XPM */ static char * view_yl_zd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " .............. ", " .. . ", " . . ", " . ", " . . . ", " . . . ", " .. . . ", " ... . ", " . . ", " . . ", " . . . ", " .. ..... . ", " .. ..... ", " . ... ", " .. . ", " ..... ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-yl-zu.xpm0000644000211500021150000000132510761615361021003 0ustar benebene00000000000000/* XPM */ static char * view_yl_zu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " ..... ", " .. . ", " . ... ", " .. ..... ", " ..... . ", " . . . ", " . . . ", " .. . . ", " ... . ", " . . ", " . . ", " . . . ", " .. . ", " . . ", " .. . ", " .............. ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/pyformex.xpm0000644000211500021150000000231211302717706020617 0ustar benebene00000000000000/* XPM */ static char *dummy[]={ "32 32 2 1", "# c #ff3366", ". c None", "................................", "................................", "...............##...............", "............#########...........", "..........############..........", ".........##############.........", "........################........", "........################........", ".......##################.......", ".......#######...########.......", ".......#######....#######.......", ".......######......######.......", ".......######.....#######.......", ".......######.....#######.......", ".......######..##########.......", ".......######..##########.......", ".......######..#########........", ".......######..########.........", ".......######..########.........", ".......######..######...........", ".......######..####.............", ".......######...................", ".......######...................", ".......######...................", ".......######...................", ".......######...................", ".......######...................", ".......######...................", ".......######...................", ".......######...................", "................................", "................................"}; pyformex-0.8.6/pyformex/icons/next.xpm0000644000211500021150000000132610474005063017723 0ustar benebene00000000000000/* XPM */ static char *next[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXX XXXXXXXXXXXXXX", "XXXXXXX XXXXXXXXXXXXX", "XXXXXXX XXXXXXXXXXXX", "XXXXXXX XXXXXXXXXXX", "XXXXXXX XXXXXXXXXX", "XXXXXXX XXXXXXXXX", "XXXXXXX XXXXXXXX", "XXXXXXX XXXXXXX", "XXXXXXX XXXXXXX", "XXXXXXX XXXXXXXX", "XXXXXXX XXXXXXXXX", "XXXXXXX XXXXXXXXXX", "XXXXXXX XXXXXXXXXXX", "XXXXXXX XXXXXXXXXXXX", "XXXXXXX XXXXXXXXXXXXX", "XXXXXXX XXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/question.xpm0000644000211500021150000000132610646675754020641 0ustar benebene00000000000000/* XPM */ static char *next[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXX XXXXXXXXX", "XXXXXX XXXXXXX", "XXXXXX XXXXX XXXXXX", "XXXXX XXXXXXX XXXXXX", "XXXXXX XXXXXXX XXXXXX", "XXXXXXXXXXXXXX XXXXXX", "XXXXXXXXXXXXX XXXXXX", "XXXXXXXXXXXXX XXXXXXX", "XXXXXXXXXXX XXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXX XXXXXXXXXXX", "XXXXXXXXX XXXXXXXXXXX", "XXXXXXXXX XXXXXXXXXXX", "XXXXXXXXX XXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXX XXXXXXXXXXX", "XXXXXXXXX XXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/rotright.xpm0000644000211500021150000000133210474005063020604 0ustar benebene00000000000000/* XPM */ static char *rotright[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXXX", "XXXXXX XX XX XXX", "XXXXX X XX XXXX", "XXXX XXX XX XXXXX", "XXXX XXXX XX XXXX", "XXX XXXXX XX X XXX", "XXX XXXXX XX XXX XXX", "XXX XXXXX XXXXXX XXX", "XXXX XXXX XXXXX XXXX", "XXXX XXX XXXX XXXX", "XXXXX XXXXX", "XXXXXXX XXXXXXX", "XXXXXXXXXX XXXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXXX", "XXXXXXXXXX XXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/view-xr-yd.xpm0000644000211500021150000000132510761615361020766 0ustar benebene00000000000000/* XPM */ static char * view_xr_yd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " ............... ", " . .. ", " . . ", " . ", " . .. .. ", " . ... ", " . . ", " . ... ", " . . . .. .. ", " . . . ", " . .. . ", " ..... ... ", " ... . ", " . . ", " . . ", " .. ", " ", " "}; pyformex-0.8.6/pyformex/icons/lamp.xpm0000644000211500021150000000131711607302705017700 0ustar benebene00000000000000/* XPM */ static char * lamp_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c none", ". c black", /* pixels */ " ", " ", " . ", " ... ... ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " . . ", " ..... ", " .. ", " . .. ", " .. ", " . .. ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-xr-zd.xpm0000644000211500021150000000132510730450407020761 0ustar benebene00000000000000/* XPM */ static char * view_xr_zd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " ............... ", " . .. ", " . . ", " . ", " . .. .. ", " . ... ", " . . ", " . ... ", " . .. .. ", " . ", " . ..... ", " ..... .. ", " ... . ", " . .. ", " ..... ", " ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-xr-yu.xpm0000644000211500021150000000132510730450407021001 0ustar benebene00000000000000/* XPM */ static char * view_xr_yu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . . ", " . . ", " . .. . ", " ... ... ", " ..... . ", " . . ", " . . . ", " . .. .. .. ", " . ... ", " . . ", " . ... ", " . .. .. ", " . ", " . . ", " . .. ", " ............... ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-xr-zu.xpm0000644000211500021150000000132510730450407021002 0ustar benebene00000000000000/* XPM */ static char * view_xr_zu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " ", " ..... ", " . .. ", " ... . ", " ..... .. ", " . ..... ", " . ", " . .. .. ", " . ... ", " . . ", " . ... ", " . .. .. ", " . ", " . . ", " . .. ", " ............... ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/zoomin.xpm0000644000211500021150000000133010474005063020253 0ustar benebene00000000000000/* XPM */ static char *zoomin[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXX XXXXXXXXXXXXXX XXX", "XX XXXXXXXXXXXX XX", "XXX XX XXXX XX XXX", "XXXX X XXXX X XXXX", "XXXXX XXXX XXXXX", "XXXXXX XXXX XXXXXX", "XXXX XXXX XXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXXXXXXX XXXXXXXXX", "XXXXXXXXX XXXXXXXXX", "XXXXXXXXXX XXXXXXXXXX", "XXXX XXXX XXXX", "XXXXXX XXXX XXXXXX", "XXXXX XXXX XXXXX", "XXXX X XXXX X XXXX", "XXX XX XXXX XX XXX", "XX XXXXXXXXXXXX XX", "XXX XXXXXXXXXXXXXX XXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/stop.xpm0000644000211500021150000000132610637741616017746 0ustar benebene00000000000000/* XPM */ static char *next[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXX XXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/normals-avg.xpm0000644000211500021150000000733511012331246021173 0ustar benebene00000000000000/* XPM */ static char *dummy[]={ "32 32 99 2", ".# c #474747", ".a c #494949", ".b c #6a6a6a", ".e c #707070", ".c c #787878", ".d c #7a7a7a", ".f c #7f7f7f", ".k c #888888", "#u c #8b8b8b", "#i c #8c8c8c", "#b c #8d8d8d", "#s c #8e8e8e", "#j c #8f8f8f", "#c c #909090", ".3 c #919191", ".l c #929292", ".0 c #949494", ".Z c #959595", "#y c #969696", ".U c #979797", "#w c #989898", "#r c #999999", ".O c #9a9a9a", ".g c #9b9b9b", ".H c #9d9d9d", "#C c #9e9e9e", ".s c #9f9f9f", ".j c #a0a0a0", "#a c #a2a2a2", "#n c #a3a3a3", "#k c #a4a4a4", "#d c #a5a5a5", ".4 c #a6a6a6", ".h c #a7a7a7", ".1 c #a8a8a8", ".i c #a9a9a9", ".z c #aaaaaa", ".T c #acacac", ".V c #aeaeae", "#h c #afafaf", "#x c #b0b0b0", "#o c #b1b1b1", ".N c #b2b2b2", "#z c #b3b3b3", "#q c #b4b4b4", ".P c #b5b5b5", "## c #b6b6b6", "#B c #b7b7b7", "#E c #b8b8b8", "#e c #b9b9b9", ".5 c #bababa", ".2 c #bbbbbb", ".I c #bcbcbc", ".m c #bdbdbd", ".Y c #bebebe", "#G c #bfbfbf", "#t c #c0c0c0", ".G c #c1c1c1", ".A c #c2c2c2", "#A c #c3c3c3", "#F c #c4c4c4", ".r c #c5c5c5", "#f c #c6c6c6", "#D c #c7c7c7", ".S c #c8c8c8", ".n c #c9c9c9", "#p c #cacaca", ".6 c #cbcbcb", "#v c #cccccc", "#m c #cdcdcd", ".q c #cecece", ".J c #cfcfcf", ".y c #d0d0d0", "#g c #d1d1d1", ".B c #d2d2d2", "Qt c None", ".M c #d4d4d4", ".o c #d5d5d5", ".p c #d6d6d6", ".F c #d7d7d7", ".L c #d8d8d8", ".K c #d9d9d9", ".x c #dadada", ".E c #dbdbdb", ".X c #dcdcdc", ".C c #dddddd", ".R c #dedede", ".7 c #dfdfdf", ".t c #e0e0e0", ".w c #e1e1e1", ".Q c #e2e2e2", ".8 c #e3e3e3", ".W c #e4e4e4", ".D c #e5e5e5", "#l c #e6e6e6", ".9 c #e7e7e7", ".v c #e8e8e8", ".u c #e9e9e9", "#. c #eaeaea", "QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.#.aQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQtQtQtQtQt.b.c.d.eQtQtQtQtQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQtQtQtQt.f.g.h.i.j.kQtQtQtQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQtQtQt.l.m.n.o.p.q.r.jQtQtQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQtQt.s.n.o.t.u.v.w.x.y.zQtQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQt.s.A.B.C.D.u.v.w.E.F.G.HQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQt.j.I.J.K.w.D.u.v.w.C.L.M.N.OQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQt.j.P.n.p.C.w.D.u.v.Q.R.x.p.S.T.UQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQt.O.V.AQt.K.C.w.D.u.v.W.t.X.FQt.Y.z.ZQtQtQtQtQtQtQt", "QtQtQtQtQtQt.0.1.I.J.o.K.C.w.D.u.u.D.w.C.K.o.J.2.h.lQtQtQtQtQtQt", "QtQtQtQtQt.3.4.5.6Qt.F.E.7.8.9#..u.D.w.R.x.p.B.n###a#bQtQtQtQtQt", "QtQtQtQt#c#d#e#f.B.p.x.C.w.D.9#..u.D.Q.R.x.F.M#g.A#h.g#iQtQtQtQt", "QtQtQt#j#k.P.A.q.M.L.X.t.Q.D.9#..u#l.Q.R.E.L.pQt#m.2.1.O#iQtQtQt", "QtQt#j#n#o.m#pQt.p.x.C.t.Q.D.9#..u#l.Q.7.X.x.F.o.B#f#q.4#r#iQtQt", "Qt#s.s.T#e.r#g.o.K.E.R.t.8.D.9#..u#l.Q.t.C.E.K.p.M#g#t.N.4#r#uQt", "#b.g.1.P.G#vQt.p.K.E.R.t.8.D.v#..u#l.W.w.7.X.x.L.oQt.6.m#o#d#w#u", "#w#k#x.I.S.B.M.F.K.X.R.w.8.D.v#..u.9.D.Q.t.R.E.K.p.M.B.S.m#o#k#y", ".4#z.Y.n#g.M.p.K.E.C.t.Q.W.9.u#.#..v.D.8.w.R.X.K.F.o.B.q#A#B.z#C", "#q#t#D.J.M.p.K.E.C.t.Q.W#l.v.u#.#..9.D.8.w.R.X.x.F.o.B.y.n.m#o#d", ".Y#f#m.M.p.K.E.C.t.Q.W#l.9.v.u#.#..9.D.8.w.R.X.x.F.oQt.y.q#A#E.T", "#F.6.B.p.K.E.C.t.Q.W.D#l.9.v.u#.#..9.D.8.w.R.X.x.F.oQt#g.q.n.Y#z", "#p#g.p.K.E.C.t.w.Q.8.D#l.9.v.u#..u.9.D.8.w.R.X.x.L.oQt#g.J#v#F#e", ".J.p.K.E.C.7.t.w.Q.8.D#l.9.v.u#..u.9.D.8.w.R.X.x.L.p.M#g.J#m#p#G", ".M.K.E.C.R.7.t.w.Q.8.D#l.9.v.u#..u.9.D.8.w.7.X.x.L.p.M.B.J#m.6.r", ".K.E.X.C.R.7.t.w.Q.8.D#l.9.v.u#..u.9.D.8.w.7.C.E.L.p.M.B.y#m.6.n", "QtQtQtQtQtQt.t.w.Q.8.D#l.9.v.u#..u.9.D.8.w.7.C.E.K.pQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", "QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt"}; pyformex-0.8.6/pyformex/icons/rotup.xpm0000644000211500021150000000132710474005063020117 0ustar benebene00000000000000/* XPM */ static char *rotup[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXX XXX XXXXXX", "XXXXXXX X XXXXXX", "XXXXXX XX XXXXXX", "XXXXXX XXXX XXXXXX", "XXXXX XXXX XXXXXX", "XXXXX XXX XXXXXX", "XXXXX XXXXXXXXXXXXXXX", "XXXXX XXXXXXXXXXXXXXX", "XX X X XX", "XXXXX XXXXXXXXXXXXXXX", "XXXXX XXXXXXX XXXXXXX", "XXXXX XXXXXXX XXXXXX", "XXXXXX XXXXX XXXXXX", "XXXXXX XXX XXXXXXX", "XXXXXXX XXXXXXXX", "XXXXXXXXX XXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/icons/view-zr-xd.xpm0000644000211500021150000000132510761615361020767 0ustar benebene00000000000000/* XPM */ static char * view_zr_xd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " .............. ", " . .. ", " . . ", " . ", " . ..... ", " . .. ", " . . ", " . .. ", " . ..... ", " . ", " . ", " . .. .. ", " ..... ... ", " ... . ", " . ... ", " .. .. ", " ", " "}; pyformex-0.8.6/pyformex/icons/normals-ind.xpm0000644000211500021150000000233111012331246021157 0ustar benebene00000000000000/* XPM */ static char *dummy[]={ "32 32 3 1", "a c #b4b4b4", ". c None", "# c #e7e7e7", "................................", "................................", "................................", "...............##...............", "..............a##a..............", ".............a####a.............", "............a######a............", "...........aa######aa...........", "..........aa########aa..........", ".........aa##########aa.........", "........aaa##########aaa........", ".......aaa############aaa.......", "......aaaa############aaaa......", ".....aaaa##############aaaa.....", "....aaaa################aaaa....", "...aaaaa################aaaaa...", "..aaaaa##################aaaaa..", ".aaaaa####################aaaaa.", "aaaaaa####################aaaaaa", "aaaaa######################aaaaa", "aaaaa######################aaaaa", "aaaa########################aaaa", "aaa##########################aaa", "aaa##########################aaa", "aa############################aa", "aa############################aa", "a##############################a", "################################", "......####################......", "................................", "................................", "................................"}; pyformex-0.8.6/pyformex/icons/view-zr-yd.xpm0000644000211500021150000000132510761615361020770 0ustar benebene00000000000000/* XPM */ static char * view_zr_yd_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . ", " .. ", " ............... ", " . .. ", " . . ", " . ", " . ..... ", " . .. ", " . . ", " . .. ", " . . . ..... ", " . . . ", " . .. . ", " ..... ... ", " ... . ", " . . ", " . . ", " .. ", " ", " "}; pyformex-0.8.6/pyformex/icons/table.xpm0000644000211500021150000000121311117433757020041 0ustar benebene00000000000000/* XPM */ static char * table_xpm[] = { "22 22 2 1", " c None", ". c black", " ", " ", " ", " .................... ", " .................... ", " .. .. .. .. ", " .. .. .. .. ", " .. .. .. .. ", " .................... ", " .................... ", " .. .. .. .. ", " .. .. .. .. ", " .. .. .. .. ", " .................... ", " .................... ", " .. .. .. .. ", " .. .. .. .. ", " .. .. .. .. ", " .................... ", " .................... ", " ", " "}; pyformex-0.8.6/pyformex/icons/view-zr-xu.xpm0000644000211500021150000000132510761615361021010 0ustar benebene00000000000000/* XPM */ static char * view_zr_xu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " .. .. ", " . ... ", " ... . ", " ..... ... ", " . .. .. ", " . ", " . ", " . ..... ", " . .. ", " . . ", " . .. ", " . ..... ", " . ", " . . ", " . .. ", " .............. ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/flatwire.xpm0000644000211500021150000000236710654036551020576 0ustar benebene00000000000000/* XPM */ static char *dummy[]={ "32 32 5 1", "# c #000000", "c c #0000ff", "a c #008000", ". c None", "b c #ff0000", "................................", "...............##...............", ".............##aa##.............", "...........##aaaaaa##...........", ".........##aaaaaaaaaa##.........", "......###aaaaaaaaaaaaaa###......", "....##aaaaaaaaaaaaaaaaaaaa##....", "..##aaaaaaaaaaaaaaaaaaaaaaaa##..", ".#aaaaaaaaaaaaaaaaaaaaaaaaaaaa#.", ".###aaaaaaaaaaaaaaaaaaaaaaaa###.", ".####aaaaaaaaaaaaaaaaaaaaaa####.", ".#bb##aaaaaaaaaaaaaaaaaaaa##cc#.", "..#bb###aaaaaaaaaaaaaaaa###cc#..", "..#bbb###aaaaaaaaaaaaaa###ccc#..", "..#bbbbb##aaaaaaaaaaaa##ccccc#..", "...#bbbbb##aaaaaaaaaa##ccccc#...", "...#bbbbbb###aaaaaa###cccccc#...", "...#bbbbbbb###aaaa###ccccccc#...", "....#bbbbbbbb##aa##cccccccc#....", "....#bbbbbbbbb####ccccccccc#....", "....#bbbbbbbbbb##cccccccccc#....", ".....#bbbbbbbbb##ccccccccc#.....", "......#bbbbbbbb##cccccccc#......", ".......#bbbbbbb##ccccccc#.......", "........#bbbbbb##cccccc#........", ".........#bbbbb##ccccc#.........", "..........#bbbb##cccc#..........", "...........#bbb##ccc#...........", "............#bb##cc#............", ".............#b##c#.............", "..............####..............", "...............##..............."}; pyformex-0.8.6/pyformex/icons/view-zr-yu.xpm0000644000211500021150000000132510761615361021011 0ustar benebene00000000000000/* XPM */ static char * view_zr_yu_xpm[] = { /* width height ncolors chars_per_pixel */ "22 22 2 1", /* colors */ " c None", ". c black", /* pixels */ " ", " ", " . . ", " . . ", " . .. . ", " ... ... ", " ..... . ", " . . ", " . . . ", " . .. ..... ", " . .. ", " . . ", " . .. ", " . ..... ", " . ", " . . ", " . .. ", " ............... ", " .. ", " . ", " ", " "}; pyformex-0.8.6/pyformex/icons/rotdown.xpm0000644000211500021150000000133110474005063020435 0ustar benebene00000000000000/* XPM */ static char *rotdown[] = { /* width height ncolors chars_per_pixel */ "22 22 3 1", /* colors */ " c #000", ". c #FFF", "X c None", /* pixels */ "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXX XXXXXXXXXX", "XXXXXXX XXXXXXXX", "XXXXXX XXX XXXXXXX", "XXXXXX XXXXX XXXXXX", "XXXXX XXXXXXX XXXXXX", "XXXXX XXXXXXX XXXXXXX", "XXXXX XXXXXXXXXXXXXXX", "XX X X XX", "XXXXX XXXXXXXXXXXXXXX", "XXXXX XXXXXXXXXXXXXXX", "XXXXX XXX XXXXXX", "XXXXX XXXX XXXXXX", "XXXXXX XXXX XXXXXX", "XXXXXX XX XXXXXX", "XXXXXXX X XXXXXX", "XXXXXXXXX XXX XXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX", "XXXXXXXXXXXXXXXXXXXXXX" }; pyformex-0.8.6/pyformex/olist.py0000644000211500021150000001046211705104656016617 0ustar benebene00000000000000# $Id: olist.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Some convenient shortcuts for common list operations. While most of these functions look (and work) like set operations, their result differs from using Python builtin Sets in that they preserve the order of the items in the lists. """ def roll(a,n=1): """Roll the elements of a list n positions forward (backward if n < 0)""" return a[n:] + a[:n] def union(a,b): """Return a list with all items in a or in b, in the order of a,b.""" return a + [ i for i in b if i not in a ] def difference(a,b): """Return a list with all items in a but not in b, in the order of a.""" return [ i for i in a if i not in b ] def symdifference(a,b): """Return a list with all items in a or b but not in both.""" return difference(a,b) + difference(b,a) def intersection (a,b): """Return a list with all items in a and in b, in the order of a.""" return [ i for i in a if i in b ] def concatenate(a): """Concatenate a list of lists""" return reduce(list.__add__,a) def flatten(a,recurse=False): """Flatten a nested list. By default, lists are flattened one level deep. If recurse=True, flattening recurses through all sublists. >>> flatten([[[3.,2,],6.5,],[5],6,'hi']) [[3.0, 2], 6.5, 5, 6, 'hi'] >>> flatten([[[3.,2,],6.5,],[5],6,'hi'],True) [3.0, 2, 6.5, 5, 6, 'hi'] """ r = [] for i in a: if type(i) == list: if recurse: r.extend(flatten(i,True)) else: r.extend(i) else: r.append(i) return r def select(a,b): """Return a subset of items from a list. Returns a list with the items of a for which the index is in b. """ return [ a[i] for i in b ] def collectOnLength(items,return_indices=False): """Collect items of a list in separate bins according to the item length. items is a list of items of any type having the len() method. The items are put in separate lists according to their length. The return value is a dict where the keys are item lengths and the values are lists of items with this length. If return_indices is True, a second dict is returned, with the same keys, holding the original indices of the items in the lists. """ if return_indices: res,ind = {},{} for i,item in enumerate(items): li = len(item) if li in res.keys(): res[li].append(item) ind[li].append(i) else: res[li] = [ item ] ind[li] = [ i ] return res,ind else: res = {} for item in items: li = len(item) if li in res.keys(): res[li].append(item) else: res[li] = [ item ] return res if __name__ == "__main__": a = [1,2,3,5,6,7] b = [2,3,4,7,8,9] print(a) print(b) print(union(a,b)) print(difference(a,b)) print(difference(b,a)) print(symdifference(a,b)) print(intersection(a,b)) print(select(a,[1,3])) print(concatenate([a,b,a])) print(flatten([1,2,a,[a]])) print(flatten([1,2,a,[a]],recurse=True)) # End pyformex-0.8.6/pyformex/pyformexrc0000644000211500021150000002261511705104656017237 0ustar benebene00000000000000# This is the default pyFormex config file. DO NOT CHANGE IT! -*- PYTHON -*- # $Id: pyformexrc 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """pyformexrc This is the global configuration file for pyFormex. It should always be present in the installation path and not be changed. Changes should go to /etc/pyformexrc (for site-wide changes) or to ~/.pyformex/pyformexrc (for personal settings). If your are missing these files, you can safely copy (parts of) this file as a start. Config settings are read from the following files in order: this file, /etc/pyformexrc, ~/.pyformex/pyformexrc. The last settings override previous ones. The config files are read by the :class:`config.Config`. This means that the syntax is a subset of Python. Two config variables are already set on entry: `pyformexdir`, `homedir` """ import os # files and directories workdir = "." icondir = os.path.join(pyformexdir,'icons') examplesdir = os.path.join(pyformexdir,'examples') datadir = os.path.join(pyformexdir,'data') bindir = os.path.join(pyformexdir,'bin') userconfdir = os.path.join(homedir,'.pyformex') siteprefs = '/etc/pyformexrc' localprefs = '.pyformexrc' scripttemplate = os.path.join(pyformexdir,'template.py') # scriptdirs is a list of (title,dir) tuples # for 'examples', the directory will be filled in automagically scriptdirs = [ ('Examples','') ] # extra paths to append tot sys.path syspath = [] # commands ### THESE MAY BE CHANGED BY THE INSTALLATION PROCEDURE editor = os.environ.get('VISUAL','') viewer = 'firefox' # html viewer browser = 'firefox' # web browser fortune = 'fortune -s' # command to generate a fortune cookie autoglobals = True uselib = True [warnings] nice = True popup = True # Warnings to be filtered out: a list of tuples (message,module,category) # module and category have defaults # Example: filterwarnings = [('warn_viewport_switching',)] # This should best be left empty and only filled by the user # Warnings from upstream packages can be filtered out here filters = [('.*return_index.*','numpy')] [gui] #imagesfromeps = True fortune = False # show a fortune cookie on startup history = [] history_max = 20 splash = os.path.join(icondir,'pyformex-splash.png') plugins = ['geometry_menu'] style = "Plastique" size=(800,600) bdsize=size pos=(0,0) fullscreen = False icontype = '.xpm' prefsmenu = True # add settings menu menu viewportmenu = True # add viewport menu cameramenu = True # add camera menu modemenu = True # add rendermode menu viewmenu = True # add views menu examplesmenu = True # add examples menu camerabar = 'top' # add camera toolbar to top toolbar space modebar = 'top' # add rendemode toolbar to top toolbar space viewbar = 'top' # add views toolbar to top toolbar space normalsbutton = True # add normals button to toolbar lightbutton = True # add light button to toolbar shrinkbutton = False # add shrink button to toolbar timeoutbutton = False # add timeout button to toolbar timeoutvalue = 2 # timeout after this number of seconds coordsbox = False # add coords display widget to toolbar defviews = [('front','view-xr-yu'),('back','view-xl-yu'),\ ('right','view-zl-yu'),('left','view-zr-yu'),\ ('top','view-xr-zd'),('bottom','view-xr-zu'),\ ('iso','view-iso1'),\ ] zoomfactor = 1.15 # zooming factor for zoom buttons rotfactor = 5. # rotation angle for rotation buttons panfactor = 0.05 # translation factor for pan buttons autozoomfactor = 1.732 # enlargement factor for the autozoom (sqrt(3)) wheelzoomfactor = 0.25 # enlargement factor for the wheelzoom dynazoom = ['area','dolly'] # preferred zoom technique ('area', 'dolly', 'lens' or 'none') wheelzoom = 'area' # action performed by the wheel ('area', 'dolly', 'lens') plot2d = 'gnuplot' easter_egg = () layout = 1 showfocus = True # Default canvas settings [canvas] bgmode = 'solid' # solid background mode bgcolor = 'grey90' # canvas background color, left/top for graded modes bgcolor2 = 'grey80' # canvas background color, right/bottom fgcolor = 'black' # default drawing color bkcolor = None # if defined, backsides of planes have this color slcolor = 'yellow' # highlighting color colormap = ['black','red','green','blue','cyan','magenta','yellow','white'] bkcolormap = colormap transparency = 0.5 # default transparency value pointsize = 4.0 # size of points linewidth = 1.0 # width of lines linestipple = (0,1) # line stipple (factor,pattern) marksize = 0.4 # size of 3D marks marktype = 'sphere' # type of 3D mark rendermode = 'wireframe' # default rendering mode [mark] avgnormalsize = '_auto_' [draw] wait = 0.0 # time(sec.) to wait between draw operations flywait = 0.3 # time(sec.) to wait between subsequent views in auto flying localaxes = True # by default, buttons rotate/pan in local axes quadline = [] # 'line3' ] quadsurf = [] # [ 'quad8', 'quad9', 'hex20' ] [pick] size=(12,12) # size of the pick window [material] matte = dict(ambient=0.8,diffuse=0.8,specular=0.2) default = dict(ambient=0.3,diffuse=0.3,specular=0.6) plastic = dict(ambient=0.9,diffuse=0.9,specular=0.7) metal = dict(ambient=0.1,diffuse=0.1,specular=0.9) [light] light0 = {'enabled':True,'ambient':0.3,'diffuse':1.0,'specular':0.2,'position':(1.,1.,1.,0.)} light1 = {'enabled':True,'ambient':0.0,'diffuse':1.0,'specular':0.2,'position':(-1.,1.,1.,0.)} light2 = {'enabled':False,'ambient':0.0,'diffuse':1.0,'specular':0.2,'position':(0.,0.,1.,0.)} #light3 = {'enabled':False,'ambient':0.6,'diffuse':0.7,'specular':0.2,'position':(1.,-1.,1.,0.)} #light4 = {'enabled':False,'ambient':0.6,'diffuse':0.5,'specular':0.3,'position':(1.,1.,-1.,0.)} #light5 = {'enabled':False,'ambient':0.6,'diffuse':0.5,'specular':0.3,'position':(-1.,1.,-1.,0.)} #light6 = {'enabled':False,'ambient':0.6,'diffuse':0.5,'specular':0.3,'position':(-1.,-1.,-1.,0.)} #light7 = {'enabled':False,'ambient':0.6,'diffuse':0.5,'specular':0.3,'position':(1.,-1.,-1.,0.)} [render] mode = 'wireframe' lighting = None # smooth modes will switch it on lightmodel = 'ambient and diffuse' # currently only model supported ambient = 0.2 # does this really do anything? material = 'default' # the name of a material defined above lights = ['light0','light1'] # a list of enabled lights avgnormaltreshold = 0.5 line = '2' # '1' or '2'; if '1', quadratic lines are shown as linear surface = '1' # '1' or '2'; if '1', quadratic surfaces are shown as linear volume = True # if False, volumes are shown as their border surface [help] htmldir = os.path.join(pyformexdir,'doc','html') localdoc = os.path.join(htmldir,"index.html") refman = os.path.join(htmldir,"refman.html") tutorial = os.path.join(htmldir,"tutorial.html") index = os.path.join(htmldir,"genindex.html") modindex = os.path.join(htmldir,"py-modindex.html") running = os.path.join(htmldir,"running.html") readme = os.path.join(pyformexdir,"doc","README") notes = os.path.join(pyformexdir,"doc","ReleaseNotes") license = os.path.join(pyformexdir,"doc","COPYING") style = os.path.join(pyformexdir,"doc","STYLE") devtodo = os.path.join(pyformexdir,"..","TODO") devhowto = os.path.join(pyformexdir,"..","HOWTO-dev.rst") website = "http://pyformex.org" webdoc = website+"/doc" projpage = "http://savannah.nongnu.org/projects/pyformex/" support = "http://savannah.nongnu.org/support/?func=additem&group=pyformex" bugs = "http://savannah.nongnu.org/bugs/?func=additem&group=pyformex" links = [('pyFormex','pyformex.org'),('pyFormex-dev',projpage),('Python','www.python.org'),('IBiTech-bioMMeda','www.biommeda.ugent.be'),('stent-boys@bioMMeda','www.stent-ibitech.ugent.be'),('FEops','www.feops.com'),] docs = [('&Local documentation',localdoc),('&Reference Manual',refman),('Tutorial',tutorial),('&Module Index',modindex),('&Index',index),('&Running pyFormex',running),('&Online documentation',webdoc),] docs2 = [('&Readme',readme),('&ReleaseNotes',notes),('&License',license)] developer = [('Developer HOWTO',devhowto),('pyFormex TODO list',devtodo),('Numpy documentation guidelines','http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines'),('re-structured text (reST)','http://docutils.sourceforge.net/rst.html')] [keys] save = 'F2' [surface] stlread = '.off' [mail] sender = '' server = 'localhost' [jobs] hosts = ['localhost'] host = 'localhost' inputdir = '~' outputdir = '~' #End pyformex-0.8.6/pyformex/messages.py0000644000211500021150000001213611705104656017274 0ustar benebene00000000000000# $Id: messages.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Error and Warning Messages """ import pyformex as pf def getMessage(msg): """Return the real message corresponding with the specified mnemonic. If no matching message was defined, the original is returned. """ msg = str(msg) # allows for msg being a Warning return globals().get(msg,msg) warn_flat_removed = "The 'flat=True' parameter of the draw function has been replaced with 'nolight=True'." warn_viewport_linking = "Linking viewports is an experimental feature and is not fully functional yet." warn_avoid_sleep = """.. warn_avoid_sleep Avoid sleep function -------------------- The sleep function may cause a heavy processor load during it wait cycle, and its use should therefore be avoided. Depending on your intentions, there are several better alternatives: - the `Draw Wait Time` preference setting, - the delay() and wait() functions, - the pause() function, """ warn_old_table_dialog = "The use of OldTableDialog is deprecated. Please use a combination of the Dialog, Tabs and Table widgets." warn_widgets_updatedialogitems = "gui.widgets.updateDialogItems now expects data in the new InputItem format. Use gui.widgets.updateOldDialogItems for use with old data format." warn_deprecated_inputitem = "Using a list or tuple as InputItem data is deprecated. Please use the new dict format." _future_deprecation = "This functionality is deprecated and will probably be removed in future, unless you explain to the developers why they should retain it." warn_quadratic_drawing = """.. Quadratic surface drawing ------------------------- We have started implementing quadratic surface drawing. Currently, quad8 and quad9 elements can be drawn as quadratics in the smooth or flat rendering style. To activate the quadratic surface drawing, change the default in Settings->Drawing. Developers: please test and report. """ warn_mesh_reverse = "The meaning of Mesh.reverse has changed. Before, it would just reorder the nodes of the elements in backwards order (just like the Formex.reverse still does. The new definition of Mesh.reverse however is to reverse the line direction for 1D eltypes, to reverse the normals for 2D eltypes and to turn 3D volumes inside out. This definition may have more practical use. It can e.g. be used to fix meshes after a mirroring operation." warn_mesh_reflect = "The Mesh.reflect will now by default reverse the elements after the reflection, since that is what the user will want in most cases. The extra reversal can be skipped by specifying 'reverse=False' in the argument list of the `reflect` operation." radio_enabler = "A 'radio' type input item can currently not be used as an enabler for other input fields." warn_pattern = "The 'pattern' function has changed! It now returns a list of points, with integer (grid) coordinates. You can still get the old behavior of creating pairs of connected points by using the 'lpattern' function. If your intent is to initialize a Formex, you can just prepend 'l:' to the string and use that string directly as data to the Formex() initializer." warn_no_dxfparser = """.. No dxfparser ------------ I can not import .DXF format on your machine, because I can not find the required external program *dxfparser*. *dxfparser* comes with pyFormex, so this probably means that it just was not (properly) installed. The pyFormex install manual describes how to do it. """ if pf.svnversion: warn_no_dxfparser += """ If you are running pyFormex from SVN sources and you can get root access, you can go to the directory `...pyformex/extra/dxfparser/` and follow the instructions there, or you can just try the **Install externals** menu option of the **Help** menu. """ warn_old_project = """.. Old project format ------------------ This is an old format project file. Unless you need to read this project file from an older pyFormex version, we strongly advise you to convert the project file to the latest format. Otherwise future versions of pyFormex might not be able to read it back. """ # End pyformex-0.8.6/pyformex/flatkeydb.py0000644000211500021150000004024311705104656017432 0ustar benebene00000000000000#!/usr/bin/python # $Id: flatkeydb.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Flat Text File Database. A simple database stored as a flat text file. | (C) 2005 Benedict Verhegghe. | Distributed under the GNU GPL version 3 or later. """ # A few utility functions def firstWord(s): """Return the first word of a string. Words are delimited by blanks. If the string does not contain a blank, the whole string is returned. """ n = s.find(' ') if n >= 0: return s[:n] else: return s def unQuote(s): """Remove one level of quotes from a string. If the string starts with a quote character (either single or double) and ends with the SAME character, they are stripped of the string. """ if len(s) > 0 and s[0] in "'\"" and s[-1] == s[0]: return s[1:-1] else: return s def splitKeyValue(s,key_sep): """Split a string in a (key,value) on occurrence of key_sep. The string is split on the first occurrence of the substring key_sep. Key and value are then stripped of leading and trailing whitespace. If there is no key_sep, the whole string becomes the key and the value is an empty string. If the string starts with key_sep, the key becomes an empty string. """ n = s.find(key_sep) if n >= 0: return ( s[:n], s[n+len(key_sep):] ) else: return ( s, '' ) def ignore_error(dummy): """This function can be used to override the default error handlers. The effect will be to ignore the error (duplicate key, invalid record) and to not add the affected data to the database. """ pass # The Flat text file database class class FlatDB(dict): """A database stored as a dictionary of dictionaries. Each record is a dictionary where keys and values are just strings. The field names (keys) can be different for each record, but there is at least one field that exists for all records and will be used as the primary key. This field should have unique values for all records. The database itself is also a dictionary, with the value of the primary key as key and the full record as value. On constructing the database a list of keys must be specified that will be required for each record. The first key in this list will be used as the primary key. Obviously, the list must at least have one required key. The database is stored in a flat text file. Each field (key,value pair) is put on a line by itself. Records are delimited by a (beginrec, endrec) pair. The beginrec marker can be followed by a (key,value) pair on the same line. The endrec marker should be on a line by itself. If endrec is an empty string, each occurrence of beginrec will implicitly end the previous record. Lines starting with the comment string are ignored. They can occur anywhere between or inside records. Blank lines are also ignored (except they serve as record delimiter if endrec is empty) Thus, with the initialization:: FlatDB(req_keys=['key1'], comment = 'com', key_sep = '=', beginrec = 'rec', endrec = '') the following is a legal database:: com This is a comment com rec key1=val1 key2=val2 rec com Yes, this starts another record key1=val3 key3=val4 The `readFile` function can even be instructed to ignore anything not between a (beginrec,endrec) pair. This allows for multiple databases being stored on the same file, even with records intermixed. Keys and values can be any strings, except that a key can not begin nor end with a blank, and can not be equal to any of the comment, beginrec or endrec markers. Whitespace around the key is always stripped. By default, this is also done for the value (though this can be switched off.) If strip_quotes is True (default), a single pair of matching quotes surrounding the value will be stripped off. Whitespace is stripped before stripping the quotes, so that by including the value in quotes, you can keep leading and trailing whitespace in the value. A record checking function can be specified. It takes a record as its argument. It is called whenever a new record is inserted in the database (or an existing one is replaced). Before calling this ``check_func``, the system will already have checked that the record is a dictionary and that it has all the required keys. Two error handlers may be overridden by the user: - record_error_handler(record) is called when the record does not pass the checks; - key_error_handler(key) is called when a dunplicat key is encountered. The default for both is to raise an error. Overriding is done by changing the instance attibute. """ def __init__(self, req_keys=[], comment = '#', key_sep = '=', beginrec = 'beginrec', endrec = 'endrec', strip_blanks = True, strip_quotes = True, check_func = None, ): """Initialize a new (empty) database. Make sure that the arguments are legal.""" dict.__init__(self) self.req_keys = map(str,list(req_keys)) self.key = self.req_keys[0] self.comment = str(comment) self.key_sep = str(key_sep) self.beginrec = str(beginrec) self.endrec = str(endrec) self.strip_quotes = strip_quotes self.check_func = check_func self.error_msg = '' if self.check_func and not callable(check_func): raise TypeError, "FlatDB: check_func should be callable" def newRecord(self): """Returns a new (empty) record. The new record is a temporary storage. It should be added to the database by calling append(record). This method can be overriden in subclasses. """ return {}.fromkeys(self.req_keys) def checkKeys(self, record): """Check that record has the required keys.""" return reduce(int.__and__,map(record.has_key,self.req_keys),True) def checkRecord(self, record): """Check a record. This function checks that the record is a dictionary type, that the record has the required keys, and that check_func(record) returns True (if a `check_func` was specified). If the record passes, just return True. If it does not, call the `record_error_handler` and (if it returns) return False. This method can safely be overriden in subclasses. """ OK = type(record) == dict and self.checkKeys(record) and ( self.check_func == None or self.check_func(record) ) if not OK: self.record_error_handler(record) return OK def record_error_handler(self,record): """Error handler called when a check error on record is discovered. Default is to raise a runtime error. This method can safely be overriden in subclasses. """ raise ValueError, "FlatDB: invalid record : %s" % record def key_error_handler(self,key): """Error handler called when a duplicate key is found. Default is to raise a runtime error. This method can safely be overriden in subclasses. """ raise ValueError, "FlatDB: duplicate key : '%s'" % key def __setitem__(self, key, record): """Sets the record with specified primary key (if record is valid). This will change the primary key value of the record to the value of key. """ if self.checkRecord(record): record[self.key] = key dict.__setitem__(self, key, record) def insert(self, record): """Insert a record to the database, overwriting existing records. This is equivalent to `__setitem__` but using the value stored in the the primary key field of the record as key for storing the record. This is also similar to append(), but overwriting an old record with the same primary key. """ self.__setitem__(record[self.key], record) def append(self, record): """Add a record to the database. Since the database is a dictionary, keys are unique and appending a record with an existing key is not allowed. If you want to overwrite the old record, use insert() instead. """ if self.has_key(record[self.key]): self.key_error_handler(record[self.key]) else: self.insert(record) def splitKeyValue(self,line): """Split a line in key,value pair. The field is split on the first occurrence of the `key_sep`. Key and value are then stripped of leading and trailing whitespace. If there is no key_sep, the whole line becomes the key and the value is an empty string. If the key_sep is the first character, the key becomes an empty string. """ key,value = splitKeyValue(line,self.key_sep) key = key.rstrip() value = value.lstrip() if self.strip_quotes: value = unQuote(value) return (key,value) def parseLine(self,line): """Parse a line of the flat database file. A line starting with the comment string is ignored. Leading whitespace on the remaining lines is ignored. Empty (blank) lines are ignored, unless the ENDREC mark was set to an empty string, in which case they count as an end of record if a record was started. Lines starting with a 'BEGINREC' mark start a new record. The remainder of the line is then reparsed. Lines starting with an 'ENDREC' mark close and store the record. All lines between the BEGINREC and ENDREC should be field definition lines of the type 'KEY [ = VALUE ]'. This function returns 0 if the line was parsed correctly. Else, the variable self.error_msg is set. """ if len(self.comment) > 0 and line.startswith(self.comment): return 0 line = line.lstrip() if len(line) > 0 and line[-1] == '\n': line = line[:-1] if len(line) == 0: if self.endrec != '' or self.record == None: # ignore empty lines in these cases return 0 w = firstWord(line) if w == self.endrec: if self.record == None: self.error_msg = "Found endrec without previous beginrec" return 1 else: self.append(self.record) self.record = None return 0 elif w == self.beginrec: if self.record == None or self.endrec == '': self.record = self.newRecord() # parse rest of beginrec line, if not empty # this allows fields or comments on the beginrec line line = line[len(w):].lstrip() if len(line) > 0: return self.parseLine(line) else: return 0 else: self.error_msg = "Found beginrec without previous endrec" return 1 else: if self.record == None: if self.beginrec == '': self.record = self.newRecord() else: self.error_msg = "Unrecognized line '%s'" % line return 1 key,value = self.splitKeyValue(line) self.record[key] = value return 0 return 0 def parse(self, lines, ignore=False, filename=None): """Read a database from text. lines is an iterater over text lines (e.g. a text file or a multiline string splitted on '\\n') Lines starting with a comment string are ignored. Every record is delimited by a (beginrec,endrec) pair. If ignore is True, all lines that are not between a (beginrec,endrec) pair are simply ignored. Default is to raise a RuntimeError. """ self.record = None linenr = 0 for line in lines: linenr += 1 if self.parseLine(line) != 0 and not ignore: raise RuntimeError, "FlatDB: error while reading line %d of database (File: %s)\n%s" % (linenr,filename,self.error_msg) break def readFile(self, filename, ignore=False): """Read a database from file. Lines starting with a comment string are ignored. Every record is delimited by a (beginrec,endrec) pair. If ignore is True, all lines that are not between a (beginrec,endrec) pair are simply ignored. Default is to raise a RuntimeError. """ infile=None try: infile = open(filename,'r') lines = infile.readlines() finally: if infile: infile.close() self.parse(lines,ignore,filename) def writeFile(self,filename,mode='w',header=None): """Write the database to a text file. Default mode is 'w'. Use 'a' to append to the file. The header is written at the start of the database. Make sure to start each line with a comment marker if you want to read it back! """ outfile = open(filename,mode) if type(header) == str: outfile.writelines(header) for record in self.itervalues(): s = self.beginrec+'\n' for (k, v) in record.iteritems(): s += " %s%s%s\n" % (k,self.key_sep,v) s += self.endrec+'\n' outfile.writelines(s) if type(outfile) == file: outfile.close() def match(self,key,value): """Return a list of records matching key=value. This returns a list of primary keys of the matching records. """ return [ i for i in self.iterkeys() if self[i].has_key(key) and self[i][key] == value ] if __name__ == '__main__': db = FlatDB(['aa']) db.append({'aa':'bb'}) db.append({'aa':'cc'}) print(db) print(db['bb']) db[1] = { 'aa':'dd'} print(db) print(len(db)) mat = FlatDB(['name'],beginrec='material',endrec='endmaterial') mat.readFile('data/materials.db') mat.append({'name':'concrete', 'junk':''}) print(mat) mat.writeFile('materials.copy') for i in mat.match('name','steel'): print(mat[i]) mat = FlatDB(req_keys=['name'],beginrec='material',endrec='endmaterial') mat.readFile('data/materials.db') mat.append({'name':'concrete'}) try: mat.append({'junk':'concrete'}) except: print("Could not append record without 'name' field") print(mat) mat.key_error_handler = ignore_error mat.append({'name':'concrete'}) print(mat) # Variant without endmarker mat = FlatDB(req_keys=['name'],beginrec='material',endrec='') mat.readFile('data/materials.db',ignore=True) print(mat) # Variant without begin/endrec markers: records separated by blanks mat = FlatDB(req_keys=['name'],beginrec='',endrec='') mat.readFile('data/materials.db') print(mat) # End pyformex-0.8.6/pyformex/config.py0000644000211500021150000003454311705104656016740 0ustar benebene00000000000000# $Id: config.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A general yet simple configuration class. | (C) 2005 Benedict Verhegghe | Distributed under the GNU GPL version 3 or later Why I wrote this simple class because I wanted to use Python expressions in my configuration files. This is so much more fun than using .INI style config files. While there are some other Python config modules available on the web, I couldn't find one that suited my needs and my taste: either they are intended for more complex configuration needs than mine, or they do not work with the simple Python syntax I expected. What Our Config class is just a normal Python dictionary which can hold anything. Fields can be accessed either as dictionary lookup (config['foo']) or as object attributes (config.foo). The class provides a function for reading the dictionary from a flat text (multiline string or file). I will always use the word 'file' hereafter, because that is what you usually will read the configuration from. Your configuration file can have named sections. Sections are stored as other Python dicts inside the top Config dictionary. The current version is limited to one level of sectioning. """ import copy from mydict import Dict,returnNone def formatDict(d): """Format a dict in Python source representation. Each (key,value) pair is formatted on a line of the form:: key = value The resulting text is a legal Python script to define the items in the dict. """ s = "" if isinstance(d,dict): for k,v in d.iteritems(): if type(v) == str: s += '%s = "%s"\n' % (k,v) else: s += '%s = %s\n' % (k,v) return s class Config(Dict): """A configuration class allowing Python expressions in the input. The configuration settings are stored in the __dict__ of a Python object. An item 'foo' in the configuration 'config' can be accessed either as dictionary lookup (``config['foo']``) or as object attribute (``config.foo``). The configuration object can be initialized from a multiline string or a text file (or any other object that allows iterating over strings). The format of the config file/text is described hereafter. All config lines should have the format: key = value, where key is a string and value is a Python expression The first '=' character on the line is the delimiter between key and value. Blanks around both the key and the value are stripped. The value is then evaluated as a Python expression and stored in a variable with name specified by the key. This variable is available for use in subsequent configuration lines. It is an error to use a variable before it is defined. The key,value pair is also stored in the config dictionary, unless the key starts with an underscore ('_'): this provides for local variables. Lines starting with '#' are comments and are ignored, as are empty and blank lines. Lines ending with '\' are continued on the next line. A line starting with '[' starts a new section. A section is nothing more than a Python dictionary inside the config dictionary. The section name is delimited by '['and ']'. All subsequent lines will be stored in the section dictionary instead of the toplevel dictionary. All other lines are executed as python statements. This allows e.g. for importing modules. Whole dictionaries can be inserted at once in the config with the update() function. All defined variables while reading config files remain available for use in the config file statements, even over multiple calls to the read() function. Variables inserted with addSection() will not be available as individual variables though, but can be access as ``self['name']``. As an example, if your config file looks like:: aa = 'bb' bb = aa [cc] aa = 'aa' _n = 3 rng = range(_n) the resulting configuration dictionary is ``{'aa': 'bb', 'bb': 'bb', 'cc': {'aa': 'aa', 'rng': [0, 1, 2]}}`` As far as the resulting Config contents is concerned, the following are equivalent:: C.update({'key':'value'}) C.read("key='value'\\n") There is an important difference though: the second line will make a variable key (with value 'value') available in subsequent Config read() method calls. """ def __init__(self,data={},default=None): """Creates a new Config instance. The configuration can be initialized with a dictionary, or with a variable that can be passed to the read() function. The latter includes the name of a config file, or a multiline string holding the contents of a configuration file. """ Dict.__init__(self,default=default) if isinstance(data,dict): self.update(data) elif data: self.read(data) def update(self,data={},name=None,removeLocals=False): """Add a dictionary to the Config object. The data, if specified, should be a valid Python dict. If no name is specified, the data are added to the top dictionary and will become attributes. If a name is specified, the data are added to the named attribute, which should be a dictionary. If the name does not specify a dictionary, an empty one is created, deleting the existing attribute. If a name is specified, but no data, the effect is to add a new empty dictionary (section) with that name. If removeLocals is set, keys starting with '_' are removed from the data before updating the dictionary and not included in the config. This behaviour can be changed by setting removeLocals to false. """ if removeLocals: for k in data.keys(): if k[0] == '_': del data[k] if name: if not self.has_key(name) or not isinstance(self[name],dict): self[name] = Dict() self[name].update(data) else: Dict.update(self,data) def _read_error(self,filename,lineno,line): if filename: where = 'config file %s,' % filename else: where = '' raise RuntimeError,'Error in %s line %d:\n%s' % (where,lineno,line) def read(self,fil,debug=False): """Read a configuration from a file or text `fil` is a sequence of strings. Any type that allows a loop like ``for line in fil:`` to iterate over its text lines will do. This could be a file type, or a multiline text after splitting on '\\n'. The function will try to react intelligently if a string is passed as argument. If the string contains at least one '\\n', it will be interpreted as a multiline string and be splitted on '\\n'. Else, the string will be considered and a file with that name will be opened. It is an error if the file does not exist or can not be opened. The function returns self, so that you can write: cfg = Config(). """ filename = None if type(fil) == str: if fil.find('\n') >= 0: fil = fil.split('\n') else: filename = fil fil = open(fil,'r') section = None contents = {} lineno = 0 continuation = False comments = False for line in fil: lineno += 1 ls = line.strip() if comments: comments = ls[-3:] != '"""' ls = '' else: comments = ls[:3] == '"""' if comments or len(ls)==0 or ls[0] == '#': continue if continuation: s += ls else: s = ls continuation = s[-1] == '\\' if s[-1] == '\\': s = s[:-1] if continuation: continue if s[0] == '[': if contents: self.update(name=section,data=contents,removeLocals=True) contents = {} i = s.find(']') if i<0: self.read_error(filename,lineno,line) section = s[1:i] if debug: print("Starting new section '%s'" % section) continue else: if debug: print("READ: "+line) i = s.find('=') if i >= 0: key = s[:i].strip() if len(key) == 0: self.read_error(filename,lineno,line) contents[key] = eval(s[i+1:].strip()) globals().update(contents) else: exec(s) if contents: self.update(name=section,data=contents,removeLocals=True) return self def __setitem__(self, key, val): """Allows items to be set as self[section/key] = val. """ i = key.rfind('/') if i == -1: self.update({key:val}) else: self.update({key[i+1:]:val},key[:i]) def __getitem__(self, key): """Allows items to be addressed as self[key]. This is equivalent to the Dict lookup, except that items in subsections can also be retrieved with a single key of the format section/key. While this lookup mechanism works for nested subsections, the syntax for config files allows for only one level of sections! Also beware that because of this functions, no '/' should be used inside normal keys and sections names. """ i = key.rfind('/') if i == -1: return Dict.__getitem__(self, key) else: try: return self[key[:i]][key[i+1:]] except KeyError: return self._default_(key) def __delitem__(self,key): """Allows items to be delete with del self[section/key]. """ i = key.rfind('/') if i == -1: Dict.__delitem__(self,key) else: del self[key[:i]][key[i+1:]] def __str__(self): """Format the Config in a way that can be read back. This function is mostly used to format the data for writing it to a configuration file. See the write() method. The return value is a multiline string with Python statements that can be read back through Python to recreate the Config data. Usually this is done with the Config.read() method. """ s = '' for k,v in self.iteritems(): if not isinstance(v,Dict): s += formatDict({k:v}) for k,v in self.iteritems(): if isinstance(v,Dict): s += "\n[%s]\n" % k s += formatDict(v) return s def write(self,filename,header="# Config written by pyFormex -*- PYTHON -*-\n\n",trailer="\n# End of config\n"): """Write the config to the given file The configuration data will be written to the file with the given name in a text format that is both readable by humans and by the Config.read() method. The header and trailer arguments are strings that will be added at the start and end of the outputfile. Make sure they are valid Python statements (or comments) and that they contain the needed line separators, if you want to be able to read it back. """ fil = open(filename,'w') fil.write(header) fil.write("%s" % self) fil.write(trailer) fil.close() def keys(self,descend=True): """Return the keys in the config. By default this descends one level of Dicts. """ keys = Dict.keys(self) if descend: for k,v in self.iteritems(): if isinstance(v,Dict): keys += ['%s/%s' % (k,ki) for ki in v.keys()] return keys if __name__ == '__main__': def show(s): try: v = eval(s) print("%s = %s" % (s,v)) except: print("%s ! ERROR" % s) C = Config("""# A simple config example aa = 'bb' bb = aa [cc] aa = 'aa' # yes ! comments are allowed (they are stripped by eval()) _n = 3 # local: will get stripped rng = range(_n) """) show("C") show("C['aa']") show("C['cc']") show("C['cc/aa']") show("C['dd']") def reflookup(key): return C[key] D = Config(default = reflookup) show("D") show("D['aa']") show("D['cc']") show("D['cc/aa']") show("D['dd']") D['aa'] = 'wel' D['dd'] = 'hoe' D['cc/aa'] = 'ziedewel' show("D") show("C") show("D['cc/aa']") show("D['cc/rng']") print("BUT!!!!") show("D['cc']") # This should give an error show("D['ee']") show("D.get('ee','NO Error')") show("D.get('cc/ee','NO Error')") D['cc/bb'] = 'ok' show("D.keys()") del D['aa'] del D['cc/aa'] show("D.keys()") del D['cc'] show("D.keys()") # End pyformex-0.8.6/pyformex/main.py0000755000211500021150000005704411705104656016423 0ustar benebene00000000000000#!/usr/bin/python # $Id: main.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # This is the only pyFormex module that is imported by the main script, # so this is the place to put startup code import pyformex as pf import sys,os startup_warnings = '' startup_messages = '' pyformexdir = sys.path[0] pf.svnversion = os.path.exists(os.path.join(pyformexdir,'.svn')) if pf.svnversion: def checkLibraries(): #print "Checking pyFormex libraries" msg = '' libdir = os.path.join(pyformexdir,'lib') libraries = [ 'misc_','nurbs_','drawgl_' ] for lib in libraries: src = os.path.join(libdir,lib+'module.c') obj = os.path.join(libdir,lib+'.so') if not os.path.exists(obj) or os.path.getmtime(obj) < os.path.getmtime(src): msg += "\nThe compiled library '%smodule' is not up to date!" % lib return msg msg = checkLibraries() if msg: print "Rebuilding pyFormex libraries, please wait" cmd = "cd %s/..; make lib" % pyformexdir os.system(cmd) msg = checkLibraries() if msg: msg += """ I had a problem rebuilding the libraries in %s/lib. You should probably exit pyFormex, fix the problem first and then restart pyFormex. """ % pyformexdir startup_warnings += msg import utils # intended Python version minimal_version = '2.5' target_version = '2.7' found_version = utils.hasModule('python') if utils.SaneVersion(found_version) < utils.SaneVersion(minimal_version): #if utils.checkVersion('python',minimal_version) < 0: startup_warnings += """ Your Python version is %s, but pyFormex requires Python >= %s. We advice you to upgrade your Python version. Getting pyFormex to run on Python 2.4 requires only minor adjustements. Lower versions are problematic. """ % (found_version,minimal_version) print startup_warnings sys.exit() if utils.SaneVersion(found_version[:3]) > utils.SaneVersion(target_version): #if utils.checkVersion('python',target_version) > 0: startup_warnings += """ Your Python version is %s, but pyFormex has only been tested with Python <= %s. We expect pyFormex to run correctly with your Python version, but if you encounter problems, please contact the developers at http://pyformex.org. """ % (found_version,target_version,) from config import Config # Remove unwanted warnings # We have moved this to the config file #utils.filterWarning('.*return_index.*','numpy') ########################### main ################################ def filterWarnings(): try: for w in pf.cfg['warnings/filters']: utils.filterWarning(*w) except: pf.debug("Error while processing warning filters: %s" % pf.cfg['warnings/filters']) def refLookup(key): """Lookup a key in the reference configuration.""" try: return pf.refcfg[key] except: pf.debug("!There is no key '%s' in the reference config!"%key) return None def prefLookup(key): """Lookup a key in the reference configuration.""" return pf.prefcfg[key] def printcfg(key): try: print("!! refcfg[%s] = %s" % (key,pf.refcfg[key])) except KeyError: pass print("!! cfg[%s] = %s" % (key,pf.cfg[key])) def setRevision(): sta,out = utils.runCommand('cd %s && svnversion' % pf.cfg['pyformexdir'],quiet=True) if sta == 0 and not out.startswith('exported'): pf.__revision__ = out.strip() def remove_pyFormex(pyformexdir,scriptdir): """Remove the pyFormex installation.""" print(""" BEWARE! This procedure will remove the complete pyFormex installation! You should only use this on a pyFormex installed with 'python setup.py install'. If you continue, pyFormex will exit and you will not be able to run it again. The pyFormex installation is in: %s The pyFormex executable script is in: %s You will need proper permissions to actually delete the files. """ % (pyformexdir,scriptdir)) s = raw_input("Are you sure you want to remove pyFormex? yes/NO: ") if s == 'yes': print("Removing %s" % pyformexdir) utils.removeTree(pyformexdir) script = os.path.join(scriptdir,'pyformex') egginfo = "%s-%s.egg-info" % (pyformexdir,pf.__version__.replace('-','_')) datadir = os.path.commonprefix(['/usr/local/share',pyformexdir]) datadir = os.path.join(datadir,'share') data = utils.prefixFiles(datadir,['man/man1/pyformex.1', 'applications/pyformex.desktop', 'pixmaps/pyformex-64x64.png', 'pixmaps/pyformex.xpm']) for f in [ script,egginfo ] + data: if os.path.exists(f): print("Removing %s" % f) os.remove(f) else: print("Could not remove %s" % f) print("\nBye, bye! I won't be back until you reinstall me!") elif s.startswith('y') or s.startswith('Y'): print("You need to type exactly 'yes' to remove me.") else: print("Thanks for letting me stay this time.") sys.exit() def savePreferences(): """Save the preferences. The name of the preferences file is determined at startup from the configuration files, and saved in ``pyformex.preffile``. If a local preferences file was read, it will be saved there. Otherwise, it will be saved as the user preferences, possibly creating that file. If ``pyformex.preffile`` is None, preferences are not saved. """ if pf.preffile is None: return # Create the user conf dir prefdir = os.path.dirname(pf.preffile) if not os.path.exists(prefdir): try: os.makedirs(prefdir) except: print("The path where your user preferences should be stored can not be created!\nPreferences are not saved!") return # Cleanup up the prefcfg del pf.prefcfg['__ref__'] # Currently erroroneously processed, therefore not saved del pf.prefcfg['render']['light0'] del pf.prefcfg['render']['light1'] del pf.prefcfg['render']['light2'] del pf.prefcfg['render']['light3'] pf.options.debug = 1 pf.debug("="*60) pf.debug("!!!Saving config:\n%s" % pf.prefcfg) try: pf.prefcfg.write(pf.preffile) res = "Saved" except: res = "Could not save" pf.debug("%s preferences to file %s" % (res,pf.preffile)) def apply_config_changes(cfg): """Apply incompatible changes in the configuration cfg is the user configuration that is to be saved. """ # Safety checks if type(cfg['warnings/filters']) != list: cfg['warnings/filters'] = [] # Adhoc changes if type(cfg['gui/dynazoom']) is str: cfg['gui/dynazoom'] = [ cfg['gui/dynazoom'], '' ] for i in range(8): t = "render/light%s"%i try: cfg[t] = dict(cfg[t]) except: pass # Rename settings for old,new in [ ('history','gui/history'), ]: if old in cfg.keys(): if new not in cfg.keys(): cfg[new] = cfg[old] del cfg[old] # Delete settings for key in [ 'input/timeout','filterwarnings', 'render/ambient','render/diffuse','render/specular','render/emission', 'render/material','canvas/propcolors','Save changes', ]: if key in cfg.keys(): print("DELETING CONFIG VARIABLE %s" % key) del cfg[key] def test_module(module): """Run the doctests in the modules docstrings.""" import doctest # Note that a non-empty fromlist is needed to make the # __import__ function always return the imported module # even if a dotted path is specified mod = __import__(module,fromlist=['a']) return doctest.testmod(mod) ########################### app ################################ def run(argv=[]): """This is a fairly generic main() function. It is responsible for reading the configuration file(s), processing the command line options and starting the application. The basic configuration file is 'pyformexrc' located in the pyformex directory. It should always be present and be left unchanged. You can copy this file to another location if you want to make changes. By default, pyformex will try to read the following extra configuration files (in this order: default settings: /pyformexrc system-wide settings: /etc/pyformexrc user settings: $HOME/.pyformex/pyformexrc local settings $PWD/.pyformexrc Also, an extra config file can be specified in the command line. Config file settings always override previous ones. On exit, the preferences that were changed are written to the last read config file. Changed settings are those that differ from the settings in all but the last one. """ # Create a config instance pf.cfg = Config() # Fill in the pyformexdir and homedir variables # (use a read, not an update) if os.name == 'posix': homedir = os.environ['HOME'] elif os.name == 'nt': homedir = os.environ['HOMEDRIVE']+os.environ['HOMEPATH'] pf.cfg.read("pyformexdir = '%s'\n" % pyformexdir) pf.cfg.read("homedir = '%s'\n" % homedir) # Read the defaults (before the options) defaults = os.path.join(pyformexdir,"pyformexrc") pf.cfg.read(defaults) # Process options import optparse from optparse import make_option as MO parser = optparse.OptionParser( # THE Qapp options are removed, because it does not seem to work !!! # SEE the comments in the gui.startGUI function usage = "usage: %prog [] [ [ scriptname [scriptargs] ] ...]", version = utils.FullVersion(), description = pf.Description, formatter = optparse.TitledHelpFormatter(), option_list=[ MO("--gui", action="store_true", dest="gui", default=None, help="start the GUI (default if no scriptfile argument is given)", ), MO("--nogui", action="store_false", dest="gui", default=None, help="do not load the GUI (default if a scriptfile argument is given)", ), MO("--interactive",'-i', action="store_true", dest="interactive", default=False, help="Go into interactive mode after processing the command line parameters. This is implied by the --gui option.", ), MO("--force-dri", action="store_true", dest="dri", default=None, help="Force use of Direct Rendering", ), MO("--force-nodri", action="store_false", dest="dri", default=None, help="Disables the Direct Rendering", ), MO("--uselib", action="store_true", dest="uselib", default=None, help="Use the pyFormex C lib if available. This is the default.", ), MO("--nouselib", action="store_false", dest="uselib", default=None, help="Do not use the pyFormex C-lib.", ), MO("--norst2html", action="store_false", dest="rst2html", default=True, help="Do not try to convert rst messages to html before displaying.", ), MO("--config", action="store", dest="config", default=None, help="Use file CONFIG for settings", ), MO("--nodefaultconfig", action="store_true", dest="nodefaultconfig", default=False, help="Skip the default site and user config files. This option can only be used in conjunction with the --config option.", ), MO("--redirect", action="store_true", dest="redirect", default=False, help="Redirect standard output to the message board (ignored with --nogui)", ), MO("--debug", action="store_const", dest="debug", const=-1, help="display debugging info to sys.stdout", ), MO("--debuglevel", action="store", dest="debug", type="int", default=0, help="display debugging info to sys.stdout", ), ## MO("--classify", ## action="store_true", dest="classify", default=False, ## help="classify the examples in categories", ## ), MO("--newviewports", action="store_true", dest="newviewports", default=False, help="Use the new multiple viewport canvas implementation. This is an experimental feature only intended for developers.", ), MO("--testmodule", action="store", dest="testmodule", default=None, help="Run the docstring tests for module TESTMODULE. TESTMODULE is the name of the module, using . as path separator.", ), MO("--testcamera", action="store_true", dest="testcamera", default=False, help="Print camera settings whenever they change.", ), ## MO("--test", ## action="store_true", dest="test", default=False, ## help="testing mode: only for developers!", ## ), MO("--testexecutor", action="store_true", dest="executor", default=False, help="test alternate executor: only for developers!", ), ## MO("--olddraw", ## action="store_true", dest="olddraw", default=False, ## help="use the old (slower) drawing function: use only when the new function gives problems", ## ), MO("--fastnurbs", action="store_true", dest="fastnurbs", default=False, help="test C library nurbs drawing: only for developers!", ), MO("--listfiles", action="store_true", dest="listfiles", default=False, help="list the pyformex Python source files.", ), MO("--search", action="store_true", dest="search", default=False, help="search the pyformex source for a specified pattern and exit. This can optionally be followed by -- followed by options for the grep command. The final argument is the pattern to search.", ), MO("--remove", action="store_true", dest="remove", default=False, help="remove the pyformex installation and exit", ), MO("--whereami", action="store_true", dest="whereami", default=False, help="show where the pyformex package is installed and exit", ), MO("--detect", action="store_true", dest="detect", default=False, help="show detected helper software and exit", ), ]) pf.options, args = parser.parse_args(argv) pf.print_help = parser.print_help # process options if pf.options.nodefaultconfig and not pf.options.config: print("\nInvalid options: --nodefaultconfig but no --config option\nDo pyformex --help for help on options.\n") sys.exit() pf.debug("Options: %s" % pf.options) ########## Process special options which will not start pyFormex ####### if pf.options.listfiles or \ pf.options.search or \ pf.options.remove or \ pf.options.whereami or \ pf.options.detect or \ pf.options.testmodule: if pf.options.listfiles: print '\n'.join(utils.pyformexFiles(relative=True)) if pf.options.search: if len(args) > 0: #from script import grepSource #print grepSource(args[-1],' '.join(args[:-1]),quiet=True) os.system("grep %s %s" % (' '.join(args),' '.join(utils.pyformexFiles(relative=True)))) if pf.options.remove: remove_pyFormex(pyformexdir,pf.scriptdir) if pf.options.whereami or pf.options.debug : print("Script started from %s" % pf.scriptdir) print("I found pyFormex in %s " % pyformexdir) print("Current Python sys.path: %s" % sys.path) if pf.options.detect or pf.options.debug : print("Detecting all installed helper software") utils.checkExternal() print(utils.reportDetected()) if pf.options.testmodule: for a in pf.options.testmodule.split(','): test_module(a) sys.exit() ########### Read the config files #################### # These values should not be changed pf.cfg.userprefs = os.path.join(pf.cfg.userconfdir,'pyformexrc') pf.cfg.autorun = os.path.join(pf.cfg.userconfdir,'startup.py') # Set the config files if pf.options.nodefaultconfig: sysprefs = [] userprefs = [] else: sysprefs = [ pf.cfg.siteprefs ] userprefs = [ pf.cfg.userprefs ] if os.path.exists(pf.cfg.localprefs): userprefs.append(pf.cfg.localprefs) if pf.options.config: userprefs.append(pf.options.config) if len(userprefs) == 0: # We should always have a place to store the user preferences userprefs = [ pf.cfg.userprefs ] pf.preffile = os.path.abspath(userprefs[-1]) # Settings will be saved here # Read all but the last as reference for f in filter(os.path.exists,sysprefs + userprefs[:-1]): pf.debug("Reading config file %s" % f) pf.cfg.read(f) pf.refcfg = pf.cfg pf.debug("="*60) pf.debug("RefConfig: %s" % pf.refcfg) # Use the last as place to save preferences pf.prefcfg = Config(default=refLookup) if os.path.exists(pf.preffile): pf.debug("Reading config file %s" % pf.preffile) pf.prefcfg.read(pf.preffile) pf.debug("="*60) pf.debug("Config: %s" % pf.prefcfg) # Fix incompatible changes in configuration apply_config_changes(pf.prefcfg) # Create an empty one for the session settings pf.cfg = Config(default=prefLookup) # This should probably be changed to options overriding config # Set option from config if it was not explicitely given if pf.options.uselib is None: pf.options.uselib = pf.cfg['uselib'] # Set default --nogui if first remaining argument is a pyformex script. if pf.options.gui is None: pf.options.gui = not (len(args) > 0 and utils.is_pyFormex(args[0])) if pf.options.gui: pf.options.interactive = True # Set Revision and run svnclean if we run from an SVN version if pf.svnversion: setRevision() svnclean = os.path.join(pyformexdir,'svnclean') if os.path.exists(svnclean): try: utils.runCommand(svnclean) except: print("Error while executing %s, we ignore it and continue" % svnclean) def getSVNURL(): sta,out = utils.runCommand("cd %s;svn info | grep -F 'URL:'"%pyformexdir) if sta == 0: return out else: return '' ## s = getSVNURL() ## print s ## import re ## m = re.match(".*//(?P[^@]*)@svn\.savanna\.nongnu\.org.*",s) ## pf.svnuser = m.group('user') ## print pf.svnuser # Add in subversion specific config devhowto = os.path.join(pyformexdir,'..','HOWTO-dev.rst') builddoc = os.path.join(pyformexdir,"doc","build-local-docs.rst") pf.refcfg.help['developer'][0:0] = [('Developer HOWTO',devhowto),('&Build local documentation',builddoc)] #print pf.refcfg.help['developer'] ###### We have the config and options all set up ############ filterWarnings() def _format_warning(message,category,filename,lineno,line=None): """Replace the default warnings.formatwarning This allows the warnings being called using a simple mnemonic string. The full message is then found from the message module. """ import messages message = messages.getMessage(message) message = """.. pyFormex Warning ================ %s `Called from:` %s `line:` %s """ % (message,filename,lineno) if line: message += "%s\n" % line return message if pf.cfg['warnings/nice']: import warnings warnings.formatwarning = _format_warning # Make sure pf.PF is a Project from project import Project pf.PF = Project() # Start the GUI if needed # Importing the gui should be done after the config is set !! if pf.options.gui: from gui import guimain pf.debug("GUI version") res = guimain.startGUI(args) if res != 0: print("Could not start the pyFormex GUI: %s" % res) return res # EXIT # Display the startup warnings and messages if startup_warnings: if pf.cfg['startup_warnings']: pf.warning(startup_warnings) else: print(startup_warnings) if startup_messages: print(startup_messages) if pf.options.debug: # Avoid computing the report if not printed pf.debug(utils.reportDetected()) #print(pf.cfg.keys()) #print(pf.refcfg.keys()) # # Qt4 may have changed the locale. # Since a LC_NUMERIC setting other than C may cause lots of troubles # with reading and writing files (formats become incompatible!) # we put it back to a sane setting # utils.setSaneLocale() # Initialize the libraries #print("NOW LOAIDNG LIBS") #import lib #lib.init_libs(pf.options.uselib,pf.options.gui) # Prepend the autorun scripts ar = pf.cfg.get('autorun','') if ar : if type(ar) is str: ar = [ ar ] # expand tilde, as would bash ar = map(utils.tildeExpand,ar) args[0:0] = [ fn for fn in ar if os.path.exists(fn) ] # remaining args are interpreted as scripts and their parameters res = 0 if args: pf.debug("Remaining args: %s" % args) from script import processArgs res = processArgs(args) if res: if pf.options.gui: pf.message("There was an error while executing a script") else: return res # EXIT else: pf.debug("stdin is a tty: %s" % sys.stdin.isatty()) # Play script from stdin # Can we check for interactive session: stdin connected to terminal? #from script import playScript #playScript(sys.stdin) # after processing all args, go into interactive mode if pf.options.gui and pf.app: res = guimain.runGUI() ## elif pf.options.interactive: ## print("Enter your script and end with CTRL-D") ## from script import playScript ## playScript(sys.stdin) #Save the preferences that have changed savePreferences() # Exit return res # End pyformex-0.8.6/pyformex/geomtools.py0000644000211500021150000010722511705104656017501 0ustar benebene00000000000000# $Id: geomtools.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Basic geometrical operations. This module defines some basic operations on simple geometrical entities such as lines, triangles, circles, planes. """ from coords import * class Plane(object): def __init__(self,P,n): self.coords = Coords.concatenate([P,normalize(n)]) def areaNormals(x): """Compute the area and normal vectors of a collection of triangles. x is an (ntri,3,3) array of coordinates. Returns a tuple of areas,normals. The normal vectors are normalized. The area is always positive. """ area,normals = vectorPairAreaNormals(x[:,1]-x[:,0],x[:,2]-x[:,1]) area *= 0.5 return area,normals def polygonArea(x,project=None): """Compute area inside a polygon. Parameters: - `x`: (nplex,3) Coords array representing the vertices of a (possibly nonplanar) polygon. - `project`: (3,) Coords array representing a unit direction vector. Returns: a single float value with the area inside the polygon. If a direction vector is given, the area projected in that direction is returned. Note that if the polygon is nonplanar and no direction is given, the area inside the polygon is not well defined. """ if x.shape[1] < 3: return 0.0 x1 = roll(x,-1,axis=0) if project is None: area = vectorPairArea(x,x1) else: area = vectorTripleProduct(Coords(project),x,x1) return 0.5 *area.sum() def polygonNormals(x): """Compute normals in all points of polygons in x. x is an (nel,nplex,3) coordinate array representing nel (possibly nonplanar) polygons. The return value is an (nel,nplex,3) array with the unit normals on the two edges ending in each point. """ if x.shape[1] < 3: #raise ValueError,"Cannot compute normals for plex-2 elements" n = zeros_like(x) n[:,:,2] = -1. return n ni = arange(x.shape[1]) nj = roll(ni,1) nk = roll(ni,-1) v1 = x-x[:,nj] v2 = x[:,nk]-x return vectorPairNormals(v1.reshape(-1,3),v2.reshape(-1,3)).reshape(x.shape) def triangleInCircle(x): """Compute the incircles of the triangles x The incircle of a triangle is the largest circle that can be inscribed in the triangle. x is a Coords array with shape (ntri,3,3) representing ntri triangles. Returns a tuple r,C,n with the radii, Center and unit normals of the incircles. """ checkArray(x,shape=(-1,3,3)) # Edge vectors v = roll(x,-1,axis=1) - x v = normalize(v) # create bisecting lines in x0 and x1 b0 = v[:,0]-v[:,2] b1 = v[:,1]-v[:,0] # find intersection => center point of incircle center = lineIntersection(x[:,0],b0,x[:,1],b1) # find distance to any side => radius radius = center.distanceFromLine(x[:,0],v[:,0]) # normals normal = cross(v[:,0],v[:,1]) normal /= length(normal).reshape(-1,1) return radius,center,normal def triangleCircumCircle(x,bounding=False): """Compute the circumcircles of the triangles x x is a Coords array with shape (ntri,3,3) representing ntri triangles. Returns a tuple r,C,n with the radii, Center and unit normals of the circles going through the vertices of each triangle. If bounding=True, this returns the triangle bounding circle. """ checkArray(x,shape=(-1,3,3)) # Edge vectors v = x - roll(x,-1,axis=1) vv = dotpr(v,v) # Edge lengths lv = sqrt(vv) n = cross(v[:,0],v[:,1]) nn = dotpr(n,n) # Radius N = sqrt(nn) r = asarray(lv.prod(axis=-1) / N / 2) # Center w = -dotpr(roll(v,1,axis=1),roll(v,2,axis=1)) a = w * vv C = a.reshape(-1,3,1) * roll(x,1,axis=1) C = C.sum(axis=1) / nn.reshape(-1,1) / 2 # Unit normals n = n / N.reshape(-1,1) # Bounding circle if bounding: # Modify for obtuse triangles for i,j,k in [[0,1,2],[1,2,0],[2,0,1]]: obt = vv[:,i] >= vv[:,j]+vv[:,k] r[obt] = 0.5 * lv[obt,i] C[obt] = 0.5 * (x[obt,i] + x[obt,j]) return r,C,n def triangleBoundingCircle(x): """Compute the bounding circles of the triangles x The bounding circle is the smallest circle in the plane of the triangle such that all vertices of the triangle are on or inside the circle. If the triangle is acute, this is equivalent to the triangle's circumcircle. It the triangle is obtuse, the longest edge is the diameter of the bounding circle. x is a Coords array with shape (ntri,3,3) representing ntri triangles. Returns a tuple r,C,n with the radii, Center and unit normals of the bounding circles. """ return triangleCircumCircle(x,bounding=True) def triangleObtuse(x): """Checks for obtuse triangles x is a Coords array with shape (ntri,3,3) representing ntri triangles. Returns an (ntri) array of True/False values indicating whether the triangles are obtuse. """ checkArray(x,shape=(-1,3,3)) # Edge vectors v = x - roll(x,-1,axis=1) vv = dotpr(v,v) return (vv[:,0] > vv[:,1]+vv[:,2]) + (vv[:,1] > vv[:,2]+vv[:,0]) + (vv[:,2] > vv[:,0]+vv[:,1]) def lineIntersection(P1,D1,P2,D2): """Finds the intersection of 2 coplanar lines. The lines (P1,D1) and (P2,D2) are defined by a point and a direction vector. Let a and b be unit vectors along the lines, and c = P2-P1, let ld and d be the length and the unit vector of the cross product a*b, the intersection point X is then given by X = 0.5(P1+P2+sa*a+sb*b) where sa = det([c,b,d])/ld and sb = det([c,a,d])/ld """ P1 = asarray(P1).reshape((-1,3)).astype(float64) D1 = asarray(D1).reshape((-1,3)).astype(float64) P2 = asarray(P2).reshape((-1,3)).astype(float64) D2 = asarray(D2).reshape((-1,3)).astype(float64) N = P1.shape[0] # a,b,c,d la,a = vectorNormalize(D1) lb,b = vectorNormalize(D2) c = (P2-P1) d = cross(a,b) ld,d = vectorNormalize(d) # sa,sb a = a.reshape((-1,1,3)) b = b.reshape((-1,1,3)) c = c.reshape((-1,1,3)) d = d.reshape((-1,1,3)) m1 = concatenate([c,b,d],axis=-2) m2 = concatenate([c,a,d],axis=-2) # This may still be optimized sa = zeros((N,1)) sb = zeros((N,1)) for i in range(P1.shape[0]): sa[i] = linalg.det(m1[i]) / ld[i] sb[i] = linalg.det(m2[i]) / ld[i] # X a = a.reshape((-1,3)) b = b.reshape((-1,3)) X = 0.5 * ( P1 + sa*a + P2 + sb*b ) return Coords(X) def displaceLines(A,N,C,d): """Move all lines (A,N) over a distance a in the direction of point C. A,N are arrays with points and directions defining the lines. C is a point. d is a scalar or a list of scalars. All line elements of F are translated in the plane (line,C) over a distance d in the direction of the point C. Returns a new set of lines (A,N). """ l,v = vectorNormalize(N) w = C - A vw = (v*w).sum(axis=-1).reshape((-1,1)) Y = A + vw*v l,v = vectorNormalize(C-Y) return A + d*v, N def segmentOrientation(vertices,vertices2=None,point=None): """Determine the orientation of a set of line segments. vertices and vertices2 are matching sets of points. point is a single point. All arguments are Coords objects. Line segments run between corresponding points of vertices and vertices2. If vertices2 is None, it is obtained by rolling the vertices one position foreward, thus corresponding to a closed polygon through the vertices). If point is None, it is taken as the center of vertices. The orientation algorithm checks whether the line segments turn positively around the point. Returns an array with +1/-1 for positive/negative oriented segments. """ if vertices2 is None: vertices2 = roll(vertices,-1,axis=0) if point is None: point = vertices.center() w = cross(vertices,vertices2) orient = sign(dotpr(point,w)).astype(Int) return orient def rotationAngle(A,B,m=None,angle_spec=Deg): """Return rotation angles and vectors for rotations of A to B. A and B are (n,3) shaped arrays where each line represents a vector. This function computes the rotation from each vector of A to the corresponding vector of B. If m is None, the return value is a tuple of an (n,) shaped array with rotation angles (by default in degrees) and an (n,3) shaped array with unit vectors along the rotation axis. If m is a (n,3) shaped array with vectors along the rotation axis, the return value is a (n,) shaped array with rotation angles. Specify angle_spec=Rad to get the angles in radians. """ A = asarray(A).reshape(-1,3) B = asarray(B).reshape(-1,3) if m is None: A = normalize(A) B = normalize(B) n = cross(A,B) # vectors perpendicular to A and B t = length(n) == 0. if t.any(): # some vectors A and B are parallel n[t] = anyPerpendicularVector(A[t]) n = normalize(n) c = dotpr(A,B) angle = arccosd(c.clip(min=-1.,max=1.),angle_spec) return angle,n else: m = asarray(m).reshape(-1,3) # project vectors on plane A = projectionVOP(A,m) B = projectionVOP(B,m) angle,n = rotationAngle(A,B,angle_spec=angle_spec) # check sign of the angles m = normalize(m) inv = isClose(dotpr(n,m),[-1.]) angle[inv] *= -1. return angle def anyPerpendicularVector(A): """Return arbitrary vectors perpendicular to vectors of A. A is a (n,3) shaped array of vectors. The return value is a (n,3) shaped array of perpendicular vectors. The returned vector is always a vector in the x,y plane. If the original is the z-axis, the result is the x-axis. """ A = asarray(A) x,y,z = hsplit(A,[1,2]) n = zeros(x.shape,dtype=Float) i = ones(x.shape,dtype=Float) t = (x==0.)*(y==0.) B = where(t,column_stack([i,n,n]),column_stack([-y,x,n])) # B = where(t,column_stack([-z,n,x]),column_stack([-y,x,n])) return B def perpendicularVector(A,B): """Return vectors perpendicular on both A and B.""" return cross(A,B) def projectionVOV(A,B): """Return the projection of vector of A on vector of B.""" L = projection(A,B) B = normalize(B) shape = list(L.shape) shape.append(1) return L.reshape(shape)*B def projectionVOP(A,n): """Return the projection of vector of A on plane of B.""" Aperp = projectionVOV(A,n) return A-Aperp ################## intersection tools ############### # # IT SHOULD BE CLEARLY DOCUMENTED WHETHER NORMALS ARE REQUIRED # TO BE NORMALIZED OR NOT # svc: plane normals and line vectors are not required to be normalized # # MAYBE WE SHOULD ADOPT CONVENTION TO USE m,n FOR NORMALIZED # VECTORS, AND u,v,w for (possibly) unnormalized # def pointsAtLines(q,m,t): """Return the points of lines (q,m) at parameter values t. Parameters: - `q`,`m`: (...,3) shaped arrays of points and vectors, defining a single line or a set of lines. - `t`: array of parameter values, broadcast compatible with `q` and `m`. Returns: An array with the points at parameter values t. """ t = t[...,newaxis] return q+t*m def pointsAtSegments(S,t): """Return the points of line segments S at parameter values t. Parameters: - `S`: (...,2,3) shaped array, defining a single line segment or a set of line segments. - `t`: array of parameter values, broadcast compatible with `S`. Returns: An array with the points at parameter values t. """ q0 = S[...,0,:] q1 = S[...,1,:] return pointsAtLines(q0,q1-q0,t) def intersectionTimesLWL(q1,m1,q2,m2,mode='all'): """Return the intersection of lines (q1,m1) and lines (q2,m2) with the perpendiculars between them. Parameters: - `qi`,`mi` (i=1...2): (nqi,3) shaped arrays of points and vectors (`mode=all`) or broadcast compatible arrays (`mode=pair`), defining a single line or a set of lines. - `mode`: `all` to calculate the intersection of each line (q1,m1) with all lines (q2,m2) or `pair` for pairwise intersections. Returns: A tuple of (nq1,nq2) shaped (`mode=all`) arrays of parameter values t1 and t2, such that the intersection points are given by q1+t1*m1 and q2+t2*m2. """ if mode == 'all': q1 = asarray(q1).reshape(-1,1,3) m1 = asarray(m1).reshape(-1,1,3) q2 = asarray(q2).reshape(1,-1,3) m2 = asarray(m2).reshape(1,-1,3) dot11 = dotpr(m1,m1) dot22 = dotpr(m2,m2) dot12 = dotpr(m1,m2) denom = (dot12**2-dot11*dot22) q12 = q2-q1 dot11 = dot11[...,newaxis] dot22 = dot22[...,newaxis] dot12 = dot12[...,newaxis] t1 = dotpr(q12,m2*dot12-m1*dot22) / denom t2 = dotpr(q12,m2*dot11-m1*dot12) / denom return t1,t2 def intersectionPointsLWL(q1,m1,q2,m2,mode='all'): """Return the intersection points of lines (q1,m1) and lines (q2,m2) with the perpendiculars between them. This is like intersectionTimesLWL but returns a tuple of (nq1,nq2,3) shaped (`mode=all`) arrays of intersection points instead of the parameter values. """ t1,t2 = intersectionTimesLWL(q1,m1,q2,m2,mode) if mode == 'all': q1 = q1[:,newaxis] m1 = m1[:,newaxis] return pointsAtLines(q1,m1,t1),pointsAtLines(q2,m2,t2) def intersectionTimesLWP(q,m,p,n,mode='all'): """Return the intersection of lines (q,m) with planes (p,n). Parameters: - `q`,`m`: (nq,3) shaped arrays of points and vectors (`mode=all`) or broadcast compatible arrays (`mode=pair`), defining a single line or a set of lines. - `p`,`n`: (np,3) shaped arrays of points and normals (`mode=all`) or broadcast compatible arrays (`mode=pair`), defining a single plane or a set of planes. - `mode`: `all` to calculate the intersection of each line (q,m) with all planes (p,n) or `pair` for pairwise intersections. Returns: A (nq,np) shaped (`mode=all`) array of parameter values t, such that the intersection points are given by q+t*m. """ if mode == 'all': return (dotpr(p,n) - inner(q,n)) / inner(m,n) elif mode == 'pair': return (dotpr(p,n) - dotpr(q,n)) / dotpr(m,n) def intersectionPointsLWP(q,m,p,n,mode='all'): """Return the intersection points of lines (q,m) with planes (p,n). This is like intersectionTimesLWP but returns a (nq,np,3) shaped (`mode=all`) array of intersection points instead of the parameter values. """ t = intersectionTimesLWP(q,m,p,n,mode) if mode == 'all': q = q[:,newaxis] m = m[:,newaxis] return pointsAtLines(q,m,t) def intersectionTimesSWP(S,p,n,mode='all'): """Return the intersection of line segments S with planes (p,n). Parameters: - `S`: (nS,2,3) shaped array (`mode=all`) or broadcast compatible array (`mode=pair`), defining a single line segment or a set of line segments. - `p`,`n`: (np,3) shaped arrays of points and normals (`mode=all`) or broadcast compatible arrays (`mode=pair`), defining a single plane or a set of planes. - `mode`: `all` to calculate the intersection of each line segment S with all planes (p,n) or `pair` for pairwise intersections. Returns: A (nS,np) shaped (`mode=all`) array of parameter values t, such that the intersection points are given by `(1-t)*S[...,0,:] + t*S[...,1,:]`. This function is comparable to intersectionTimesLWP, but ensures that parameter values 0<=t<=1 are points inside the line segments. """ q0 = S[...,0,:] q1 = S[...,1,:] return intersectionTimesLWP(q0,q1-q0,p,n,mode) def intersectionPointsSWP(S,p,n,mode='all',return_all=False): """Return the intersection points of line segments S with planes (p,n). Parameters: - `S`: (nS,2,3) shaped array, defining a single line segment or a set of line segments. - `p`,`n`: (np,3) shaped arrays of points and normals, defining a single plane or a set of planes. - `mode`: `all` to calculate the intersection of each line segment S with all planes (p,n) or `pair` for pairwise intersections. - `return_all`: if True, all intersection points of the lines along the segments are returned. Default is to return only the points that lie on the segments. Returns: if `return_all==True`, a (nS,np,3) shaped (`mode=all`) array of intersection points, else, a tuple of intersection points with shape (n,3) and line and plane indices with shape (n), where n <= nS*np. """ S = asanyarray(S).reshape(-1,2,3) p = asanyarray(p).reshape(-1,3) n = asanyarray(n).reshape(-1,3) t = intersectionTimesSWP(S,p,n,mode) if mode == 'all': S = S[:,newaxis] x = pointsAtSegments(S,t) if x.ndim == 1: x = x.reshape(1,3) t = t.reshape(1) if not return_all: # Find points inside segments ok = (t >= 0.0) * (t <= 1.0) if mode == 'all': wl,wt = where(ok) elif mode == 'pair': wl = wt = where(ok)[0] return x[ok],wl,wt return x def intersectionTimesLWT(q,m,F,mode='all'): """Return the intersection of lines (q,m) with triangles F. Parameters: - `q`,`m`: (nq,3) shaped arrays of points and vectors (`mode=all`) or broadcast compatible arrays (`mode=pair`), defining a single line or a set of lines. - `F`: (nF,3,3) shaped array (`mode=all`) or broadcast compatible array (`mode=pair`), defining a single triangle or a set of triangles. - `mode`: `all` to calculate the intersection of each line (q,m) with all triangles F or `pair` for pairwise intersections. Returns: A (nq,nF) shaped (`mode=all`) array of parameter values t, such that the intersection points are given q+tm. """ Fn = cross(F[...,1,:]-F[...,0,:],F[...,2,:]-F[...,1,:]) return intersectionTimesLWP(q,m,F[...,0,:],Fn,mode) def intersectionPointsLWT(q,m,F,mode='all',return_all=False): """Return the intersection points of lines (q,m) with triangles F. Parameters: - `q`,`m`: (nq,3) shaped arrays of points and vectors, defining a single line or a set of lines. - `F`: (nF,3,3) shaped array, defining a single triangle or a set of triangles. - `mode`: `all` to calculate the intersection points of each line (q,m) with all triangles F or `pair` for pairwise intersections. - `return_all`: if True, all intersection points are returned. Default is to return only the points that lie inside the triangles. Returns: if `return_all==True`, a (nq,nF,3) shaped (`mode=all`) array of intersection points, else, a tuple of intersection points with shape (n,3) and line and plane indices with shape (n), where n <= nq*nF. """ q = asanyarray(q).reshape(-1,3) m = asanyarray(m).reshape(-1,3) F = asanyarray(F).reshape(-1,3,3) if not return_all: # Find lines passing through the bounding spheres of the triangles r,c,n = triangleBoundingCircle(F) if mode == 'all': ## d = distancesPFL(c,q,m,mode).transpose() # this is much slower for large arrays mode = 'pair' d = row_stack([ distancesPFL(c,q[i],m[i],mode) for i in range(q.shape[0]) ]) wl,wt = where(d<=r) elif mode == 'pair': d = distancesPFL(c,q,m,mode) wl = wt = where(d<=r)[0] if wl.size == 0: return empty((0,3,),dtype=float),wl,wt q,m,F = q[wl],m[wl],F[wt] t = intersectionTimesLWT(q,m,F,mode) if mode == 'all': q = q[:,newaxis] m = m[:,newaxis] x = pointsAtLines(q,m,t) if not return_all: # Find points inside the faces ok = insideTriangle(F,x[newaxis]).reshape(-1) return x[ok],wl[ok],wt[ok] else: return x def intersectionTimesSWT(S,F,mode='all'): """Return the intersection of lines segments S with triangles F. Parameters: - `S`: (nS,2,3) shaped array (`mode=all`) or broadcast compatible array (`mode=pair`), defining a single line segment or a set of line segments. - `F`: (nF,3,3) shaped array (`mode=all`) or broadcast compatible array (`mode=pair`), defining a single triangle or a set of triangles. - `mode`: `all` to calculate the intersection of each line segment S with all triangles F or `pair` for pairwise intersections. Returns: A (nS,nF) shaped (`mode=all`) array of parameter values t, such that the intersection points are given by `(1-t)*S[...,0,:] + t*S[...,1,:]`. """ Fn = cross(F[...,1,:]-F[...,0,:],F[...,2,:]-F[...,1,:]) return intersectionTimesSWP(S,F[...,0,:],Fn,mode) def intersectionPointsSWT(S,F,mode='all',return_all=False): """Return the intersection points of lines segments S with triangles F. Parameters: - `S`: (nS,2,3) shaped array, defining a single line segment or a set of line segments. - `F`: (nF,3,3) shaped array, defining a single triangle or a set of triangles. - `mode`: `all` to calculate the intersection points of each line segment S with all triangles F or `pair` for pairwise intersections. - `return_all`: if True, all intersection points are returned. Default is to return only the points that lie on the segments and inside the triangles. Returns: if `return_all==True`, a (nS,nF,3) shaped (`mode=all`) array of intersection points, else, a tuple of intersection points with shape (n,3) and line and plane indices with shape (n), where n <= nS*nF. """ S = asanyarray(S).reshape(-1,2,3) F = asanyarray(F).reshape(-1,3,3) if not return_all: # Find lines passing through the bounding spheres of the triangles r,c,n = triangleBoundingCircle(F) if mode == 'all': ## d = distancesPFS(c,S,mode).transpose() # this is much slower for large arrays mode = 'pair' d = row_stack([ distancesPFS(c,S[i],mode) for i in range(S.shape[0]) ]) wl,wt = where(d<=r) elif mode == 'pair': d = distancesPFS(c,S,mode) wl = wt = where(d<=r)[0] if wl.size == 0: return empty((0,3,),dtype=float),wl,wt S,F = S[wl],F[wt] t = intersectionTimesSWT(S,F,mode) if mode == 'all': S = S[:,newaxis] x = pointsAtSegments(S,t) if not return_all: # Find points inside the segments and faces ok = (t >= 0.0) * (t <= 1.0) * insideTriangle(F,x[newaxis]).reshape(-1) return x[ok],wl[ok],wt[ok] else: return x def intersectionPointsPWP(p1,n1,p2,n2,p3,n3,mode='all'): """Return the intersection points of planes (p1,n1), (p2,n2) and (p3,n3). Parameters: - `pi`,`ni` (i=1...3): (npi,3) shaped arrays of points and normals (`mode=all`) or broadcast compatible arrays (`mode=pair`), defining a single plane or a set of planes. - `mode`: `all` to calculate the intersection of each plane (p1,n1) with all planes (p2,n2) and (p3,n3) or `pair` for pairwise intersections. Returns: A (np1,np2,np3,3) shaped (`mode=all`) array of intersection points. """ if mode == 'all': p1 = asanyarray(p1).reshape(-1,1,1,3) n1 = asanyarray(n1).reshape(-1,1,1,3) p2 = asanyarray(p2).reshape(1,-1,1,3) n2 = asanyarray(n2).reshape(1,-1,1,3) p3 = asanyarray(p3).reshape(1,1,-1,3) n3 = asanyarray(n3).reshape(1,1,-1,3) dot1 = dotpr(p1,n1)[...,newaxis] dot2 = dotpr(p2,n2)[...,newaxis] dot3 = dotpr(p3,n3)[...,newaxis] cross23 = cross(n2,n3) cross31 = cross(n3,n1) cross12 = cross(n1,n2) denom = dotpr(n1,cross23)[...,newaxis] return (dot1*cross23+dot2*cross31+dot3*cross12)/denom def intersectionLinesPWP(p1,n1,p2,n2,mode='all'): """Return the intersection lines of planes (p1,n1) and (p2,n2). Parameters: - `pi`,`ni` (i=1...2): (npi,3) shaped arrays of points and normals (`mode=all`) or broadcast compatible arrays (`mode=pair`), defining a single plane or a set of planes. - `mode`: `all` to calculate the intersection of each plane (p1,n1) with all planes (p2,n2) or `pair` for pairwise intersections. Returns: A tuple of (np1,np2,3) shaped (`mode=all`) arrays of intersection points q and vectors m, such that the intersection lines are given by q+t*m. """ if mode == 'all': p1 = asanyarray(p1).reshape(-1,1,3) n1 = asanyarray(n1).reshape(-1,1,3) p2 = asanyarray(p2).reshape(1,-1,3) n2 = asanyarray(n2).reshape(1,-1,3) m = cross(n1,n2) q = intersectionPointsPWP(p1,n1,p2,n2,p1,m,mode='pair') return q,m def intersectionTimesPOP(X,p,n,mode='all'): """Return the intersection of perpendiculars from points X on planes (p,n). Parameters: - `X`: a (nX,3) shaped array of points (`mode=all`) or broadcast compatible array (`mode=pair`). - `p`,`n`: (np,3) shaped arrays of points and normals (`mode=all`) or broadcast compatible arrays (`mode=pair`), defining a single plane or a set of planes. - `mode`: `all` to calculate the intersection for each point X with all planes (p,n) or `pair` for pairwise intersections. Returns: A (nX,np) shaped (`mode=all`) array of parameter values t, such that the intersection points are given by X+t*n. """ if mode == 'all': return (dotpr(p,n) - inner(X,n)) / dotpr(n,n) elif mode == 'pair': return (dotpr(p,n) - dotpr(X,n)) / dotpr(n,n) def intersectionPointsPOP(X,p,n,mode='all'): """Return the intersection points of perpendiculars from points X on planes (p,n). This is like intersectionTimesPOP but returns a (nX,np,3) shaped (`mode=all`) array of intersection points instead of the parameter values. """ t = intersectionTimesPOP(X,p,n,mode) if mode == 'all': X = X[:,newaxis] return pointsAtLines(X,n,t) def intersectionTimesPOL(X,q,m,mode='all'): """Return the intersection of perpendiculars from points X on lines (q,m). Parameters: - `X`: a (nX,3) shaped array of points (`mode=all`) or broadcast compatible array (`mode=pair`). - `q`,`m`: (nq,3) shaped arrays of points and vectors (`mode=all`) or broadcast compatible arrays (`mode=pair`), defining a single line or a set of lines. - `mode`: `all` to calculate the intersection for each point X with all lines (q,m) or `pair` for pairwise intersections. Returns: A (nX,nq) shaped (`mode=all`) array of parameter values t, such that the intersection points are given by q+t*m. """ if mode == 'all': return (inner(X,m) - dotpr(q,m)) / dotpr(m,m) elif mode == 'pair': return (dotpr(X,m) - dotpr(q,m)) / dotpr(m,m) def intersectionPointsPOL(X,q,m,mode='all'): """Return the intersection points of perpendiculars from points X on lines (q,m). This is like intersectionTimesPOL but returns a (nX,nq,3) shaped (`mode=all`) array of intersection points instead of the parameter values. """ t = intersectionTimesPOL(X,q,m,mode) if mode == 'all': q = q[:,newaxis] return pointsAtLines(q,m,t) #################### distance tools ############### def distancesPFL(X,q,m,mode='all'): """Return the distances of points X from lines (q,m). Parameters: - `X`: a (nX,3) shaped array of points (`mode=all`) or broadcast compatible array (`mode=pair`). - `q`,`m`: (nq,3) shaped arrays of points and vectors (`mode=all`) or broadcast compatible arrays (`mode=pair`), defining a single line or a set of lines. - `mode`: `all` to calculate the distance of each point X from all lines (q,m) or `pair` for pairwise distances. Returns: A (nX,nq) shaped (`mode=all`) array of distances. """ if mode == 'all': X = asanyarray(X).reshape(-1,1,3) q = asanyarray(q).reshape(1,-1,3) m = asanyarray(m).reshape(1,-1,3) C = length(X-q) A = abs(dotpr(X,m)-dotpr(q,m))/length(m) d = sqrt(abs(C**2-A**2)) return d def distancesPFS(X,S,mode='all'): """Return the distances of points X from line segments S. Parameters: - `X`: a (nX,3) shaped array of points (`mode=all`) or broadcast compatible array (`mode=pair`). - `S`: (nS,2,3) shaped array of line segments (`mode=all`) or broadcast compatible array (`mode=pair`), defining a single line segment or a set of line segments. - `mode`: `all` to calculate the distance of each point X from all line segments S or `pair` for pairwise distances. Returns: A (nX,nS) shaped (`mode=all`) array of distances. """ q0 = S[...,0,:] q1 = S[...,1,:] return distancesPFL(X,q0,q1-q0,mode) def insideTriangle(x,P,method='bary'): """Checks whether the points P are inside triangles x. x is a Coords array with shape (ntri,3,3) representing ntri triangles. P is a Coords array with shape (npts,ntri,3) representing npts points in each of the ntri planes of the triangles. This function checks whether the points of P fall inside the corresponding triangles. Returns an array with (npts,ntri) bool values. """ if method == 'bary': return insideSimplex(baryCoords(x,P)) else: # Older, slower algorithm xP = x[newaxis,...] - P[:,:,newaxis,:] xx = [ cross(xP[:,:,i],xP[:,:,j]) for (i,j) in ((0,1),(1,2),(2,0)) ] xy = (xx[0]*xx[1]).sum(axis=-1) yz = (xx[1]*xx[2]).sum(axis=-1) d = dstack([xy,yz]) return (d > 0).all(axis=-1) def faceDistance(X,Fp,return_points=False): """Compute the closest perpendicular distance to a set of triangles. X is a (nX,3) shaped array of points. Fp is a (nF,3,3) shaped array of triangles. Note that some points may not have a normal with footpoint inside any of the facets. The return value is a tuple OKpid,OKdist,OKpoints where: - OKpid is an array with the point numbers having a normal distance; - OKdist is an array with the shortest distances for these points; - OKpoints is an array with the closest footpoints for these points and is only returned if return_points = True. """ if not Fp.shape[1] == 3: raise ValueError, "Currently this function only works for triangular faces." # Compute normals on the faces Fn = cross(Fp[:,1]-Fp[:,0],Fp[:,2]-Fp[:,1]) # Compute intersection points of perpendiculars from X on facets F Y = intersectionPointsPOP(X,Fp[:,0,:],Fn) # Find intersection points Y inside the facets inside = insideTriangle(Fp,Y) pid = where(inside)[0] if pid.size == 0: if return_points: return [],[],[] else: return [],[] # Compute the distances X = X[pid] Y = Y[inside] dist = length(X-Y) # Get the shortest distances OKpid,OKpos = groupArgmin(dist,pid) OKdist = dist[OKpos] if return_points: # Get the closest footpoints matching OKpid OKpoints = Y[OKpos] return OKpid,OKdist,OKpoints return OKpid,OKdist def edgeDistance(X,Ep,return_points=False): """Compute the closest perpendicular distance of points X to a set of edges. X is a (nX,3) shaped array of points. Ep is a (nE,2,3) shaped array of edge vertices. Note that some points may not have a normal with footpoint inside any of the edges. The return value is a tuple OKpid,OKdist,OKpoints where: - OKpid is an array with the point numbers having a normal distance; - OKdist is an array with the shortest distances for these points; - OKpoints is an array with the closest footpoints for these points and is only returned if return_points = True. """ # Compute vectors along the edges En = Ep[:,1] - Ep[:,0] # Compute intersection points of perpendiculars from X on edges E t = intersectionTimesPOL(X,Ep[:,0],En) Y = Ep[:,0] + t[:,:,newaxis] * En # Find intersection points Y inside the edges inside = (t >= 0.) * (t <= 1.) pid = where(inside)[0] if pid.size == 0: if return_points: return [],[],[] else: return [],[] # Compute the distances X = X[pid] Y = Y[inside] dist = length(X-Y) # Get the shortest distances OKpid,OKpos = groupArgmin(dist,pid) OKdist = dist[OKpos] if return_points: # Get the closest footpoints matching OKpid OKpoints = Y[OKpos] return OKpid,OKdist,OKpoints return OKpid,OKdist def vertexDistance(X,Vp,return_points=False): """Compute the closest distance of points X to a set of vertices. X is a (nX,3) shaped array of points. Vp is a (nV,3) shaped array of vertices. The return value is a tuple OKdist,OKpoints where: - OKdist is an array with the shortest distances for the points; - OKpoints is an array with the closest vertices for the points and is only returned if return_points = True. """ # Compute the distances dist = length(X[:,newaxis]-Vp) # Get the shortest distances OKdist = dist.min(-1) if return_points: # Get the closest points matching X minid = dist.argmin(-1) OKpoints = Vp[minid] return OKdist,OKpoints return OKdist, #################### barycentric coordinates ############### def baryCoords(S,P): """Compute the barycentric coordinates of points P wrt. simplexes S. S is a (nel,nplex,3) shaped array of n-simplexes (n=nplex-1): - 1-simplex: line segment - 2-simplex: triangle - 3-simplex: tetrahedron P is a (npts,3), (npts,nel,3) or (npts,1,3) shaped array of points. The return value is a (nplex,npts,nel) shaped array of barycentric coordinates. """ if S.ndim != 3: raise ValueError,"S should be a 3-dim array, got shape %s" % str(S.shape) if P.ndim == 2: P = P.reshape(-1,1,3) elif P.shape[1] != S.shape[0] and P.shape[1] != 1: raise ValueError,"Second dimension of P should be first dimension of S or 1." S = S.transpose(1,0,2) # (nplex,nel,3) vp = P - S[0] vs = S[1:] - S[:1] A = dotpr(vs[:,newaxis],vs[newaxis]) # (nplex-1,nplex-1,nel) b = dotpr(vp[newaxis],vs[:,newaxis]) # (nplex-1,npts,nel) #import timer #T = timer.Timer() t = solveMany(A,b) #print "DIRECT SOLVER: %s" % T.seconds() #T.reset() #tt = solveMany(A,b,False) #print "GENERAL SOLVER: %s" % T.seconds() #print "RESULTS MATCH: %s" % (tt-t).sum() t0 = (1.-t.sum(0)) t0 = addAxis(t0,0) t = row_stack([t0,t]) return t def insideSimplex(BC,bound=True): """Check if points are in simplexes. BC is an array of barycentric coordinates (along the first axis), which sum up to one. If bound = True, a point lying on the boundary is considered to be inside the simplex. """ if bound: return (BC >= 0.).all(0) else: return (BC > 0.).all(0) # End pyformex-0.8.6/pyformex/mesh.py0000644000211500021150000021027211705104656016422 0ustar benebene00000000000000# $Id: mesh.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Finite element meshes in pyFormex. This module defines the Mesh class, which can be used to describe discrete geometrical models like those used in Finite Element models. It also contains some useful functions to create such models. """ from formex import * from connectivity import Connectivity from elements import elementType,elementName from utils import deprecation from geometry import Geometry from simple import regularGrid ############################################################## class Mesh(Geometry): """A Mesh is a discrete geometrical model defined by nodes and elements. In the Mesh geometrical data model, the coordinates of all the points are gathered in a single twodimensional array with shape (ncoords,3). The individual geometrical elements are then described by indices into the coordinates array. This model has some advantages over the Formex data model (which stores all the points of all the elements by their coordinates): - a more compact storage, because coordinates of coinciding points are not repeated, - faster connectivity related algorithms. The downside is that geometry generating algorithms are far more complex and possibly slower. In pyFormex we therefore mostly use the Formex data model when creating geometry, but when we come to the point of exporting the geometry to file (and to other programs), a Mesh data model may be more adequate. The Mesh data model has at least the following attributes: - coords: (ncoords,3) shaped Coords object, holding the coordinates of all points in the Mesh; - elems: (nelems,nplex) shaped Connectivity object, defining the elements by indices into the Coords array. All values in elems should be in the range 0 <= value < ncoords. - prop: an array of element property numbers, default None. - eltype: an Element subclass or a string designing the element type, default None. If eltype is None, the eltype of the elems Connectivity table is used, and if that is missing, a default eltype is derived from the plexitude, by a call to the elements.elementType function. In most cases the eltype can be set automatically. The user can override the default value, but an error will occur if the element type does not exist or does not match the plexitude. A Mesh can be initialized by its attributes (coords,elems,prop,eltype) or by a single geometric object that provides a toMesh() method. """ ################################################################### ## DEVELOPERS: ATTENTION ## ## Because the TriSurface is derived from Mesh, all methods which ## return a Mesh and will also work correctly on a TriSurface, ## should use self.__class__ to return the proper class, and they ## should specify the prop and eltype arguments using keywords ## (because only the first two arguments match). ## See the copy() method for an example. ################################################################### def _formex_transform(func): """Perform a Formex transformation on the .coords attribute of the object. This is a decorator function. It should be used only for Formex methods which are not Geometry methods as well. """ formex_func = getattr(Formex,func.__name__) def newf(self,*args,**kargs): """Performs the Formex %s transformation on the coords attribute""" F = Formex(self.coords).formex_func(self.coords,*args,**kargs) return self._set_coords(coords_func(self.coords,*args,**kargs)) newf.__name__ = func.__name__ newf.__doc__ = coords_func.__doc__ return newf def __init__(self,coords=None,elems=None,prop=None,eltype=None): """Initialize a new Mesh.""" self.coords = self.elems = self.prop = self.eltype = None self.ndim = -1 self.nodes = self.edges = self.faces = self.cells = None self.elem_edges = self.eadj = None self.conn = self.econn = self.fconn = None if coords is None: # Create an empty Mesh object return if elems is None: try: # initialize from a single object coords,elems = coords.toMesh() except: raise ValueError,"No `elems` specified and the first argument can not be converted to a Mesh." try: self.coords = Coords(coords) if self.coords.ndim != 2: raise ValueError,"\nExpected 2D coordinate array, got %s" % self.coords.ndim self.elems = Connectivity(elems) if self.elems.size > 0 and ( self.elems.max() >= self.coords.shape[0] or self.elems.min() < 0): raise ValueError,"\nInvalid connectivity data: some node number(s) not in coords array (min=%s, max=%s, ncoords=%s)" % (self.elems.min(),self.elems.max(),self.coords.shape[0]) except: raise self.setType(eltype) self.setProp(prop) def _set_coords(self,coords): """Replace the current coords with new ones. Returns a Mesh or subclass exactly like the current except for the position of the coordinates. """ if isinstance(coords,Coords) and coords.shape == self.coords.shape: return self.__class__(coords,self.elems,prop=self.prop,eltype=self.eltype) else: raise ValueError,"Invalid reinitialization of %s coords" % self.__class__ def setType(self,eltype=None): """Set the eltype from a character string. This function allows the user to change the element type of the Mesh. The input is a character string with the name of one of the element defined in elements.py. The function will only allow to set a type matching the plexitude of the Mesh. This method is seldom needed, because the applications should normally set the element type at creation time. """ # For compatibility reasons, the eltype is set as an attribute # of both the Mesh and the Mesh.elems attribute if eltype is None and hasattr(self.elems,'eltype'): eltype = self.elems.eltype self.eltype = self.elems.eltype = elementType(eltype,self.nplex()) return self def setProp(self,prop=None): """Create or destroy the property array for the Mesh. A property array is a rank-1 integer array with dimension equal to the number of elements in the Mesh. You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored. If a value None is given, the properties are removed from the Mesh. """ if prop is None: self.prop = None else: prop = array(prop).astype(Int) self.prop = resize(prop,(self.nelems(),)) return self def __getitem__(self,i): """Return element i of the Mesh. This allows addressing element i of Mesh M as M[i]. The return value is an array with the coordinates of all the points of the element. M[i][j] then will return the coordinates of node j of element i. This also allows to change the individual coordinates or nodes, by an assignment like M[i][j] = [1.,0.,0.]. """ return self.coords[self.elems[i]] def __setitem__(self,i,val): """Change element i of the Mesh. This allows changing all the coordinates of an element by direct assignment such as M[i] = [[1.,0.,0.], ...]. The user should make sure that the data match the plexitude of the element. """ self.coords[i] = val def __setstate__(self,state): """Set the object from serialized state. This allows to read back old pyFormex Project files where the Mesh class did not set element type yet. """ self.__dict__.update(state) self.setType(self.eltype) def getProp(self): """Return the properties as a numpy array (ndarray)""" return self.prop def maxProp(self): """Return the highest property value used, or None""" if self.prop is None: return None else: return self.prop.max() def propSet(self): """Return a list with unique property values.""" if self.prop is None: return None else: return unique(self.prop) def copy(self): """Return a copy using the same data arrays""" # SHOULD THIS RETURN A DEEP COPY? return self.__class__(self.coords,self.elems,prop=self.prop,eltype=self.eltype) def toFormex(self): """Convert a Mesh to a Formex. The Formex inherits the element property numbers and eltype from the Mesh. Node property numbers however can not be translated to the Formex data model. """ return Formex(self.coords[self.elems],self.prop,self.eltype.name()) def toSurface(self): """Convert a Mesh to a Surface. If the plexitude of the mesh is 3, returns a TriSurface equivalent with the Mesh. Else, an error is raised. """ from plugins.trisurface import TriSurface if self.nplex() == 3: return TriSurface(self) else: raise ValueError,"Only plexitude-3 Meshes can be converted to TriSurface. Got plexitude %s" % self.nplex() def ndim(self): return 3 def ngrade(self): return self.eltype.ndim def nelems(self): return self.elems.shape[0] def nplex(self): return self.elems.shape[1] def ncoords(self): return self.coords.shape[0] nnodes = ncoords npoints = ncoords def shape(self): return self.elems.shape def info(self): return "coords" + str(self.coords.shape) + "; elems" + str(self.elems.shape) def nedges(self): """Return the number of edges. This returns the number of rows that would be in getEdges(), without actually constructing the edges. The edges are not fused! """ try: return self.nelems() * self.eltype.nedges() except: return 0 def centroids(self): """Return the centroids of all elements of the Mesh. The centroid of an element is the point whose coordinates are the mean values of all points of the element. The return value is a Coords object with nelems points. """ return self.coords[self.elems].mean(axis=1) def getCoords(self): """Get the coords data. Returns the full array of coordinates stored in the Mesh object. Note that this may contain points that are not used in the mesh. :meth:`compact` will remove the unused points. """ return self.coords def getElems(self): """Get the elems data. Returns the element connectivity data as stored in the object. """ return self.elems @deprecation("Mesh.getLowerEntitiesSelector is deprecated. Use Element.getEntities instead.") def getLowerEntitiesSelector(self,level=-1): """Get the entities of a lower dimensionality. """ return self.eltype.getEntities(level) def getLowerEntities(self,level=-1,unique=False): """Get the entities of a lower dimensionality. If the element type is defined in the :mod:`elements` module, this returns a Connectivity table with the entities of a lower dimensionality. The full list of entities with increasing dimensionality 0,1,2,3 is:: ['points', 'edges', 'faces', 'cells' ] If level is negative, the dimensionality returned is relative to that of the caller. If it is positive, it is taken absolute. Thus, for a Mesh with a 3D element type, getLowerEntities(-1) returns the faces, while for a 2D element type, it returns the edges. For both meshes however, getLowerEntities(+1) returns the edges. By default, all entities for all elements are returned and common entities will appear multiple times. Specifying unique=True will return only the unique ones. The return value may be an empty table, if the element type does not have the requested entities (e.g. the 'point' type). If the eltype is not defined, or the requested entity level is outside the range 0..3, the return value is None. """ sel = self.eltype.getEntities(level) ent = self.elems.selectNodes(sel) ent.eltype = sel.eltype if unique: ent = ent.removeDuplicate() return ent def getNodes(self): """Return the set of unique node numbers in the Mesh. This returns only the node numbers that are effectively used in the connectivity table. For a compacted Mesh, it is equal to ```arange(self.nelems)```. This function also stores the result internally so that future requests can return it without the need for computing it again. """ if self.nodes is None: self.nodes = unique(self.elems) return self.nodes def getPoints(self): """Return the nodal coordinates of the Mesh. This returns only those points that are effectively used in the connectivity table. For a compacted Mesh, it is equal to the coords attribute. """ return self.coords[self.getNodes()] def getEdges(self): """Return the unique edges of all the elements in the Mesh. This is a convenient function to create a table with the element edges. It is equivalent to ```self.getLowerEntities(1,unique=True)```, but this also stores the result internally so that future requests can return it without the need for computing it again. """ if self.edges is None: self.edges = self.getLowerEntities(1,unique=True) return self.edges def getFaces(self): """Return the unique faces of all the elements in the Mesh. This is a convenient function to create a table with the element faces. It is equivalent to ```self.getLowerEntities(2,unique=True)```, but this also stores the result internally so that future requests can return it without the need for computing it again. """ if self.faces is None: self.faces = self.getLowerEntities(2,unique=True) return self.faces def getCells(self): """Return the cells of the elements. This is a convenient function to create a table with the element cells. It is equivalent to ```self.getLowerEntities(3,unique=True)```, but this also stores the result internally so that future requests can return it without the need for computing it again. """ if self.cells is None: self.cells = self.getLowerEntities(3,unique=True) return self.cells def getElemEdges(self): """Defines the elements in function of its edges. This returns a Connectivity table with the elements defined in function of the edges. It is equivalent to ```self.elems.insertLevel(self.eltype.getEntities(1))``` but it also stores the definition of the edges and the returned element to edge connectivity. """ if self.elem_edges is None: sel = self.eltype.getEntities(1) self.elem_edges,self.edges = self.elems.insertLevel(sel) return self.elem_edges def getFreeEntities(self,level=-1,return_indices=False): """Return the border of the Mesh. Returns a Connectivity table with the free entities of the specified level of the Mesh. Free entities are entities that are only connected with a single element. If return_indices==True, also returns an (nentities,2) index for inverse lookup of the higher entity (column 0) and its local lower entity number (column 1). """ sel = self.eltype.getEntities(level) if sel.size == 0: if return_indices: return Connectivity(),[] else: return Connectivity() hi,lo = self.elems.insertLevel(sel) hiinv = hi.inverse() ncon = (hiinv>=0).sum(axis=1) isbrd = (ncon<=1) # < 1 should not occur brd = lo[isbrd] # # WE SET THE eltype HERE, BECAUSE THE INDEX OPERATION ABOVE # LOOSES THE eltype # SHOULD BE FIXED, BUT NEEDS TO BE CHECKED !!! BV # brd.eltype = sel.eltype if not return_indices: return brd # return indices where the border elements come from binv = hiinv[isbrd] enr = binv[binv >= 0] # element number a = hi[enr] b = arange(lo.shape[0])[isbrd].reshape(-1,1) fnr = where(a==b)[1] # local border part number return brd,column_stack([enr,fnr]) def getFreeEntitiesMesh(self,level=-1,compact=True): """Return a Mesh with lower entities. Returns a Mesh representing the lower entities of the specified level. If the Mesh has property numbers, the lower entities inherit the property of the element to which they belong. By default, the resulting Mesh is compacted. Compaction can be switched off by setting `compact=False`. """ if self.prop==None: M = Mesh(self.coords,self.getFreeEntities(level=level)) else: brd,indices = self.getFreeEntities(return_indices=True,level=level) enr = indices[:,0] M = Mesh(self.coords,brd,prop=self.prop[enr]) # THIS SEEMS SUPERFLUOUS M.setType(brd.eltype) if compact: M = M.compact() return M def getBorder(self,return_indices=False): """Return the border of the Mesh. This returns a Connectivity table with the border of the Mesh. The border entities are of a lower hierarchical level than the mesh itself. These entities become part of the border if they are connected to only one element. If return_indices==True, it returns also an (nborder,2) index for inverse lookup of the higher entity (column 0) and its local border part number (column 1). This is a convenient shorthand for :: self.getFreeEntities(level=-1,return_indices=return_indices) """ return self.getFreeEntities(level=-1,return_indices=return_indices) def getBorderMesh(self,compact=True): """Return a Mesh with the border elements. The returned Mesh is of the next lower hierarchical level and contains all the free entitites of that level. If the Mesh has property numbers, the border elements inherit the property of the element to which they belong. By default, the resulting Mesh is compacted. Compaction can be switched off by setting `compact=False`. This is a convenient shorthand for :: self.getFreeEntitiesMesh(level=-1,compact=compact) """ return self.getFreeEntitiesMesh(level=-1,compact=compact) def getFreeEdgesMesh(self,compact=True): """Return a Mesh with the free edge elements. The returned Mesh is of the hierarchical level 1 (no mather what the level of the parent Mesh is) and contains all the free entitites of that level. If the Mesh has property numbers, the border elements inherit the property of the element to which they belong. By default, the resulting Mesh is compacted. Compaction can be switched off by setting `compact=False`. This is a convenient shorthand for :: self.getFreeEntitiesMesh(level=1,compact=compact) """ return self.getFreeEntitiesMesh(level=1,compact=compact) def reverse(self): """Return a Mesh where all elements have been reversed. Reversing an element has the following meaning: - for 1D elements: reverse the traversal direction, - for 2D elements: reverse the direction of the positive normal, - for 3D elements: reverse inside and outside directions of the element's border surface The :meth:`reflect` method by default calls this method to undo the element reversal caused by the reflection operation. """ utils.warn('warn_mesh_reverse') if hasattr(self.eltype,'reversed'): elems = self.elems[:,self.eltype.reversed] else: elems = self.elems[:,::-1] return self.__class__(self.coords,elems,prop=self.prop,eltype=self.eltype) def reflect(self,dir=0,pos=0.0,reverse=True,**kargs): """Reflect the coordinates in one of the coordinate directions. Parameters: - `dir`: int: direction of the reflection (default 0) - `pos`: float: offset of the mirror plane from origin (default 0.0) - `reverse`: boolean: if True, the :meth:`Mesh.reverse` method is called after the reflection to undo the element reversal caused by the reflection of its coordinates. This will in most cases have the desired effect. If not however, the user can set this to False to skip the element reversal. """ if 'autofix' in kargs: utils.warn("The `autofix` parameter of Mesh.reflect has been renamed to `reverse`.") reverse = kargs['autofix'] if reverse is None: reverse = True utils.warn("warn_mesh_reflect") M = Geometry.reflect(self,dir=dir,pos=pos) if reverse: M = M.reverse() return M ############################################################################# # Adjacency # def nodeConnections(self): """Find and store the elems connected to nodes.""" if self.conn is None: self.conn = self.elems.inverse() return self.conn def nNodeConnected(self): """Find the number of elems connected to nodes.""" return (self.nodeConnections() >=0).sum(axis=-1) def edgeConnections(self): """Find and store the elems connected to edges.""" if self.econn is None: self.econn = self.getElemEdges().inverse() return self.econn def nEdgeConnected(self): """Find the number of elems connected to edges.""" return (self.edgeConnections() >=0).sum(axis=-1) def nodeAdjacency(self): """Find the elems adjacent to each elem via one or more nodes.""" return self.elems.adjacency() def nNodeAdjacent(self): """Find the number of elems which are adjacent by node to each elem.""" return (self.nodeAdjacency() >=0).sum(axis=-1) def edgeAdjacency(self): """Find the elems adjacent to elems via an edge.""" return self.getElemEdges().adjacency() def nEdgeAdjacent(self): """Find the number of adjacent elems.""" return (self.edgeAdjacency() >=0).sum(axis=-1) # BV: REMOVED node2nodeAdjacency: # # Either use # - self.elems.nodeAdjacency() (gives nodes connected by elements) # - self.getEdges().nodeAdjacency() (gives nodes connected by edges) # BV: name is way too complex # should not be a mesh method, but of some MeshValue class? Field? def avgNodalScalarOnAdjacentNodes(self, val, iter=1): """_Smooth nodal scalar values by averaging over adjacent nodes iter times. Nodal scalar values (val is a 1D array of self.ncoords() scalar values ) are averaged over adjacent nodes an number of time (iter) in order to provide a smoothed mapping. """ if iter==0: return val nadjn=self.getEdges().adjacency(kind='n') nadjn=[x[x>=0] for x in nadjn] lnadjn=[len(i) for i in nadjn] lmax= max( lnadjn ) adjmatrix=zeros([self.ncoords(), lmax], float) avgval=val for i in range(iter): for i in range( self.ncoords() ): adjmatrix[i, :len( nadjn[i]) ]=avgval[ nadjn[i] ] avgval= sum(adjmatrix, axis=1)/lnadjn return avgval def report(self): """Create a report on the Mesh shape and size. The report contains the number of nodes, number of elements, plexitude, bbox and size. """ bb = self.bbox() return """ Shape: %s nodes, %s elems, plexitude %s BBox: %s, %s Size: %s """ % (self.ncoords(),self.nelems(),self.nplex(),bb[1],bb[0],bb[1]-bb[0]) def __str__(self): """Format a Mesh in a string. This creates a detailed string representation of a Mesh, containing the report() and the lists of nodes and elements. """ return self.report() + "Coords:\n" + self.coords.__str__() + "\nElems:\n" + self.elems.__str__() def fuse(self,**kargs): """Fuse the nodes of a Meshes. All nodes that are within the tolerance limits of each other are merged into a single node. The merging operation can be tuned by specifying extra arguments that will be passed to :meth:`Coords:fuse`. """ coords,index = self.coords.fuse(**kargs) return self.__class__(coords,index[self.elems],prop=self.prop,eltype=self.eltype) def matchCoords(self,mesh,**kargs): """Match nodes of Mesh with nodes of self. This is a convenience function equivalent to:: self.coords.match(mesh.coords,**kargs) See also :meth:`Coords.match` """ return self.coords.match(mesh.coords,**kargs) def matchCentroids(self, mesh,**kargs): """Match elems of Mesh with elems of self. self and Mesh are same eltype meshes and are both without duplicates. Elems are matched by their centroids. """ c = Mesh(self.centroids(), arange(self.nelems() )) mc = Mesh(mesh.centroids(), arange(mesh.nelems() )) return c.matchCoords(mc,**kargs) # BV: I'm not sure that we need this. Looks like it can or should # be replaced with a method applied on the BorderMesh #~ FI It has been tested on quad4-quad4, hex8-quad4, tet4-tri3 def matchFaces(self,mesh): """Match faces of mesh with faces of self. self and Mesh can be same eltype meshes or different eltype but of the same hierarchical type (i.e. hex8-quad4 or tet4 - tri3) and are both without duplicates. Returns the indices array of the elems of self that matches the faces of mesh """ sel = self.eltype.getEntities(2) hi,lo = self.elems.insertLevel(sel) hiinv = hi.inverse() fm=Mesh(self.coords,self.getFaces()) mesh=Mesh(mesh.coords,mesh.getFaces()) c=fm.matchCentroids(mesh) hiinv = hiinv[c] enr = unique(hiinv[hiinv >= 0]) # element number return enr def compact(self): """Remove unconnected nodes and renumber the mesh. Returns a mesh where all nodes that are not used in any element have been removed, and the nodes are renumbered to a compacter scheme. """ nodes = unique(self.elems) if nodes.size == 0: return self.__class__([],[]) elif nodes.shape[0] < self.ncoords() or nodes[-1] >= nodes.size: coords = self.coords[nodes] if nodes[-1] >= nodes.size: elems = inverseUniqueIndex(nodes)[self.elems] else: elems = self.elems return self.__class__(coords,elems,prop=self.prop,eltype=self.eltype) else: return self def select(self,selected,compact=True): """Return a Mesh only holding the selected elements. - `selected`: an object that can be used as an index in the `elems` array, e.g. a list of (integer) element numbers, or a boolean array with the same length as the `elems` array. - `compact`: boolean. If True (default), the returned Mesh will be compacted, i.e. the unused nodes are removed and the nodes are renumbered from zero. If False, returns the node set and numbers unchanged. Returns a Mesh (or subclass) with only the selected elements. See `cselect` for the complementary operation. """ M = self.__class__(self.coords,self.elems[selected],eltype=self.eltype) if self.prop is not None: M.setProp(self.prop[selected]) if compact: M = M.compact() return M def cselect(self,selected,compact=True): """Return a mesh without the selected elements. - `selected`: an object that can be used as an index in the `elems` array, e.g. a list of (integer) element numbers, or a boolean array with the same length as the `elems` array. - `compact`: boolean. If True (default), the returned Mesh will be compacted, i.e. the unused nodes are removed and the nodes are renumbered from zero. If False, returns the node set and numbers unchanged. Returns a Mesh with all but the selected elements. This is the complimentary operation of `select`. """ selected = asarray(selected) if selected.dtype==bool: return self.select(selected==False,compact=compact) else: wi = range(self.nelems()) wi = delete(wi,selected) return self.select(wi,compact=compact) def avgNodes(self,nodsel,wts=None): """Create average nodes from the existing nodes of a mesh. `nodsel` is a local node selector as in :meth:`selectNodes` Returns the (weighted) average coordinates of the points in the selector as `(nelems*nnod,3)` array of coordinates, where nnod is the length of the node selector. `wts` is a 1-D array of weights to be attributed to the points. Its length should be equal to that of nodsel. """ elems = self.elems.selectNodes(nodsel) return self.coords[elems].average(wts=wts,axis=1) # The following is equivalent to avgNodes(self,nodsel,wts=None) # But is probably more efficient def meanNodes(self,nodsel): """Create nodes from the existing nodes of a mesh. `nodsel` is a local node selector as in :meth:`selectNodes` Returns the mean coordinates of the points in the selector as `(nelems*nnod,3)` array of coordinates, where nnod is the length of the node selector. """ elems = self.elems.selectNodes(nodsel) return self.coords[elems].mean(axis=1) def addNodes(self,newcoords,eltype=None): """Add new nodes to elements. `newcoords` is an `(nelems,nnod,3)` or`(nelems*nnod,3)` array of coordinates. Each element gets exactly `nnod` extra nodes from this array. The result is a Mesh with plexitude `self.nplex() + nnod`. """ newcoords = newcoords.reshape(-1,3) newnodes = arange(newcoords.shape[0]).reshape(self.elems.shape[0],-1) + self.coords.shape[0] elems = Connectivity(concatenate([self.elems,newnodes],axis=-1)) coords = Coords.concatenate([self.coords,newcoords]) return Mesh(coords,elems,self.prop,eltype) def addMeanNodes(self,nodsel,eltype=None): """Add new nodes to elements by averaging existing ones. `nodsel` is a local node selector as in :meth:`selectNodes` Returns a Mesh where the mean coordinates of the points in the selector are added to each element, thus increasing the plexitude by the length of the items in the selector. The new element type should be set to correct value. """ newcoords = self.meanNodes(nodsel) return self.addNodes(newcoords,eltype) def selectNodes(self,nodsel,eltype=None): """Return a mesh with subsets of the original nodes. `nodsel` is an object that can be converted to a 1-dim or 2-dim array. Examples are a tuple of local node numbers, or a list of such tuples all having the same length. Each row of `nodsel` holds a list of local node numbers that should be retained in the new connectivity table. """ elems = self.elems.selectNodes(nodsel) prop = self.prop if prop is not None: prop = column_stack([prop]*len(nodsel)).reshape(-1) return Mesh(self.coords,elems,prop=prop,eltype=eltype) def withProp(self,val): """Return a Mesh which holds only the elements with property val. val is either a single integer, or a list/array of integers. The return value is a Mesh holding all the elements that have the property val, resp. one of the values in val. The returned Mesh inherits the matching properties. If the Mesh has no properties, a copy with all elements is returned. """ if self.prop is None: return self.__class__(self.coords,self.elems,eltype=self.eltype) elif type(val) == int: return self.__class__(self.coords,self.elems[self.prop==val],prop=val,eltype=self.eltype) else: t = zeros(self.prop.shape,dtype=bool) for v in asarray(val).flat: t += (self.prop == v) return self.__class__(self.coords,self.elems[t],prop=self.prop[t],eltype=self.eltype) def withoutProp(self, val): """Return a Mesh without the elements with property val. This is the complementary method of Mesh.withProp(). val is either a single integer, or a list/array of integers. The return value is a Mesh holding all the elements that do not have the property val, resp. one of the values in val. The returned Mesh inherits the matching properties. If the Mesh has no properties, a copy with all elements is returned. """ ps=self.propSet() if type(val)==int: t=ps==val else: t=sum([ps==v for v in val], axis=0) return self.withProp(ps[t==0]) def splitProp(self): """Partition a Mesh according to its propery values. Returns a dict with the property values as keys and the corresponding partitions as values. Each value is a Mesh instance. It the Mesh has no props, an empty dict is returned. """ if self.prop is None: return {} else: return dict([(p,self.withProp(p)) for p in self.propSet()]) def splitRandom(self,n,compact=True): """Split a mesh in n parts, distributing the elements randomly. Returns a list of n Mesh objects, constituting together the same Mesh as the original. The elements are randomly distributed over the subMeshes. By default, the Meshes are compacted. Compaction may be switched off for efficiency reasons. """ sel = random.randint(0,n,(self.nelems())) return [ self.select(sel==i,compact=compact) for i in range(n) if i in sel ] def convert(self,totype,fuse=False): """Convert a Mesh to another element type. Converting a Mesh from one element type to another can only be done if both element types are of the same dimensionality. Thus, 3D elements can only be converted to 3D elements. The conversion is done by splitting the elements in smaller parts and/or by adding new nodes to the elements. Not all conversions between elements of the same dimensionality are possible. The possible conversion strategies are implemented in a table. New strategies may be added however. The return value is a Mesh of the requested element type, representing the same geometry (possibly approximatively) as the original mesh. If the requested conversion is not implemented, an error is raised. ..warning: Conversion strategies that add new nodes may produce double nodes at the common border of elements. The :meth:`fuse` method can be used to merge such coincident nodes. Specifying fuse=True will also enforce the fusing. This option become the default in future. """ # # totype is a string ! # if elementType(totype) == self.eltype: return self strategy = self.eltype.conversions.get(totype,None) while not type(strategy) is list: # This allows for aliases in the conversion database strategy = self.eltype.conversions.get(strategy,None) if strategy is None: raise ValueError,"Don't know how to convert %s -> %s" % (self.eltype,totype) # 'r' and 'v' steps can only be the first and only step steptype,stepdata = strategy[0] if steptype == 'r': # Randomly convert elements to one of the types in list return self.convertRandom(stepdata) elif steptype == 'v': return self.convert(stepdata).convert(totype) # Execute a strategy mesh = self totype = totype.split('-')[0] for step in strategy: steptype,stepdata = step if steptype == 'm': mesh = mesh.addMeanNodes(stepdata,totype) elif steptype == 's': mesh = mesh.selectNodes(stepdata,totype) else: raise ValueError,"Unknown conversion step type '%s'" % steptype if fuse: mesh = mesh.fuse() return mesh def convertRandom(self,choices): """Convert choosing randomly between choices """ ml = self.splitRandom(len(choices),compact=False) ml = [ m.convert(c) for m,c in zip(ml,choices) ] prop = self.prop if prop is not None: prop = concatenate([m.prop for m in ml]) elems = concatenate([m.elems for m in ml],axis=0) eltype = set([m.eltype for m in ml]) if len(eltype) > 1: raise RuntimeError,"Invalid choices for random conversions" eltype = eltype.pop() return Mesh(self.coords,elems,prop,eltype) def reduceDegenerate(self,eltype=None): """Reduce degenerate elements to lower plexitude elements. This will try to reduce the degenerate elements of the mesh to elements of a lower plexitude. If a target element type is given, only the matching reduce scheme is tried. Else, all the target element types for which a reduce scheme from the Mesh eltype is available, will be tried. The result is a list of Meshes of which the last one contains the elements that could not be reduced and may be empty. Property numbers propagate to the children. """ # # This duplicates a lot of the functionality of # Connectivity.reduceDegenerate # But this is really needed to keep the properties # if self.nelems() == 0: return [self] try: strategies = self.eltype.degenerate except: return [self] if eltype is not None: s = strategies.get(eltype,[]) if s: strategies = {eltype:s} else: strategies = {} if not strategies: return [self] m = self ML = [] for eltype in strategies: elems = [] prop = [] for conditions,selector in strategies[eltype]: e = m.elems cond = array(conditions) w = (e[:,cond[:,0]] == e[:,cond[:,1]]).all(axis=1) sel = where(w)[0] if len(sel) > 0: elems.append(e[sel][:,selector]) if m.prop is not None: prop.append(m.prop[sel]) # remove the reduced elems from m m = m.select(~w,compact=False) if m.nelems() == 0: break if elems: elems = concatenate(elems) if prop: prop = concatenate(prop) else: prop = None ML.append(Mesh(m.coords,elems,prop,eltype)) if m.nelems() == 0: break ML.append(m) return ML def splitDegenerate(self,autofix=True): """Split a Mesh in degenerate and non-degenerate elements. If autofix is True, the degenerate elements will be tested against known degeneration patterns, and the matching elements will be transformed to non-degenerate elements of a lower plexitude. The return value is a list of Meshes. The first holds the non-degenerate elements of the original Mesh. The last holds the remaining degenerate elements. The intermediate Meshes, if any, hold elements of a lower plexitude than the original. These may still contain degenerate elements. """ deg = self.elems.testDegenerate() M0 = self.select(~deg,compact=False) M1 = self.select(deg,compact=False) if autofix: ML = [M0] + M1.reduceDegenerate() else: ML = [M0,M1] return ML def removeDegenerate(self,eltype=None): """Remove the degenerate elements from a Mesh. Returns a Mesh with all degenerate elements removed. """ deg = self.elems.testDegenerate() return self.select(~deg,compact=False) def removeDuplicate(self,permutations=True): """Remove the duplicate elements from a Mesh. Duplicate elements are elements that consist of the same nodes, by default in no particular order. Setting permutations=False will only consider elements with the same nodes in the same order as duplicates. Returns a Mesh with all duplicate elements removed. """ ind,ok = self.elems.testDuplicate(permutations) return self.select(ind[ok]) def renumber(self,order='elems'): """Renumber the nodes of a Mesh in the specified order. order is an index with length equal to the number of nodes. The index specifies the node number that should come at this position. Thus, the order values are the old node numbers on the new node number positions. order can also be a predefined value that will generate the node index automatically: - 'elems': the nodes are number in order of their appearance in the Mesh connectivity. """ if order == 'elems': order = renumberIndex(self.elems) newnrs = inverseUniqueIndex(order) return self.__class__(self.coords[order],newnrs[self.elems],prop=self.prop,eltype=self.eltype) def renumberElems(self,order='nodes'): """Renumber the elements of a Mesh. Parameters: - `order`: either a 1-D integer array with a permutation of ``arange(self.nelems())``, specifying the requested order, or one of the following predefined strings: - 'nodes': order the elements in increasing node number order. - 'random': number the elements in a random order. - 'reverse': number the elements in. Returns: A Mesh equivalent with self but with the elements ordered as specified. See also: :meth:`Connectivity.reorder` """ order = self.elems.reorder(order) return self.__class__(self.coords,self.elems[order],prop=self.prop[order],eltype=self.eltype) ############################################################## # # Connection, Extrusion, Sweep, Revolution # ## BV: THE degree PARAMETER NEEDS TO BE IMPLEMENTED def connect(self,coordslist,div=1,degree=1,loop=False,eltype=None): """Connect a sequence of toplogically congruent Meshes into a hypermesh. Parameters: - `coordslist`: either a list of Coords instances, all having the same shape as self.coords, or a single Mesh instance whose `coords` attribute has the same shape. If it is a list of Coords, consider a list of Meshes obtained by combining each Coords object with the connectivity table, element type and property numbers of the current Mesh. The return value then is the hypermesh obtained by connecting each consecutive slice of (degree+1) of these meshes. The hypermesh has a dimensionality that is one higher than the original Mesh (i.e. points become lines, lines become surfaces, surfaces become volumes). The resulting elements will be of the given `degree` in the direction of the connection. Notice that the coords of the current Mesh are not used, unless these coords are explicitely included into the specified `coordslist`. In many cases `self.coords` will be the first item in `coordslist`, but it could occur anywhere in the list or even not at all. The number of Coords items in the list should be a multiple of `degree` plus 1. Specifying a single Mesh instead of a list of Coords is just a convenience for the often occurring situation of connecting a Mesh (self) with another one (mesh) having the same connectivity: in this case the list of Coords will automatically be set to ``[ self.coords, mesh.coords ]``. The `degree` should be 1 in this case. - `degree`: degree of the connection. Currently only degree 1 and 2 are supported. - If degree is 1, every Coords from the `coordslist` is connected with hyperelements of a linear degree in the connection direction. - If degree is 2, quadratic hyperelements are created from one Coords item and the next two in the list. Note that all Coords items should contain the same number of nodes, even for higher order elements where the intermediate planes contain less nodes. - `loop`: if True, the connections with loop around the list and connect back to the first. This is accomplished by adding the first Coords item back at the end of the list. - `div`: Either an integer, or a sequence of float numbers (usually in the range ]0.0..1.0]). With this parameter the generated elements can be further subdivided along the connection direction. If an int is given, the connected elements will be divided into this number of elements along the connection direction. If a sequence of float numbers is given, the numbers specify the relative distance along the connection direction where the elements should end. If the last value in the sequence is not equal to 1.0, there will be a gap between the consecutive connections. - `eltype`: the element type of the constructed hypermesh. Normally, this is set automatically from the base element type and the connection degree. If a different element type is specified, a final conversion to the requested element type is attempted. """ if type(coordslist) is list: clist = coordslist elif isinstance(coordslist,Mesh): utils.warn("Mesh.connect does no longer automatically compact the Meshes. You may have to use the Mesh.compact method to do so.") clist = [ self.coords, coordslist.coords ] else: raise ValueError,"Invalid coordslist argument" if sum([c.shape != self.coords.shape for c in clist]): raise ValueError,"Incompatible coordinate sets" # implement loop parameter if loop: clist.append(clist[0]) if (len(clist)-1) % degree != 0: raise ValueError,"Invalid length of coordslist (%s) for degree %s." % (len(clist),degree) ## if degree != 1: ## raise NotImplementedError,"Quadratic connection is not implemented yet." # set divisions if type(div) == int: div = arange(1,div+1) / float(div) else: div = array(div).ravel() # For higher order non-lagrangian elements the procedure could be # optimized by first compacting the coords and elems. # Instead we opted for the simpler method of adding the maximum # number of nodes, and then selecting the used ones. # A final compact() throws out the unused points. # Concatenate the coordinates #print "CLIST = %s" % len(clist) #print "BBOX clist = %s" % bbox(clist) x = [ Coords.interpolate(xi,xj,div).reshape(-1,3) for xi,xj in zip(clist[:-1:degree],clist[degree::degree]) ] x = Coords.concatenate(clist[:1] + x) #print "BBOX x = %s" % bbox(x) #print "X = %s" % x.shape[0] # Create the connectivity table nnod = self.ncoords() #print "NNOD = %s" % nnod nrep = (x.shape[0]//nnod - 1) // degree #print "NREP=%s" % nrep #print "INPUT ELEMS",self.elems e = extrudeConnectivity(self.elems,nnod,degree) #print "SINGLE EXTRUSION",e e = replicConnectivity(e,nrep,nnod*degree) #print "REPLICATED EXTRUSION",e #print "COORDS SHAPE: %s" % x.shape[0] # Create the Mesh M = Mesh(x,e).setProp(self.prop) # convert to proper eltype if eltype: M = M.convert(eltype) return M # deprecated in 0.8.5 def connectSequence(self,coordslist,div=1,degree=1,loop=False,eltype=None): utils.deprec("Mesh.connectSequence is deprecated: use Mesh.connect instead") return self.connect(coordslist,div=div,degree=degree,loop=loop,eltype=eltype) def extrude(self,n,step=1.,dir=0,degree=1,eltype=None): """Extrude a Mesh in one of the axes directions. Returns a new Mesh obtained by extruding the given Mesh over `n` steps of length `step` in direction of axis `dir`. """ print "Extrusion over %s steps of length %s" % (n,step) x = [ self.coords.trl(dir,i*n*step/degree) for i in range(1,degree+1) ] print bbox(x) return self.connect([self.coords] + x,n*degree,degree=degree,eltype=eltype) #return self.connect(self.trl(dir,n*step),n*degree,degree=degree,eltype=eltype) def revolve(self,n,axis=0,angle=360.,around=None,loop=False,eltype=None): """Revolve a Mesh around an axis. Returns a new Mesh obtained by revolving the given Mesh over an angle around an axis in n steps, while extruding the mesh from one step to the next. This extrudes points into lines, lines into surfaces and surfaces into volumes. """ angles = arange(n+1) * angle / n seq = [ self.coords.rotate(angle=a,axis=axis,around=around) for a in angles ] return self.connect(seq,loop=loop,eltype=eltype) def sweep(self,path,eltype=None,**kargs): """Sweep a mesh along a path, creating an extrusion Returns a new Mesh obtained by sweeping the given Mesh over a path. The returned Mesh has double plexitude of the original. The operation is similar to the extrude() method, but the path can be any 3D curve. This function is usually used to extrude points into lines, lines into surfaces and surfaces into volumes. By default it will try to fix the connectivity ordering where appropriate. If autofix is switched off, the connectivities are merely stacked, and the user may have to fix it himself. Currently, this function produces the correct element type, but the geometry . """ seq = sweepCoords(self.coords,path,**kargs) return self.connect(seq,eltype=eltype) def smooth(self, iterations=1, lamb=0.5, k=0.1, edg=True): """Return a smoothened mesh. Smoothing algorithm based on lowpass filters. If edg is True, the algorithm tries to smooth the outer border of the mesh seperately to reduce mesh shrinkage. Higher values of k can reduce shrinkage even more (up to a point where the mesh expands), but will result in less smoothing per iteration. """ if iterations<1: return self mu = -lamb/(1-k*lamb) adj=self.getEdges().adjacency(kind='n') if edg: externals = resize(False,self.ncoords()) expoints = unique(self.getFreeEntities()) if len(expoints) != self.ncoords(): externals[expoints] = True a = adj[externals].ravel() inpoints = delete(range(self.ncoords()), expoints) for i in range(len(a)): if a[i] in inpoints: a[i]=-2 adj[externals] = a.reshape(adj[externals].shape) else: message('Failed to recognize external points.\nShrinkage may be considerable.') w = ones(adj.shape,dtype=float) w[adj<0] = 0. w /= (adj>=0).sum(-1).reshape(-1,1) w = w.reshape(adj.shape[0],adj.shape[1],1) c = self.coords.copy() for i in range(iterations): c = (1.-lamb)*c + lamb*(w*c[adj]).sum(1) c = (1.-mu)*c + mu*(w*c[adj]).sum(1) return Mesh(c, self.elems, self.prop, eltype=self.eltype) def __add__(self,other): """Return the sum of two Meshes. The sum of the Meshes is simply the concatenation thereof. It allows us to write simple expressions as M1+M2 to concatenate the Meshes M1 and M2. Both meshes should be of the same plexitude and have the same eltype. The result will be of the same class as self (either a Mesh or a subclass thereof). """ return self.__class__.concatenate([self,other]) @classmethod def concatenate(clas,meshes,**kargs): """Concatenate a list of meshes of the same plexitude and eltype Merging of the nodes can be tuned by specifying extra arguments that will be passed to :meth:`Coords:fuse`. This is a class method, and should be invoked as follows:: Mesh.concatenate([mesh0,mesh1,mesh2]) """ nplex = set([ m.nplex() for m in meshes ]) if len(nplex) > 1: raise ValueError,"Cannot concatenate meshes with different plexitude: %s" % str(nplex) eltype = set([ m.eltype for m in meshes if m.eltype is not None ]) if len(eltype) > 1: raise ValueError,"Cannot concatenate meshes with different eltype: %s" % str(eltype) if len(eltype) == 1: eltype = eltype.pop() else: eltype = None prop = [m.prop for m in meshes] if None in prop: prop = None else: prop = concatenate(prop) coords,elems = mergeMeshes(meshes,**kargs) elems = concatenate(elems,axis=0) return clas(coords,elems,prop=prop,eltype=eltype) # Test and clipping functions def test(self,nodes='all',dir=0,min=None,max=None,atol=0.): """Flag elements having nodal coordinates between min and max. This function is very convenient in clipping a Mesh in a specified direction. It returns a 1D integer array flagging (with a value 1 or True) the elements having nodal coordinates in the required range. Use where(result) to get a list of element numbers passing the test. Or directly use clip() or cclip() to create the clipped Mesh The test plane can be defined in two ways, depending on the value of dir. If dir == 0, 1 or 2, it specifies a global axis and min and max are the minimum and maximum values for the coordinates along that axis. Default is the 0 (or x) direction. Else, dir should be compaitble with a (3,) shaped array and specifies the direction of the normal on the planes. In this case, min and max are points and should also evaluate to (3,) shaped arrays. nodes specifies which nodes are taken into account in the comparisons. It should be one of the following: - a single (integer) point number (< the number of points in the Formex) - a list of point numbers - one of the special strings: 'all', 'any', 'none' The default ('all') will flag all the elements that have all their nodes between the planes x=min and x=max, i.e. the elements that fall completely between these planes. One of the two clipping planes may be left unspecified. """ if min is None and max is None: raise ValueError,"At least one of min or max have to be specified." f = self.coords[self.elems] if type(nodes)==str: nod = range(f.shape[1]) else: nod = nodes if type(dir) == int: if not min is None: T1 = f[:,nod,dir] > min if not max is None: T2 = f[:,nod,dir] < max else: if min is not None: T1 = f.distanceFromPlane(min,dir) > -atol if max is not None: T2 = f.distanceFromPlane(max,dir) < atol if min is None: T = T2 elif max is None: T = T1 else: T = T1 * T2 if len(T.shape) == 1: return T if nodes == 'any': T = T.any(axis=1) elif nodes == 'none': T = (1-T.any(1)).astype(bool) else: T = T.all(axis=1) return T def clip(self,t,compact=False): """Return a Mesh with all the elements where t>0. t should be a 1-D integer array with length equal to the number of elements of the Mesh. The resulting Mesh will contain all elements where t > 0. """ return self.select(t>0,compact=compact) def cclip(self,t,compact=False): """This is the complement of clip, returning a Mesh where t<=0. """ return self.select(t<=0,compact=compact) def clipAtPlane(self,p,n,nodes='any',side='+'): """Return the Mesh clipped at plane (p,n). This is a convenience function returning the part of the Mesh at one side of the plane (p,n) """ if side == '-': n = -n return self.clip(self.test(nodes=nodes,dir=n,min=p)) def volumes(self): """Return the signed volume of all the mesh elements For a 'tet4' tetraeder Mesh, the volume of the elements is calculated as 1/3 * surface of base * height. For other Mesh types the volumes are calculated by first splitting the elements into tetraeder elements. The return value is an array of float values with length equal to the number of elements. If the Mesh conversion to tetraeder does not succeed, the return value is None. """ try: M = self.convert('tet4') mult = M.nelems() // self.nelems() if mult*self.nelems() != M.nelems(): raise ValueError,"Conversion to tet4 Mesh produces nonunique split paterns" f = M.coords[M.elems] vol = 1./6. * vectorTripleProduct(f[:, 1]-f[:, 0], f[:, 2]-f[:, 1], f[:, 3]-f[:, 0]) if mult > 1: vol = vol.reshape(-1,mult).sum(axis=1) return vol except: return None def volume(self): """Return the total volume of a Mesh. If the Mesh can not be converted to tet4 type, 0 is returned """ try: return self.volumes().sum() except: return 0.0 def actor(self,**kargs): if self.nelems() == 0: return None from gui.actors import GeomActor return GeomActor(self,**kargs) # # NEEDS CLEANUP BEFORE BEING REINSTATED # ===================================== # # BV: Should be made dependent on getFaces # or become a function?? # ?? DOES THIS WORK FOR *ANY* MESH ?? # What with a mesh of points, lines, ... # Also, el.faces can contain items of different plexitude # Maybe define this for 2D meshes only? # Formulas for both linear and quadratic edges # For quadratic: Option to select tangential or chordal directions # #### Currently reinstated in trisurface.py ! ## def getAngles(self, angle_spec=Deg): ## """_Returns the angles in Deg or Rad between the edges of a mesh. ## The returned angles are shaped as (nelems, n1faces, n1vertices), ## where n1faces are the number of faces in 1 element and the number ## of vertices in 1 face. ## """ ## #mf = self.coords[self.getFaces()] ## mf = self.coords[self.getLowerEntities(2,unique=False)] ## v = mf - roll(mf,-1,axis=1) ## v=normalize(v) ## v1=-roll(v,+1,axis=1) ## angfac= arccos( clip(dotpr(v, v1), -1., 1. ))/angle_spec ## el = self.eltype ## return angfac.reshape(self.nelems(),len(el.faces), len(el.faces[0])) ## This is dependent on removed getAngles ## # ?? IS THIS DEFINED FOR *ANY* MESH ?? ## #this is define for every linear surface or linear volume mesh. ## def equiAngleSkew(self): ## """Returns the equiAngleSkew of the elements, a mesh quality parameter . ## It quantifies the skewness of the elements: normalize difference between ## the worst angle in each element and the ideal angle (angle in the face ## of an equiangular element, qe). ## """ ## eang=self.getAngles(Deg) ## eangsh= eang.shape ## eang= eang.reshape(eangsh[0], eangsh[1]*eangsh[2]) ## eangMax, eangmin=eang.max(axis=1), eang.min(axis=1) ## f= self.eltype.faces[1] ## nedginface=len( array(f[ f.keys()[0] ], int)[0] ) ## qe=180.*(nedginface-2.)/nedginface ## extremeAngles= [ (eangMax-qe)/(180.-qe), (qe-eangmin)/qe ] ## return array(extremeAngles).max(axis=0) ######################## Functions ##################### # BV: THESE SHOULD GO TO connectivity MODULE def extrudeConnectivity(e,nnod,degree): """_Extrude a Connectivity to a higher level Connectivity. e: Connectivity nnod: a number > highest node number degree: degree of extrusion (currently 1 or 2) The extrusion adds `degree` planes of nodes, each with an node increment `nnod`, to the original Connectivity `e` and then selects the target nodes from it as defined by the e.eltype.extruded[degree] value. Currently returns an integer array, not a Connectivity! """ try: eltype,reorder = e.eltype.extruded[degree] except: try: eltype = e.eltype.name() except: eltype = None raise ValueError,"I don't know how to extrude a Connectivity of eltype '%s' in degree %s" % (eltype,degree) # create hypermesh Connectivity e = concatenate([e+i*nnod for i in range(degree+1)],axis=-1) # Reorder nodes if necessary if len(reorder) > 0: e = e[:,reorder] return Connectivity(e,eltype=eltype) def replicConnectivity(e,n,inc): """_Repeat a Connectivity with increasing node numbers. e: a Connectivity n: integer: number of copies to make inc: integer: increment in node numbers for each copy Returns the concatenation of n connectivity tables, which are each copies of the original e, but having the node numbers increased by inc. """ return Connectivity(concatenate([e+i*inc for i in range(n)]),eltype=e.eltype) def mergeNodes(nodes,fuse=True,**kargs): """Merge all the nodes of a list of node sets. Merging the nodes creates a single Coords object containing all nodes, and the indices to find the points of the original node sets in the merged set. Parameters: - `nodes`: a list of Coords objects, all having the same shape, except possibly for their first dimension - `fuse`: if True (default), coincident (or very close) points will be fused to a single point - `**kargs`: keyword arguments that are passed to the fuse operation Returns: - a Coords with the coordinates of all (unique) nodes, - a list of indices translating the old node numbers to the new. These numbers refer to the serialized Coords. The merging operation can be tuned by specifying extra arguments that will be passed to :meth:`Coords.fuse`. """ coords = Coords(concatenate([x for x in nodes],axis=0)) if fuse: coords,index = coords.fuse(**kargs) else: index = arange(coords.shape[0]) n = array([0] + [ x.npoints() for x in nodes ]).cumsum() ind = [ index[f:t] for f,t in zip(n[:-1],n[1:]) ] return coords,ind def mergeMeshes(meshes,fuse=True,**kargs): """Merge all the nodes of a list of Meshes. Each item in meshes is a Mesh instance. The return value is a tuple with: - the coordinates of all unique nodes, - a list of elems corresponding to the input list, but with numbers referring to the new coordinates. The merging operation can be tuned by specifying extra arguments that will be passed to :meth:`Coords:fuse`. Setting fuse=False will merely concatenate all the mesh.coords, but not fuse them. """ coords = [ m.coords for m in meshes ] elems = [ m.elems for m in meshes ] coords,index = mergeNodes(coords,fuse,**kargs) return coords,[Connectivity(i[e]) for i,e in zip(index,elems)] ########### Deprecated ##################### # BV: # While this function seems to make sense, it should be avoided # The creator of the mesh normally KNOWS the correct connectivity, # and should immediately fix it, instead of calculating it from # coordinate data # It could be used in a general Mesh checking/fixing utility def correctHexMeshOrientation(hm): """_hexahedral elements have an orientation. Some geometrical transformation (e.g. reflect) may produce inconsistent orientation, which results in negative (signed) volume of the hexahedral (triple product). This function fixes the hexahedrals without orientation. """ from formex import vectorTripleProduct hf=hm.coords[hm.elems] tp=vectorTripleProduct(hf[:, 1]-hf[:, 0], hf[:, 2]-hf[:, 1], hf[:, 4]-hf[:, 0])# from formex.py hm.elems[tp<0.]=hm.elems[tp<0.][:, [4, 5, 6, 7, 0, 1, 2, 3]] return hm # deprecated in 0.8.5 def connectMeshSequence(ML,loop=False,**kargs): utils.deprec("connectMeshSequence is deprecated: use Mesh.connect instead") MR = ML[1:] if loop: MR.append(ML[0]) else: ML = ML[:-1] HM = [ Mi.connect(Mj,**kargs) for Mi,Mj in zip (ML,MR) ] return Mesh.concatenate(HM) # End pyformex-0.8.6/pyformex/template.py0000644000211500021150000000241511700416072017270 0ustar benebene00000000000000# *** pyformex *** ## ## Copyright (C) 2011 John Doe (j.doe@somewhere.org) ## Distributed under the GNU General Public License version 3 or later. ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Script Template This is a template file to show the general layout of a pyFormex script. In the current version, a pyFormex script should obey the following rules: - file name extension is '.py' - first (comment) line contains 'pyformex' The script starts by preference with a docstring (like this), composed of a short first line, then a blank line and one or more lines explaining the intention of the script. """ print "This is the pyFormex template script" # End pyformex-0.8.6/pyformex/geomfile.py0000644000211500021150000004723311705104656017262 0ustar benebene00000000000000# $Id: geomfile.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Handling pyFormex Geometry Files This module defines a class to work with files in the native pyFormex Geometry File Format. """ import utils import filewrite from coords import * from formex import Formex from mesh import Mesh from odict import ODict from pyformex import message,debug,warning import os class GeometryFile(object): """A class to handle files in the pyFormex Geometry File format. The pyFormex Geometry File format allows the storage of most of the geometrical objects inside pyFormex, as well as some of their attributes. If `file` is a string, a file with that name is opened with the specified `mode`. If no mode is specified, 'r' will be used for existing files and 'w' for new files. Else, `file` should be an already open file. For files opened in write mode, Geometry classes can provide the facility """ _version_ = '1.5' def __init__(self,fil,mode=None,sep=' ',ifmt=' ',ffmt=' '): """Create the GeometryFile object.""" isname = type(fil) == str if isname: if mode is None: if os.path.exists(fil): mode = 'r' else: mode = 'w' fil = open(fil,mode) self.isname = isname self.fil = fil self.writing = self.fil.mode[0] in 'wa' if self.writing: self.sep = sep self.fmt = {'i':ifmt,'f':ffmt} if self.isname: if mode[0] == 'w': self.writeHeader() elif mode[0] == 'r': self.readHeader() def reopen(self,mode='r'): """Reopen the file, possibly changing the mode. The default mode for the reopen is 'r' """ self.fil.close() self.__init__(self.fil.name,mode) print self.fil,self.writing,self.isname def close(self): """Close the file. After closing, the file is no longer accessible. """ self.fil.close() self.fil = None def checkWritable(self): if not self.writing: raise RuntimeError,"File is not opened for writing" def writeHeader(self): """Write the header of a pyFormex geometry file. The header identifies the file as a pyFormex geometry file and sets the following global values: - `version`: the version of the geometry file format - `sep`: the default separator to be used when not specified in the data block """ self.fil.write("# pyFormex Geometry File (http://pyformex.org) version='%s'; sep='%s'\n" % (self._version_,self.sep)) def writeData(self,data,sep,fmt=None): """Write an array of data to a pyFormex geometry file. If fmt is None, the data are written using numpy.tofile, with the specified separator. If fmt is specified, """ if not self.writing: raise RuntimeError,"File is not opened for writing" kind = data.dtype.kind if fmt is None: fmt = self.fmt[kind] filewrite.writeData(data,self.fil,fmt) ## if fmt is None: ## data.tofile(self.fil,sep) ## self.fil.write('\n') ## else: ## from lib.misc import tofile_int32,tofile_float32 ## val = data.reshape(-1,data.shape[-1]) ## if kind == 'i': ## val = val.astype(int32) ## tofile_int32(val,self.fil,'%i ') ## elif kind == 'f': ## val = val.astype(float32) ## tofile_float32(val,self.fil,'%f ') def write(self,geom,name=None,sep=None): """Write any geometry object to the geometry file. `geom` is one of the Geometry data types of pyFormex or a list or dict of such objects. Currently exported geometry objects are :class:`Coords`, :class:`Formex`, :class:`Mesh`, :class:`PolyLine`, :class:`BezierSpline`. The geometry object is written to the file using the specified separator, or the default. """ self.checkWritable() if isinstance(geom,dict): for name in geom: self.write(geom[name],name,sep) elif isinstance(geom,list): if name is None: for obj in geom: self.write(obj,None,sep) else: name = utils.NameSequence(name) for obj in geom: self.write(obj,name.next(),sep) elif hasattr(geom,'write_geom'): geom.write_geom(self,name,sep) else: try: writefunc = getattr(self,'write'+geom.__class__.__name__) except: warning("Can not (yet) write objects of type %s to geometry file: skipping" % type(geom)) return try: writefunc(geom,name,sep) except: warning("Error while writing objects of type %s to geometry file: skipping" % type(geom)) def writeFormex(self,F,name=None,sep=None): """Write a Formex to the geometry file. `F` is a Formex. The coords attribute of the Formex is written as an array to the geometry file. If the Formex has a props attribute, it is also written. """ if sep is None: sep = self.sep hasprop = F.prop is not None head = "# objtype='Formex'; nelems=%s; nplex=%s; props=%s; eltype='%s'; sep='%s'" % (F.nelems(),F.nplex(),hasprop,F.eltype,sep) if name: head += "; name='%s'" % name self.fil.write(head+'\n') self.writeData(F.coords,sep) if hasprop: self.writeData(F.prop,sep) def writeMesh(self,F,name=None,sep=None,objtype='Mesh'): """Write a Mesh to a pyFormex geometry file. This writes a header line with these attributes and arguments: objtype, ncoords, nelems, nplex, props(True/False), eltype, name, sep. This is followed by the array data for: coords, elems, prop The objtype can/should be overridden for subclasses. """ if objtype is None: objtype = 'Mesh' if sep is None: sep = self.sep hasprop = F.prop is not None head = "# objtype='%s'; ncoords=%s; nelems=%s; nplex=%s; props=%s; eltype='%s'; sep='%s'" % (objtype,F.ncoords(),F.nelems(),F.nplex(),hasprop,F.eltype,sep) if name: head += "; name='%s'" % name self.fil.write(head+'\n') self.writeData(F.coords,sep) self.writeData(F.elems,sep) if hasprop: self.writeData(F.prop,sep) def writeTriSurface(self,F,name=None,sep=None): """Write a TriSurface to a pyFormex geometry file. This is equivalent to writeMesh(F,name,sep,objtype='TriSurface') """ self.writeMesh(F,name=None,sep=None,objtype='TriSurface') def writeCurve(self,F,name=None,sep=None,objtype=None,extra=None): """Write a Curve to a pyFormex geometry file. This function writes any curve type to the geometry file. The `objtype` is automatically detected but can be overridden. The following attributes and arguments are written in the header: ncoords, closed, name, sep. The following attributes are written as arrays: coords """ if sep is None: sep = self.sep head = "# objtype='%s'; ncoords=%s; closed=%s; sep='%s'" % (F.__class__.__name__,F.coords.shape[0],F.closed,sep) if name: head += "; name='%s'" % name if extra: head += extra self.fil.write(head+'\n') self.writeData(F.coords,sep) def writePolyLine(self,F,name=None,sep=None): """Write a PolyLine to a pyFormex geometry file. This is equivalent to writeCurve(F,name,sep,objtype='PolyLine') """ self.writeCurve(F,name=None,sep=None,objtype='PolyLine') def writeBezierSpline(self,F,name=None,sep=None): """Write a BezierSpline to a pyFormex geometry file. This is equivalent to writeCurve(F,name,sep,objtype='BezierSpline') """ self.writeCurve(F,name=None,sep=None,objtype='BezierSpline',extra="; degree=%s" % F.degree) def writeNurbsCurve(self,F,name=None,sep=None,extra=None): """Write a NurbsCurve to a pyFormex geometry file. This function writes a NurbsCurve instance to the geometry file. The following attributes and arguments are written in the header: ncoords, nknots, closed, name, sep. The following attributes are written as arrays: coords, knots """ if sep is None: sep = self.sep head = "# objtype='%s'; ncoords=%s; nknots=%s; closed=%s; sep='%s'" % (F.__class__.__name__,F.coords.shape[0],F.knots.shape[0],F.closed,sep) if name: head += "; name='%s'" % name if extra: head += extra self.fil.write(head+'\n') self.writeData(F.coords,sep) self.writeData(F.knots,sep) def writeNurbsSurface(self,F,name=None,sep=None,extra=None): """Write a NurbsSurface to a pyFormex geometry file. This function writes a NurbsSurface instance to the geometry file. The following attributes and arguments are written in the header: ncoords, nknotsu, nknotsv, closedu, closedv, name, sep. The following attributes are written as arrays: coords, knotsu, knotsv """ if sep is None: sep = self.sep head = "# objtype='%s'; ncoords=%s; nuknots=%s; nvknots=%s; uclosed=%s; vclosed=%s; sep='%s'" % (F.__class__.__name__,F.coords.shape[0],F.uknots.shape[0],F.uknots.shape[0],F.closed[0],F.closed[1],sep) if name: head += "; name='%s'" % name if extra: head += extra self.fil.write(head+'\n') self.writeData(F.coords,sep) self.writeData(F.uknots,sep) self.writeData(F.vknots,sep) def readHeader(self): """Read the header of a pyFormex geometry file. """ sep = ' ' s = self.fil.readline() if s.startswith('# Formex'): version = '1.1' elif s.startswith('# pyFormex Geometry File'): pos = s.rfind(')') exec(s[pos+1:].strip()) else: version = None raise RuntimeError,"This does not look like a pyFormex geometry file, or it is a very old version." self._version_ = version self.sep = sep self.objname = utils.NameSequence('%s_000' % utils.projectName(self.fil.name)) self.results = ODict() def read(self,count=-1): """Read a pyFormex Geometry File. fil is a filename or a file object. If the file is in a valid pyFormex geometry file format, geometry objects are read from the file and returned in a dictionary. The object names are used as keys. If the file does not contain object names, they will be autogenerated from the file name. A count may be specified to limit the number of objects read. If the file format is invalid and no valid geometry could be read, None is returned. Valid pyFormex geometry file formats are described in the manual. """ eltype = None # for compatibility with pre 1.1 .formex files ndim = 3 while True: objtype = 'Formex' # the default obj type obj = None sep = self.sep name = None s = self.fil.readline() if len(s) == 0: # end of file break if not s.startswith('#'): # not a header: skip continue ### ### THIS WILL USE UNDEFINED VALUES FROM A PREVIOUS OBJECT ### THIS SHOULD THEREFORE BE CHANGED !!!! ### try: exec(s[1:].strip()) except: continue # not a legal header: skip debug("READING OBJECT OF TYPE %s" % objtype) # OK, we have a legal header, try to read data if objtype == 'Formex': obj = self.readFormex(nelems,nplex,props,eltype,sep) elif objtype in ['Mesh','TriSurface']: obj = self.readMesh(ncoords,nelems,nplex,props,eltype,sep,objtype) elif objtype == 'PolyLine': obj = self.readPolyLine(ncoords,closed,sep) elif objtype == 'BezierSpline': if 'nparts' in s: # THis looks like a version 1.3 BezierSpline obj = self.oldReadBezierSpline(ncoords,nparts,closed,sep) else: if not 'degree' in s: # compatibility with 1.4 BezierSpline records degree = 3 obj = self.readBezierSpline(ncoords,closed,degree,sep) elif objtype == 'NurbsCurve': obj = self.readNurbsCurve(ncoords,nknots,closed,sep) elif globals().has_key(objtype) and hasattr(globals()[objtype],'read_geom'): obj = globals()[objtype].read_geom(self) else: message("Can not (yet) read objects of type %s from geometry file: skipping" % objtype) continue # skip to next header if obj is not None: if name is None: name = self.objname.next() self.results[name] = obj if count > 0 and len(self.results) >= count: break if self.isname: self.fil.close() return self.results def readFormex(self,nelems,nplex,props,eltype,sep): """Read a Formex from a pyFormex geometry file. The coordinate array for nelems*nplex points is read from the file. If present, the property numbers for nelems elements are read. From the coords and props a Formex is created and returned. """ ndim = 3 f = readArray(self.fil,Float,(nelems,nplex,ndim),sep=sep) if props: p = readArray(self.fil,Int,(nelems,),sep=sep) else: p = None return Formex(f,p,eltype) def readMesh(self,ncoords,nelems,nplex,props,eltype,sep,objtype='Mesh'): """Read a Mesh from a pyFormex geometry file. The following arrays are read from the file: - a coordinate array with `ncoords` points, - a connectivity array with `nelems` elements of plexitude `nplex`, - if present, a property number array for `nelems` elements. Returns the Mesh constructed from these data, or a subclass if an objtype is specified. """ # Make sure to import the Mesh subclasses that can be read from plugins.trisurface import TriSurface ndim = 3 x = readArray(self.fil,Float,(ncoords,ndim),sep=sep) e = readArray(self.fil,Float,(nelems,nplex),sep=sep) if props: p = readArray(self.fil,Int,(nelems,),sep=sep) else: p = None M = Mesh(x,e,p,eltype) if objtype != 'Mesh': try: clas = locals()[objtype] except: clas = globals()[objtype] M = clas(M) return M def readPolyLine(self,ncoords,closed,sep): """Read a Curve from a pyFormex geometry file. The coordinate array for ncoords points is read from the file and a Curve of type `objtype` is returned. """ from plugins.curve import PolyLine ndim = 3 coords = readArray(self.fil,Float,(ncoords,ndim),sep=sep) return PolyLine(control=coords,closed=closed) def readBezierSpline(self,ncoords,closed,degree,sep): """Read a BezierSpline from a pyFormex geometry file. The coordinate array for ncoords points is read from the file and a BezierSpline of the given degree is returned. """ from plugins.curve import BezierSpline ndim = 3 coords = readArray(self.fil,Float,(ncoords,ndim),sep=sep) return BezierSpline(control=coords,closed=closed,degree=degree) def readNurbsCurve(self,ncoords,nknots,closed,sep): """Read a NurbsCurve from a pyFormex geometry file. The coordinate array for ncoords control points and the nknots knot values are read from the file. A NurbsCurve of degree p = nknots - ncoords - 1 is returned. """ from plugins.nurbs import NurbsCurve ndim = 4 coords = readArray(self.fil,Float,(ncoords,ndim),sep=sep) knots = readArray(self.fil,Float,(nknots,),sep=sep) return NurbsCurve(control=coords,knots=knots,closed=closed) def readNurbsSurface(self,ncoords,nuknots,nvknots,uclosed,vclosed,sep): """Read a NurbsSurface from a pyFormex geometry file. The coordinate array for ncoords control points and the nuknots and nvknots values of uknots and vknots are read from the file. A NurbsSurface of degree ``pu = nuknots - ncoords - 1`` and ``pv = nvknots - ncoords - 1`` is returned. """ from plugins.nurbs import NurbsSurface ndim = 4 coords = readArray(self.fil,Float,(ncoords,ndim),sep=sep) uknots = readArray(self.fil,Float,(nuknots,),sep=sep) vknots = readArray(self.fil,Float,(nvknots,),sep=sep) return NurbsSurface(control=coords,knots=(uknots,vknots),closed=(uclosed,vclosed)) def oldReadBezierSpline(self,ncoords,nparts,closed,sep): """Read a BezierSpline from a pyFormex geometry file version 1.3. The coordinate array for ncoords points and control point array for (nparts,2) control points are read from the file. A BezierSpline is constructed and returned. """ from plugins.curve import BezierSpline ndim = 3 coords = readArray(self.fil,Float,(ncoords,ndim),sep=sep) control = readArray(self.fil,Float,(nparts,2,ndim),sep=sep) return BezierSpline(coords,control=control,closed=closed) def rewrite(self): """Convert the geometry file to the latest format. The conversion is done by reading all objects from the geometry file and writing them back. Parts that could not be succesfully read will be skipped. """ self.reopen('r') obj = self.read() self._version_ = GeometryFile._version_ #print self._version_ if obj is not None: self.reopen('w') self.write(obj) self.close() # End pyformex-0.8.6/pyformex/arraytools.py0000644000211500021150000014233311705104656017667 0ustar benebene00000000000000# $Id: arraytools.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A collection of numerical array utilities. These are general utility functions that depend only on the :mod:`numpy` array model. All pyformex modules needing :mod:`numpy` should import everything from this module:: from arraytools import * """ from numpy import * import utils if utils.checkVersion('python','2.6') >= 0: from itertools import combinations,permutations else: # Provide our own implementation of combinations,permutations def combinations(iterable, r): # combinations('ABCD', 2) --> AB AC AD BC BD CD # combinations(range(4), 3) --> 012 013 023 123 pool = tuple(iterable) n = len(pool) if r > n: return indices = range(r) yield tuple(pool[i] for i in indices) while True: for i in reversed(range(r)): if indices[i] != i + n - r: break else: return indices[i] += 1 for j in range(i+1, r): indices[j] = indices[j-1] + 1 yield tuple(pool[i] for i in indices) def permutations(iterable, r=None): # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC # permutations(range(3)) --> 012 021 102 120 201 210 pool = tuple(iterable) n = len(pool) if r is None: r = n if r > n: return indices = range(n) cycles = range(n, n-r, -1) yield tuple(pool[i] for i in indices[:r]) while n: for i in reversed(range(r)): cycles[i] -= 1 if cycles[i] == 0: indices[i:] = indices[i+1:] + indices[i:i+1] cycles[i] = n - i else: j = cycles[i] indices[i], indices[-j] = indices[-j], indices[i] yield tuple(pool[i] for i in indices[:r]) break else: return # Define a wrapper function for old versions of numpy try: unique([1],True) except TypeError: from numpy import unique1d as unique if unique([1],True)[0][0] == 0: # We have the old numy version utils.warn("BEWARE: OLD VERSION OF NUMPY!!!! We advise you to upgrade NumPy!") def unique(a,return_indices=False): """Replacement for numpy's unique1d""" import numpy if return_indices: indices,uniq = numpy.unique1d(a,True) return uniq,indices else: return numpy.unique1d(a) # default float and int types Float = float32 Int = int32 ########################################################################### ## ## some math functions ## ######################### # pi is defined in numpy # Deg is a multiplier to transform degrees to radians # Rad is a multiplier to transform radians to radians Deg = pi/180. Rad = 1. golden_ratio = 0.5 * (1.0 + sqrt(5.)) # Convenience functions: trigonometric functions with argument in degrees def sind(arg,angle_spec=Deg): """Return the sine of an angle in degrees. For convenience, this can also be used with an angle in radians, by specifying `angle_spec=Rad`. >>> print sind(30), sind(pi/6,Rad) 0.5 0.5 """ return sin(arg*angle_spec) def cosd(arg,angle_spec=Deg): """Return the cosine of an angle in degrees. For convenience, this can also be used with an angle in radians, by specifying ``angle_spec=Rad``. >>> print cosd(60), cosd(pi/3,Rad) 0.5 0.5 """ return cos(arg*angle_spec) def tand(arg,angle_spec=Deg): """Return the tangens of an angle in degrees. For convenience, this can also be used with an angle in radians, by specifying ``angle_spec=Rad``. """ return tan(arg*angle_spec) def arcsind(arg,angle_spec=Deg): """Return the angle whose sine is equal to the argument. By default, the angle is returned in Degrees. Specifying `angle_spec=Rad` will return the angle in radians. >>> print arcsind(0.5), arcsind(1.0,Rad) 30.0 1.57079632679 """ return arcsin(arg)/angle_spec def arccosd(arg,angle_spec=Deg): """Return the angle whose cosine is equal to the argument. By default, the angle is returned in Degrees. Specifying `angle_spec=Rad` will return the angle in radians. >>> print arccosd(0.5), arccosd(-1.0,Rad) 60.0 3.14159265359 """ return arccos(arg)/angle_spec def arctand(arg,angle_spec=Deg): """Return the angle whose tangens is equal to the argument. By default, the angle is returned in Degrees. Specifying `angle_spec=Rad` will return the angle in radians. >>> print arctand(1.0), arctand(-1.0,Rad) 45.0 -0.785398163397 """ return arctan(arg)/angle_spec def arctand2(sin,cos,angle_spec=Deg): """Return the angle whose sine and cosine values are given. By default, the angle is returned in Degrees. Specifying `angle_spec=Rad` will return the angle in radians. This returns an angle in the range ]-180,180]. >>> print arctand2(0.0,-1.0), arctand2(-sqrt(0.5),-sqrt(0.5),Rad) 180.0 -2.35619449019 """ return arctan2(sin,cos)/angle_spec def niceLogSize(f): """Return the smallest integer e such that 10**e > abs(f). This returns the number of digits before the decimal point. >>> print [ niceLogSize(a) for a in [1.3, 35679.23, 0.4, 0.00045676] ] [1, 5, 0, -3] """ return int(ceil(log10(abs(f)))) def niceNumber(f,below=False): """Return a nice number close to f. f is a float number, whose sign is disregarded. A number close to abs(f) but having only 1 significant digit is returned. By default, the value is above abs(f). Setting below=True returns a value above. Example: >>> [ str(niceNumber(f)) for f in [ 0.0837, 0.837, 8.37, 83.7, 93.7] ] ['0.09', '0.9', '9.0', '90.0', '100.0'] >>> [ str(niceNumber(f,below=True)) for f in [ 0.0837, 0.837, 8.37, 83.7, 93.7] ] ['0.08', '0.8', '8.0', '80.0', '90.0'] """ fa = abs(f) s = "%.0e" % fa m,n = map(int,s.split('e')) if not below: m = m+1 return m*10.**n def dotpr (A,B,axis=-1): """Return the dot product of vectors of A and B in the direction of axis. This multiplies the elements of the arrays A and B, and the sums the result in the direction of the specified axis. Default is the last axis. Thus, if A and B are sets of vectors in their last array direction, the result is the dot product of vectors of A with vectors of B. A and B should be broadcast compatible. >>> A = array( [[1.0, 1.0], [1.0,-1.0], [0.0, 5.0]] ) >>> B = array( [[5.0, 3.0], [2.0, 3.0], [1.33,2.0]] ) >>> print dotpr(A,B) [ 8. -1. 10.] """ A = asarray(A) B = asarray(B) return (A*B).sum(axis) def length(A,axis=-1): """Returns the length of the vectors of A in the direction of axis. The components of the vectors are stored along the specified array axis (default axis is the last). """ A = asarray(A) return sqrt((A*A).sum(axis)) def normalize(A,axis=-1): """Normalize the vectors of A in the direction of axis. The components of the vectors are stored along the specified array axis (default axis is the last). """ A = asarray(A) shape = list(A.shape) shape[axis] = 1 Al = length(A,axis).reshape(shape) # if (Al == 0.).any(): # raise ValueError,"Normalization of zero vector." return A/Al def projection(A,B,axis=-1): """Return the (signed) length of the projection of vector of A on B. The components of the vectors are stored along the specified array axis (default axis is the last). """ d = dotpr(A,B,axis) Bl = length(B,axis) if (Bl == 0.).any(): raise ValueError,"Projection on zero vector." return d/Bl def orthog(A,B,axis=-1): """Return the (signed) length of the projection of vector of A on B. The components of the vectors are stored along the specified array axis (default axis is the last). """ p = projection(A,B,axis=-1) return A - p * normalize(B) def norm(v,n=2): """Return thr `n`-norm of the vector `v`. Default is the quadratic norm (vector length). `n == 1` returns the sum. ``n <= 0`` returns the max absolute value. """ a = asarray(v).flat if n == 2: return sqrt((a*a).sum()) if n > 2: return (a**n).sum()**(1./n) if n == 1: return a.sum() if n <= 0: return abs(a).max() return def horner(a,u): """Compute the value of a polynom using Horner's rule. Params: - `a`: float(n+1,nd), `nd`-dimensional coefficients of the polynom of degree `n`, starting from lowest degree. - `u`: float(nu), parametric values where the polynom is evaluated Returns: float(nu,nd), nd-dimensional values of the polynom. >>> print horner([[1.,1.,1.],[1.,2.,3.]],[0.5,1.0]) [[ 1.5 2. 2.5] [ 2. 3. 4. ]] """ a = asarray(a) u = asarray(u).reshape(-1,1) c = a[-1] for i in range(-2,-1-len(a),-1): c = c * u + a[i] return c def solveMany(A,b,direct=True): """Solve many systems of linear equations. Parameters: - `A`: (ndof,ndof,nsys) shaped float array. - `b`: (ndof,nrhs,nsys) shaped float array. Returns: a float array `x` with same shape as `b`, where ``x[:,i,j]`` solves the system of linear equations A[:,:,j].x[:,i,j] = b[:,i,j]. For ndof in [1,2,3], all solutions are by default computed directly and simultaneously. If ``direct=False`` is specified, a general linear equation solver is called for each system of equations. This is also the method used if ``ndof > 4``. """ ndof,nrhs,nsys = b.shape if A.shape[:2] != (ndof,ndof): raise ValueError,"A(%s) and b(%s) have incompatible shape" % (A.shape,b.shape) if ndof < 4 and direct: A = addAxis(A,2) b = addAxis(b,1) if ndof == 1: x = b[0]/A[0,0] elif ndof == 2: denom = cross(A[:,0],A[:,1],axis=0) As = roll(A,-1,axis=1) As[:,1] *= -1. x = cross(b,As,axis=0) / denom elif ndof == 3: C = cross(roll(A,-1,axis=1),roll(A,-2,axis=1),axis=0) denom = dotpr(A[:,0],C[:,0],axis=0) x = dotpr(b,C,axis=0) / denom else: x = dstack([linalg.solve(A[:,:,i],b[:,:,i]) for i in range(nsys)]) return x def inside(p,mi,ma): """Return true if point p is inside bbox defined by points mi and ma""" return p[0] >= mi[0] and p[1] >= mi[1] and p[2] >= mi[2] and \ p[0] <= ma[0] and p[1] <= ma[1] and p[2] <= ma[2] def isClose(values,target,rtol=1.e-5,atol=1.e-8): """Returns an array flagging the elements close to target. `values` is a float array, `target` is a float value. `values` and `target` should be broadcastable to the same shape. The return value is a boolean array with shape of `values` flagging where the values are close to target. Two values `a` and `b` are considered close if :math:`| a - b | < atol + rtol * | b |` """ values = asarray(values) target = asarray(target) return abs(values - target) < atol + rtol * abs(target) def anyVector(v): """Create a 3D vector. v is some data compatible with a (3)-shaped float array. Returns v as such an array. """ return asarray(v,dtype=Float).reshape((3)) def unitVector(v): """Return a unit vector in the direction of v. `v` is either an integer specifying one of the global axes (0,1,2), or a 3-element array or compatible. """ if type(v) is int: u = zeros((3),dtype=Float) u[v] = 1.0 else: u = asarray(v,dtype=Float).reshape((3)) ul = length(u) if ul <= 0.0: raise ValueError,"Zero length vector %s" % v u /= ul return u def rotationMatrix(angle,axis=None,angle_spec=Deg): """Return a rotation matrix over angle, optionally around axis. The angle is specified in degrees, unless angle_spec=Rad is specified. If axis==None (default), a 2x2 rotation matrix is returned. Else, axis should specifying the rotation axis in a 3D world. It is either one of 0,1,2, specifying a global axis, or a vector with 3 components specifying an axis through the origin. In either case a 3x3 rotation matrix is returned. Note that: - rotationMatrix(angle,[1,0,0]) == rotationMatrix(angle,0) - rotationMatrix(angle,[0,1,0]) == rotationMatrix(angle,1) - rotationMatrix(angle,[0,0,1]) == rotationMatrix(angle,2) but the latter functions calls are more efficient. The result is returned as an array. """ a = angle*angle_spec c = cos(a) s = sin(a) if axis==None: f = [[c,s],[-s,c]] elif type(axis) == int: f = [[0.0 for i in range(3)] for j in range(3)] axes = range(3) i,j,k = axes[axis:]+axes[:axis] f[i][i] = 1.0 f[j][j] = c f[j][k] = s f[k][j] = -s f[k][k] = c else: X,Y,Z = unitVector(axis) t = 1.-c f = [ [ t*X*X + c , t*X*Y + s*Z, t*X*Z - s*Y ], [ t*Y*X - s*Z, t*Y*Y + c , t*Y*Z + s*X ], [ t*Z*X + s*Y, t*Z*Y - s*X, t*Z*Z + c ] ] return array(f) def rotmat(x): """Create a rotation matrix defined by 3 points in space. x is an array of 3 points. After applying the resulting rotation matrix to the global axes, the 0 axis becomes // to the vecors x0-x1, the 1 axis lies in the plane x0,x1,x2 and is orthogonal to x0-x1, and the 3 axis is orthogonal to the plane x0,x1,x2. """ u = normalize(x[1]-x[0]) v = normalize(x[2]-x[0]) v = normalize(orthog(v,u)) w = cross(u,v) # is orthog and normalized m = row_stack([u,v,w]) return m def trfMatrix(x,y): """Find the transformation matrix from points x0 to x1. x and y are each arrays of 3 non-colinear points. The return value is a tuple of a translation vector and a rotation matrix. The returned translation trl and rotationmatrix rot transform the points x thus that: - point x0 coincides with point y0, - line x0,x1 coincides with line y0,y1 - plane x0,x1,x2 coincides with plane y0,y1,y2 The rotation is to be applied first and should be around the first point x0. The full transformation of a Coords object is thus obtained by :: (coords - x0) * rot + trl + x0 = coords * rot + (trl+x0-x0*rot) """ # rotation matrices for both systems r1 = rotmat(x) r2 = rotmat(y) # combined rtoation matrix r = dot(r1.transpose(),r2) # translation vector (in a roate first operation t = y[0] - dot(x[0],r) return r,t def rotMatrix(u,w=[0.,0.,1.],n=3): """Create a rotation matrix that rotates axis 0 to the given vector. u is a vector representing the Return either a 3x3(default) or 4x4(if n==4) rotation matrix. """ u = unitVector(u) try: v = unitVector(cross(w,u)) except: if w == [0.,0.,1.]: w = [0.,1.,0.] v = unitVector(cross(w,u)) else: raise w = unitVector(cross(u,v)) m = row_stack([u,v,w]) if n != 4: return m else: a = identity(4) a[0:3,0:3] = m return a # HOW DOES THIS DEAL WITH GIMBALL LOCK? def rotationAnglesFromMatrix(mat,angle_spec=Deg): """Return rotation angles from rotation matrix mat. This returns the three angles around the global axes 0, 1 and 2. The angles are returned in degrees, unless angle_spec=Rad. """ rx = arctan(mat[1,2]/mat[2,2]) ry = -arcsin(mat[0,2]) rz = arctan(mat[0,1]/mat[0,0]) R = dot(dot(rotationMatrix(rx,0,Rad),rotationMatrix(ry,1,Rad)),rotationMatrix(rz,2,Rad)) T = isClose(mat,R,rtol=1.e-3,atol=1.e-5) w = where(~T.ravel())[0] w = w.tolist() if w == [3,4,5,6,7,8]: rx = pi + rx elif w == [0,1,3,4,6,7]: rz = pi + rz elif w == [0,1,5,8]: ry = pi - ry return rx / angle_spec, ry / angle_spec, rz / angle_spec # WHAT IF EITHER vec1 or vec2 is // to upvec? def vectorRotation(vec1,vec2,upvec=[0.,0.,1.]): """Return a rotation matrix for rotating vector vec1 to vec2 The rotation matrix will be such that the plane of vec2 and the rotated upvec will be parallel to the original upvec. This function is like :func:`arraytools.rotMatrix`, but allows the specification of vec1. The returned matrix should be used in postmultiplication to the Coords. """ u = normalize(vec1) u1 = normalize(vec2) w = normalize(upvec) v = normalize(cross(w,u)) w = normalize(cross(u,v)) v1 = normalize(cross(w,u1)) w1 = normalize(cross(u1,v1)) mat1 = column_stack([u,v,w]) mat2 = row_stack([u1,v1,w1]) mat = dot(mat1,mat2) return mat def growAxis(a,add,axis=-1,fill=0): """Increase the length of a single array axis. The specified axis of the array `a` is increased with a value `add` and the new elements all get the value `fill`. Parameters: - `a`: array - `add`: int The value to add to the axis length. If <= 0, the unchanged array is returned. - `axis`: int The axis to change, default -1 (last). - `fill`: int or float The value to set the new elements to. Returns: An array with same dimension and type as `a`, but with a length along `axis` equal to ``a.shape[axis] + add``. The new elements all have the value `fill`. Example: >>> growAxis([[1,2,3],[4,5,6]],2) array([[1, 2, 3, 0, 0], [4, 5, 6, 0, 0]]) """ a = asarray(a) if axis >= len(a.shape): raise ValueError,"No such axis number!" if add <= 0: return a else: missing = list(a.shape) missing[axis] = add return concatenate([a,fill * ones(missing,dtype=a.dtype)],axis=axis) def reorderAxis(a,order,axis=-1): """Reorder the planes of an array along the specified axis. The elements of the array are reordered along the specified axis according to the specified order. Parameters: - `a`: array_like - `order`: specifies how to reorder the elements. It is either one of the special string values defined below, or else it is an index holding a permutation of `arange(self.nelems()`. Each value specifies the index of the old element that should be placed at its position. Thus, the order values are the old index numbers at the position of the new index number. `order` can also take one of the following predefined values, resulting in the corresponding renumbering scheme being generated: - 'reverse': the elements along axis are placed in reverse order - 'random': the elements along axis are placed in random order Returns: An array with the same elements of self, where only the order along the specified axis has been changed. Example:: >>> reorderAxis([[1,2,3],[4,5,6]],[2,0,1]) array([[3, 1, 2], [6, 4, 5]]) """ a = asarray(a) n = a.shape[axis] if order == 'reverse': order = arange(n-1,-1,-1) elif order == 'random': order = random.permutation(n) else: order = asarray(order) return a.take(order,axis) def reverseAxis(a,axis=-1): """Reverse the elements along a computed axis. Example:: >>> reverseAxis([[1,2,3],[4,5,6]],0) array([[4, 5, 6], [1, 2, 3]]) Remark: if the axis is known in advance, it may be more efficient to use an indexing operation, like ``a[:,::-1,:]`` """ return reorderAxis(a,'reverse',axis) def addAxis(a,axis=0): """Add an additional axis with length 1 to an array. The new axis is inserted before the specified one. Default is to add it at the front. """ s = list(a.shape) s[axis:axis] = [1] return a.reshape(s) def stack(al,axis=0): """Stack a list of arrays along a new axis. al is a list of arrays all of the same shape. The return value is a new array with one extra axis, along which the input arrays are stacked. The position of the new axis can be specified, and is the first axis by default. """ return concatenate([addAxis(ai,axis) for ai in al],axis=axis) def checkArray(a,shape=None,kind=None,allow=None): """Check that an array a has the correct shape and type. The input `a` is anything that can be converted into a numpy array. Either `shape` and/or `kind` can be specified. and will then be checked. The dimensions where `shape` contains a -1 value are not checked. The number of dimensions should match. If `kind` does not match, but the value is included in `allow`, conversion to the requested type is attempted. Returns the array if valid; else, an error is raised. """ try: a = asarray(a) shape = asarray(shape) w = where(shape >= 0)[0] if (asarray(a.shape)[w] != shape[w]).any(): raise if kind is not None: if allow is None and a.dtype.kind != kind: raise if kind == 'f': a = a.astype(Float) return a except: raise ValueError,"Expected shape %s, kind %s, got: %s, %s" % (shape,kind,a.shape,a.dtype.kind) def checkArray1D(a,size=None,kind=None,allow=None): """Check that an array a has the correct size and type. Either size and or kind can be specified. If kind does not match, but is included in allow, conversion to the requested type is attempted. Returns the array if valid. Else, an error is raised. """ try: a = asarray(a)#.ravel() # seems sensible not to ravel! if (size is not None and a.size != size): raise if kind is not None: if allow is None and a.dtype.kind != kind: raise if kind == 'f': a = a.astype(Float) return a except: raise ValueError,"Expected size %s, kind %s, got: %s" % (size,kind,a) def checkArrayDim(a,ndim=-1): """Check that an array has the correct dimensionality. Returns asarray(a) if ndim < 0 or a.ndim == ndim Else, an error is raised. """ try: aa = asarray(a) if (ndim >= 0 and aa.ndim != ndim): raise return aa except: raise ValueError,"Expected an array with %s dimensions" % ndim def checkUniqueNumbers(nrs,nmin=0,nmax=None): """Check that an array contains a set of unique integers in a given range. This functions tests that all integer numbers in the array are within the range math:`nmin <= i < nmax` nrs: an integer array of any shape. nmin: minimum allowed value. If set to None, the test is skipped. nmax: maximum allowed value + 1! If set to None, the test is skipped. Default range is [0,unlimited]. If the numbers are no unique or one of the limits is passed, an error is raised. Else, the sorted list of unique values is returned. """ nrs = asarray(nrs) uniq = unique(nrs) if uniq.size != nrs.size or \ (nmin is not None and uniq.min() < nmin) or \ (nmax is not None and uniq.max() > nmax): raise ValueError,"Values not unique or not in range" return uniq def readArray(file,dtype,shape,sep=' '): """Read an array from an open file. This uses :func:`numpy.fromfile` to read an array with known shape and data type from an open file. The sep parameter can be specified as in fromfile. """ shape = asarray(shape) size = shape.prod() return fromfile(file=file,dtype=dtype,count=size,sep=sep).reshape(shape) def writeArray(file,array,sep=' '): """Write an array to an open file. This uses :func:`numpy.tofile` to write an array to an open file. The sep parameter can be specified as in tofile. """ array.tofile(file,sep=sep) def cubicEquation(a,b,c,d): """Solve a cubiq equation using a direct method. a,b,c,d are the (floating point) coefficients of a third degree polynomial equation:: a * x**3 + b * x**2 + c * x + d = 0 This function computes the three roots (real and complex) of this equation and returns full information about their kind, sorting order, occurrence of double roots. It uses scaling of the variables to enhance the accuracy. The return value is a tuple (r1,r2,r3,kind), where r1,r2 and r3 are three float values and kind is an integer specifying the kind of roots. Depending on the value of `kind`, the roots are defined as follows: ==== ========================================================== kind roots ==== ========================================================== 0 three real roots r1 < r2 < r3 1 three real roots r1 < r2 = r3 2 three real roots r1 = r2 < r3 3 three real roots r1 = r2 = r3 4 one real root r1 and two complex conjugate roots with real part r2 and imaginary part r3; the complex roots are thus: r2+i*r3 en r2-i*r3, where i = sqrt(-1). ==== ========================================================== If the coefficient a==0, a ValueError is raised. Example: >>> cubicEquation(1.,-3.,3.,-1.) ([1.0, 1.0, 1.0], 3) """ # # BV: We should return the solution of a second degree equation if a==0 # if a == 0.0: raise ValueError,"Coeeficient a of cubiq equation should not be 0" e3 = 1./3. pie = pi*2.*e3 r = b/a s = c/a t = d/a # scale variable sc = max(abs(r),sqrt(abs(s)),abs(t)**e3) sc = 10**(int(log10(sc))) r = r/sc s = s/sc/sc t = t/sc/sc/sc rx = r*e3 p3 = (s-r*rx)*e3 q2 = rx**3-rx*s/2.+t/2. q2s = q2*q2 p3c = p3**3 som = q2s+p3c if som <= 0.0: # 3 different real roots ic = 0 roots = [ -rx ] * 3 rt = sqrt(-p3c) if abs(rt) > 0.0: phi = cos(-q2/rt)*e3 rt = 2.*sqrt(-p3) roots += rt * cos(phi + [0.,+pie, -pie]) # sort the 3 roots roots.sort() if roots[1] == roots[2]: ic += 1 if roots[1] == roots[0]: ic += 2 else: # som < 0.0 # 1 real and 2 complex conjugate roots ic = 4 som = sqrt(som) u = -q2+som u = sign(abs(u)**e3) * u v = -q2-som v = sign(abs(v)**e3) * v r1 = u+v r2 = -r1/2-rx r3 = (u-v)*sqrt(3.)/2. r1 = r1-rx roots = array([r1,r2,r3]) # scale and return values roots *= sc return roots,ic # THIS MAY BE FASTER THAN olist.collectOnLength, BUT IT IS DEPENDENT ON NUMPY ## def collectOnLength(items): ## """Collect items with same length. ## a is a list of items of any type for which the function len() ## returns an integer value. ## The items are sorted in a number of bins, each containing the ## items with the same length. ## The return value is a tuple of: ## - a list of bins with the sorted items, ## - a list of indices of these items in the input list, ## - a list of lengths of the bins, ## - a list of the item length in each bin. ## """ ## np = array([ len(e) for e in items ]) ## itemlen = unique(np) ## itemnrs = [ where(np==p)[0] for p in itemlen ] ## itemgrps = [ olist.select(items,i) for i in itemnrs ] ## itemcnt = [ len(i) for i in itemnrs ] ## return itemgrps,itemnrs,itemcnt,itemlen def uniqueOrdered(ar1, return_index=False, return_inverse=False): """ Find the unique elements of an array. This works like numpy's unique, but uses a stable sorting algorithm. The returned index may therefore hold other entries for multiply occurring values. In such case, uniqueOrdered returns the first occurrence in the flattened array. The unique elements and the inverse index are always the same as those returned by numpy's unique. Parameters: - `ar1` : array_like This array will be flattened if it is not already 1-D. - `return_index` : bool, optional If True, also return the indices against `ar1` that result in the unique array. - `return_inverse` : bool, optional If True, also return the indices against the unique array that result in `ar1`. Returns: - `unique` : ndarray The unique values. - `unique_indices` : ndarray, optional The indices of the unique values. Only provided if `return_index` is True. - `unique_inverse` : ndarray, optional The indices to reconstruct the original array. Only provided if `return_inverse` is True. Example:: >>> a = array([2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,7,8]) >>> unique(a,True,True) (array([1, 2, 3, 4, 5, 6, 7, 8]), array([ 7, 0, 1, 10, 3, 4, 5, 6]), array([1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7])) >>> uniqueOrdered(a,True,True) (array([1, 2, 3, 4, 5, 6, 7, 8]), array([7, 0, 1, 2, 3, 4, 5, 6]), array([1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7])) Notice the difference in the 4-th entry of the second array. """ import numpy as np ar = np.asanyarray(ar1).flatten() if ar.size == 0: if return_inverse and return_index: return ar, np.empty(0, np.bool), np.empty(0, np.bool) elif return_inverse or return_index: return ar, np.empty(0, np.bool) else: return ar if return_inverse or return_index: perm = ar.argsort(kind='mergesort') aux = ar[perm] flag = np.concatenate(([True], aux[1:] != aux[:-1])) if return_inverse: iflag = np.cumsum(flag) - 1 iperm = perm.argsort() if return_index: return aux[flag], perm[flag], iflag[iperm] else: return aux[flag], iflag[iperm] else: return aux[flag], perm[flag] else: ar.sort() flag = np.concatenate(([True], ar[1:] != ar[:-1])) return ar[flag] def renumberIndex(index): """Renumber an index sequentially. Given a one-dimensional integer array with only non-negative values, and `nval` being the number of different values in it, and you want to replace its elements with values in the range `0..nval`, such that identical numbers are always replaced with the same number and the new values at their first occurrence form an increasing sequence `0..nval`. This function will give you the old numbers corresponding with each position `0..nval`. Parameters: - `index`: array_like, 1-D, integer An array with non-negative integer values Returns: A 1-D integer array with length equal to `nval`, where `nval` is the number of different values in `index`, and holding the original values corresponding to the new value `0..nval`. Remark: Use :func:`inverseUniqueIndex` to find the inverse mapping needed to replace the values in the index by the new ones. Example:: >>> renumberIndex([0,5,2,2,6,0]) array([0, 5, 2, 6]) >>> inverseUniqueIndex(renumberIndex([0,5,2,2,6,0]))[[0,5,2,2,6,0]] array([0, 1, 2, 2, 3, 0]) """ un,pos = uniqueOrdered(index,True) srt = pos.argsort() old = un[srt] return old def inverseUniqueIndex(index): """Inverse an index. Given a 1-D integer array with *unique* non-negative values, and `max` being the highest value in it, this function returns the position in the array of the values `0..max`. Values not occurring in input index get a value -1 in the inverse index. Parameters: - `index`: array_like, 1-D, integer An array with non-negative values, wihch all have to be unique. Returns: A 1-D integer array with length `max+1`, with the positions in `index` of the values `0..max`, or -1 if the value does not occur in `index`. Remark: The inverse index translates the unique index numbers in a sequential index, so that ``inverseUniqueIndex(index)[index] == arange(1+index.max())``. Example:: >>> inverseUniqueIndex([0,5,2,6]) array([ 0, -1, 2, -1, -1, 1, 3]) >>> inverseUniqueIndex([0,5,2,6])[[0,5,2,6]] array([0, 1, 2, 3]) """ ind = asarray(index) inv = -ones(ind.max()+1,dtype=ind.dtype) inv[ind] = arange(ind.size,dtype=inv.dtype) return inv def sortByColumns(a): """Sort an array on all its columns, from left to right. The rows of a 2-dimensional array are sorted, first on the first column, then on the second to resolve ties, etc.. Parameters: - `a`: array_like, 2-D Returns: A 1-D integer array specifying the order in which the rows have to be taken to obtain an array sorted by columns. Example:: >>> sortByColumns([[1,2],[2,3],[3,2],[1,3],[2,3]]) array([0, 3, 1, 4, 2]) """ A = checkArrayDim(a,2) keys = [A[:,i] for i in range(A.shape[1]-1,-1,-1)] return lexsort(keys) def uniqueRows(a,permutations=False): """Return (the indices of) the unique rows of a 2-D array. Parameters: - `a`: array_like, 2-D - `permutations`: bool If True, rows which are permutations of the same data are considered equal. The default is to consider permutations as different. Returns: - `uniq`: a 1-D integer array with the numbers of the unique rows from `a`. The order of the elements in `uniq` is determined by the sorting procedure, which in the current implementation is :func:`sortByColumns`. If `permutations==True`, `a` is sorted along its axis -1 before calling this sorting function. - `uniqid`: a 1-D integer array with length equal to `a.shape[0]` with the numbers of `uniq` corresponding to each of the rows of `a`. Example:: >>> uniqueRows([[1,2],[2,3],[3,2],[1,3],[2,3]]) (array([0, 3, 1, 2]), array([0, 2, 3, 1, 2])) >>> uniqueRows([[1,2],[2,3],[3,2],[1,3],[2,3]],permutations=True) (array([0, 3, 1]), array([0, 2, 2, 1, 2])) """ A = array(a,copy=permutations) if A.ndim != 2: raise ValueError if permutations: A.sort(axis=-1) srt = sortByColumns(A) A = A.take(srt,axis=0) ok = (A != roll(A,1,axis=0)).any(axis=1) if not ok[0]: # all doubles -> should result in one unique element ok[0] = True w = where(ok)[0] inv = inverseUniqueIndex(srt) uniqid = w.searchsorted(inv,side='right')-1 uniq = srt[ok] return uniq,uniqid def argNearestValue(values,target): """Return the index of the item nearest to target. Parameters: - `values`: a list of float values - `target`: a float value Returns: the position of the item in `values` that is nearest to `target`. Example: >>> argNearestValue([0.1,0.5,0.9],0.7) 1 """ v = array(values).ravel() c = v - target return argmin(c*c) def nearestValue(values,target): """Return the item nearest to target. ``values``: a list of float values ``target``: a single value Return value: the item in ``values`` values that is nearest to ``target``. """ return values[argNearestValue(values,target)] # # BV: This is a candidate for the C-library # def inverseIndex(index,maxcon=4): """Return an inverse index. An index is an array pointing at other items by their position. The inverse index is a collection of the reverse pointers. Negative values in the input index are disregarded. Parameters: - `index`: an array of integers, where only non-negative values are meaningful, and negative values are silently ignored. A Connectivity is a suitable argument. - `maxcon`: int: an initial estimate for the maximum number of rows a single element of index occurs at. The default will usually do well, because the procedure will automatically enlarge it when needed. Returns: An (mr,mc) shaped integer array where: - `mr` will be equal to the highest positive value in index, +1. - `mc` will be equal to the highest row-multiplicity of any number in `index`. Row `i` of the inverse index contains all the row numbers of `index` that contain the number `i`. Because the number of rows containing the number `i` is usually not a constant, the resulting array will have a number of columns `mc` corresponding to the highest row-occurrence of any single number. Shorter rows are padded with -1 values to flag non-existing entries. Example:: >>> inverseIndex([[0,1],[0,2],[1,2],[0,3]]) array([[ 0, 1, 3], [-1, 0, 2], [-1, 1, 2], [-1, -1, 3]]) """ ind = asarray(index) if len(ind.shape) != 2 or ind.dtype.kind != 'i': raise ValueError,"nndex should be an integer array with dimension 2" nr,nc = ind.shape mr = ind.max() + 1 mc = maxcon*nc # start with all -1 flags, maxcon*nc columns (because in each column # of index, some number might appear with multiplicity maxcon) inverse = zeros((mr,mc),dtype=ind.dtype) - 1 i = 0 # column in inverse where we will store next result for c in range(nc): col = ind[:,c].copy() # make a copy, because we will change it while(col.max() >= 0): # we still have values to process in this column uniq,pos = unique(col,True) #put the unique values at a unique position in inverse index ok = uniq >= 0 if i >= inverse.shape[1]: # no more columns available, expand it inverse = concatenate([inverse,zeros_like(inverse)-1],axis=-1) inverse[uniq[ok],i] = pos[ok] i += 1 # remove the stored values from index col[pos[ok]] = -1 inverse.sort(axis=-1) maxc = inverse.max(axis=0) inverse = inverse[:,maxc>=0] return inverse def matchIndex(target,values): """Find position of values in target. This function finds the position in the array `target` of the elements from the array `values`. Parameters: - `target`: an index array with all non-negative values. If not 1-D, it will be flattened. - `values`: an index array with all non-negative values. If not 1-D, it will be flattened. Returns: an index array with the same size as `values`. For each number in `values`, the index contains the position of that value in the flattened `target`, or -1 if that number does not occur in `target`. If an element from `values` occurs more than once in `target`, it is currently undefined which of those positions is returned. Remark that after ``m = matchIndex(target,values)`` the equality ``values[m] == target`` holds in all the non-negative positions of `m`. Example:: >>> A = array([1,3,4,5,7,8,9]) >>> B = array([0,6,7,1,2]) >>> matchIndex(A,B) array([-1, -1, 4, 0, -1]) """ target = target.reshape(-1,1) values = values.reshape(-1) inv = inverseIndex(target)[:,0] diff = values.max()-len(inv)+1 if diff > 0: inv = concatenate([inv,-ones((diff,),dtype=Int)]) return inv[values] ## THIS IS A CANDIDATE FOR THE LIBRARY !!! def groupArgmin(val,gid): """Compute the group minimum Computes the minimum value per group of a set of values tagged with a group number. Parameters: - `val`: (nval,) shaped array of values - `gid`: (nval,) shaped int array of group identifiers Returns: - `ugid`: (ngrp,) shaped int array with unique group identifiers - `minpos`: (ngrp,p) shape int array giving the position in `val` of the minimum of all values with the corresponding group identifier in `ugid`. After return, the minimum values corresponding to the groups in `ugid` are given by ``val[minpos]``. >>> val = array([ 0.0, 1.0, 2.0, 3.0, 4.0, -5.0 ]) >>> gid = array([ 2, 1, 1, 6, 6, 1 ]) >>> print groupArgmin(val,gid) (array([1, 2, 6]), array([5, 0, 3])) """ ugid = unique(gid) minid = hstack([ val[gid == i].argmin() for i in ugid ]) rng = arange(val.size) minpos = hstack([ rng[gid == i][j] for i,j in zip(ugid,minid) ]) return ugid,minpos ########################################################### # Working with sets of vectors def vectorLength(vec): """Return the lengths of a set of vectors. vec is an (n,3) shaped array holding a collection of vectors. The result is an (n,) shaped array with the length of each vector. """ return length(vec) def vectorNormalize(vec): """Normalize a set of vectors. vec is a (n,3) shaped arrays holding a collection of vectors. The result is a tuple of two arrays: - length (n): the length of the vectors vec - normal (n,3): unit-length vectors along vec. """ length = vectorLength(vec) normal = vec / length.reshape((-1,1)) return length,normal def vectorPairAreaNormals(vec1,vec2): """Compute area of and normals on parallellograms formed by vec1 and vec2. vec1 and vec2 are (n,3) shaped arrays holding collections of vectors. The result is a tuple of two arrays: - area (n) : the area of the parallellogram formed by vec1 and vec2. - normal (n,3) : (normalized) vectors normal to each couple (vec1,2). These are calculated from the cross product of vec1 and vec2, which indeed gives area * normal. Note that where two vectors are parallel, an area zero results and an axis with components NaN. """ normal = cross(vec1,vec2) area = vectorLength(normal) normal /= area.reshape((-1,1)) return area,normal def vectorPairArea(vec1,vec2): """Compute area of the parallellogram formed by a vector pair vec1,vec2. vec1 and vec2 are (n,3) shaped arrays holding collections of vectors. The result is an (n) shaped array with the area of the parallellograms formed by each pair of vectors (vec1,vec2). """ return vectorPairAreaNormals(vec1,vec2)[0] def vectorPairNormals(vec1,vec2): """Compute vectors normal to vec1 and vec2. vec1 and vec2 are (n,3) shaped arrays holding collections of vectors. The result is an (n,3) shaped array of unit length vectors normal to each couple (edg1,edg2). """ return vectorPairAreaNormals(vec1,vec2)[1] def vectorTripleProduct(vec1,vec2,vec3): """Compute triple product vec1 . (vec2 x vec3). vec1, vec2, vec3 are (n,3) shaped arrays holding collections of vectors. The result is a (n,) shaped array with the triple product of each set of corresponding vectors from vec1,vec2,vec3. This is also the square of the volume of the parallellepid formex by the 3 vectors. If vec1 is a unit normal, the result is also the area of the parallellogram (vec2,vec3) projected in the direction vec1. """ return dotpr(vec1,cross(vec2,vec3)) def vectorPairCosAngle(v1,v2): """Return the cosinus of the angle between the vectors v1 and v2. vec1 and vec2 are (n,3) shaped arrays holding collections of vectors. The result is an (n) shaped array with the cosinus of the angle between each pair of vectors (vec1,vec2). """ v1 = asarray(v1) v2 = asarray(v2) return dotpr(v1,v2) / sqrt(dotpr(v1,v1)*dotpr(v2,v2)) def vectorPairAngle(v1,v2): """Return the angle (in radians) between the vectors v1 and v2. vec1 and vec2 are (n,3) shaped arrays holding collections of vectors. The result is an (n) shaped array with the angle between each pair of vectors (vec1,vec2). """ return arccos(vectorPairCosAngle(v1,v2)) def histogram2(a,bins,range=None): """Compute the histogram of a set of data. This function is a like numpy's histogram function, but also returns the bin index for each individual entry in the data set. Parameters: - `a`: array_like. Input data. The histogram is computed over the flattened array. - `bins`: int or sequence of scalars. If bins is an int, it defines the number of equal-width bins in the given range. If bins is a sequence, it defines the bin edges, allowing for non-uniform bin widths. Both the leftmost and rightmost edges are included, thus the number of bins is len(bins)-1. - `range`: (float, float), optional. The lower and upper range of the bins. If not provided, range is simply (a.min(), a.max()). Values outside the range are ignored. This parameter is ignored if bins is a sequence. Returns: - `hist`: integer array with length nbins, holding the number of elements in each bin, - `ind`: a sequence of nbins integer arrays, each holding the indices of the elements fitting in the respective bins, - `xbins`: array of same type as data and with length nbins+1: returns the bin edges. Example: >>> histogram2([1,2,3,4,2,3,1],[1,2,3,4,5]) (array([2, 2, 2, 1]), [array([0, 6]), array([1, 4]), array([2, 5]), array([3])], array([1, 2, 3, 4, 5])) """ ar = asarray(a) if type(bins) == int: nbins = bins xbins = linspace(a.min(),a.max(),nbins+1) else: xbins = asarray(bins) nbins = len(xbins)-1 d = digitize(ar,xbins) ind = [ where(d==i)[0] for i in arange(1,nbins+1) ] hist = asarray([ i.size for i in ind ]) return hist,ind,xbins def movingView(a, size): """Create a moving view along the first axis of an array Parameters: - `a` : array_like: array for wihch to create a moving view - `size` : int: size of the moving view Returns: An array that is a view of the original array with an extra first axis of length w. Using swapaxes(0,axis) moving views over any axis can be created. Examples: >>> x=arange(10).reshape((5,2)) >>> print x [[0 1] [2 3] [4 5] [6 7] [8 9]] >>> print movingView(x, 3) [[[0 1] [2 3] [4 5]] [[2 3] [4 5] [6 7]] [[4 5] [6 7] [8 9]]] Calculate rolling sum of first axis: >>> print movingView(x, 3).sum(axis=0) [[ 6 9] [12 15] [18 21]] """ from numpy.lib import stride_tricks if size < 1: raise ValueError, "`size` must be at least 1." if size > a.shape[0]: raise ValueError, "`size` is too long." shape = (size, a.shape[0] - size + 1) + a.shape[1:] strides = (a.strides[0],) + a.strides return stride_tricks.as_strided(a, shape=shape, strides=strides) def movingAverage(a,n,m0=None,m1=None): """Compute the moving average along the first axis of an array. Parameters: - `a` : array_like: array to be averaged - `n` : int: moving sample size - `m0` : optional, int: if specified, the first data set of a will be prepended this number of times - `m1` : optional, int: if specified, the last data set of a will be appended this number of times Returns: An array with the moving average over n data sets along the first axis of a. The array has the same shape as a, except possibly for the length of the first axis. If neither m0 nor m1 are set, the first axis will have a length of a.shape[0] - (n-1). If both m0 and m1 are give, the first axis will have a length of a.shape[0] - (n-1) + m0 + m1. If either m0 or m1 are set and the other not, the missing value m0 or m1 will be computed thus that the return array has a first axis with length a.shape[0]. Examples: >>> x=arange(10).reshape((5,2)) >>> print movingAverage(x,3) [[ 2. 3.] [ 4. 5.] [ 6. 7.]] >>> print movingAverage(x,3,2) [[ 0. 1. ] [ 0.66666667 1.66666667] [ 2. 3. ] [ 4. 5. ] [ 6. 7. ]] """ if m0 is None and m1 is None: ae = a else: if m0 is None: m0 = n-1 - m1 elif m1 is None: m1 = n-1 - m0 ae = [a[:1]] * m0 + [ a ] + [a[-1:]] * m1 ae = concatenate(ae,axis=0) return movingView(ae,n).mean(axis=0) def randomNoise(shape,min=0.0,max=1.0): """Create an array with random values between min and max""" return random.random(shape) * (max-min) + min # End pyformex-0.8.6/pyformex/geometry.py0000644000211500021150000002712511705104656017324 0ustar benebene00000000000000# $Id: geometry.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A generic interface to the Coords transformation methods This module defines a generic Geometry superclass which adds all the possibilities of coordinate transformations offered by the Coords class to the derived classes. """ from coords import Coords class Geometry(object): """A generic geometry object allowing transformation of coords sets. The Geometry class is a generic parent class for all geometric classes, intended to make the Coords transformations available without explicit declaration. This class is not intended to be used directly, only through derived classes. Examples of derived classes are :class:`Formex`, :class:`Mesh`, :class:`Curve`. There is no initialization to be done when constructing a new instance of this class. The class just defines a set of methods which operate on the attribute `coords`, which should be a Coords object. Most of the transformation methods of the Coords class are thus exported through the Geometry class to its derived classes, and when called, will get executed on the `coords` attribute. The derived class constructor should make sure that the `coords` attribute exists, has the proper type and contains the coordinates of all the points that should get transformed under a Coords transformation. Derived classes can (and in most cases should) declare a method `_set_coords(coords)` returning an object that is identical to the original, except for its coords being replaced by new ones with the same array shape. The Geometry class provides two possible default implementations: - `_set_coords_inplace` sets the coords attribute to the provided new coords, thus changing the object itself, and returns itself, - `_set_coords_copy` creates a deep copy of the object before setting the coords attribute. The original object is unchanged, the returned one is the changed copy. When using the first method, a statement like ```B = A.scale(0.5)``` will result in both `A` and `B` pointing to the same scaled object, while with the second method, `A` would still be the untransformed object. Since the latter is in line with the design philosophy of pyFormex, it is set as the default `_set_coords` method. Most derived classes that are part of pyFormex however override this default and implement a more efficient copy method. The following :class:`Geometry` methods return the value of the same method applied on the `coords` attribute. Refer to the correponding :class:`coords.Coords` method for their precise arguments. :meth:`x`, :meth:`y`, :meth:`z`, :meth:`bbox`, :meth:`center`, :meth:`centroid`, :meth:`sizes`, :meth:`dsize`, :meth:`bsphere`, :meth:`distanceFromPlane`, :meth:`distanceFromLine`, :meth:`distanceFromPoint`, :meth:`directionalSize`, :meth:`directionalWidth`, :meth:`directionalExtremes`, :meth:`__str__`. The following :class:`Coords` transformation methods can be directly applied to a :class:`Geometry` object or a derived class object. The return value is a new object identical to the original, except for the coordinates, which will have been transformed by the specified method. Refer to the correponding :class:`coords.Coords` method in for the precise arguments of these methods: :meth:`scale`, :meth:`translate`, :meth:`centered`, :meth:`rotate`, :meth:`shear`, :meth:`reflect`, :meth:`affine`, :meth:`position`, :meth:`cylindrical`, :meth:`hyperCylindrical`, :meth:`toCylindrical`, :meth:`spherical`, :meth:`superSpherical`, :meth:`toSpherical`, :meth:`bump`, :meth:`bump1`, :meth:`bump2`, :meth:`flare`, :meth:`map`, :meth:`map1`, :meth:`mapd`, :meth:`replace`, :meth:`swapAxes`, :meth:`rollAxes`, :meth:`projectOnPlane`, :meth:`projectOnSphere`, :meth:`projectOnCylinder`, :meth:`isopar`, :meth:`transformCS`, :meth:`addNoise`, :meth:`rot`, :meth:`trl`. """ ########### Change the coords ################# def _coords_transform(func): """Perform a transformation on the .coords attribute of the object. This is a decorator function. """ coords_func = getattr(Coords,func.__name__) def newf(self,*args,**kargs): """Performs the Coords %s transformation on the coords attribute""" return self._set_coords(coords_func(self.coords,*args,**kargs)) newf.__name__ = func.__name__ newf.__doc__ ="""Apply '%s' transformation to the Geometry object. See :meth:`coords.Coords.%s` for details. """ % (func.__name__,func.__name__) return newf def _set_coords_inplace(self,coords): """Replace the current coords with new ones. """ if isinstance(coords,Coords) and coords.shape == self.coords.shape: self.coords = coords return self else: raise ValueError,"Invalid reinitialization of Geometry coords" def _set_coords_copy(self,coords): """Return a copy of the object with new coordinates replacing the old. """ return self.copy()._set_coords_inplace(coords) _set_coords = _set_coords_copy ########### Return information about the coords ################# def x(self): return self.coords.x() def y(self): return self.coords.y() def z(self): return self.coords.z() def bbox(self): return self.coords.bbox() def center(self): return self.coords.center() def centroid(self): return self.coords.centroid() def sizes(self): return self.coords.sizes() def dsize(self): return self.coords.dsize() def bsphere(self): return self.coords.bsphere() def info(self): return "coords" + str(self.coords.shape) def distanceFromPlane(self,*args,**kargs): return self.coords.distanceFromPlane(*args,**kargs) def distanceFromLine(self,*args,**kargs): return self.coords.distanceFromLine(*args,**kargs) def distanceFromPoint(self,*args,**kargs): return self.coords.distanceFromPoint(*args,**kargs) def directionalSize(self,*args,**kargs): return self.coords.directionalSize(*args,**kargs) def directionalWidth(self,*args,**kargs): return self.coords.directionalWidth(*args,**kargs) def directionalExtremes(self,*args,**kargs): return self.coords.directionalExtremes(*args,**kargs) def __str__(self): return self.coords.__str__() ########### Return a copy ################# def copy(self): """Return a deep copy of the object.""" from copy import deepcopy return deepcopy(self) ########### Coords transformations ################# @_coords_transform def scale(self,*args,**kargs): pass def resized(self,size=1.,tol=1.e-5): """Return a scaled copy of the Formex with given size in all directions. If a direction has zero size, it is not rescaled. """ s = self.sizes() s[s>> from formex import * >>> bbox([Coords([-1.,1.,0.]),Formex('l:5')]) Coords([[-1., 0., 0.], [ 1., 1., 0.]], dtype=float32) """ bboxes = [f.bbox() for f in objects if hasattr(f,'bbox') and not isnan(f.bbox()).any()] bboxes = [bb for bb in bboxes if bb is not None] if len(bboxes) == 0: o = origin() bboxes = [ [o,o] ] return Coords(concatenate(bboxes)).bbox() ########################################################################### ## ## class Coords ## ######################### # class Coords(ndarray): """A structured collection of points in a 3D cartesian space. The :class:`Coords` class is the basic data structure used throughout pyFormex to store coordinates of points in a 3D space. It is used by other classes, such as :class:`Formex` and :class:`Surface`, which thus inherit the same transformation capabilities. Applications will mostly use the higher level classes, which usually have more elaborated consistency checking and error handling. :class:`Coords` is implemented as a subclass of :class:`numpy.ndarray`, and thus inherits all its methods. The last axis of the :class:`Coords` always has a length equal to 3. Each set of 3 values along the last axis represents a single point in 3D cartesian space. The float datatype is only checked at creation time. It is the responsibility of the user to keep this consistent throughout the lifetime of the object. A new Coords object is created with the following syntax :: Coords(data=None,dtyp=Float,copy=False) Parameters: - `data`: array_like of type float. The last axis should have a length of 1, 2 or 3, bu will always be expanded to 3. If no data are specified, an empty Coords with shape (0,3) is created. - `dtyp`: the float datatype to be used. It not specified, the datatype of `data` is used, or the default :data:`Float` (which is equivalent to :data:`numpy.float32`). - `copy`: boolean. If ``True``, the data are copied. The default setting will try to use the original data if possible, e.g. if `data` is a correctly shaped and typed :class:`numpy.ndarray`. Example: >>> Coords([1.,0.]) Coords([ 1., 0., 0.], dtype=float32) """ # # :DEV # Because we have a __new__ constructor here and no __init__, # we have to list the arguments explicitely in the docstring above. # def __new__(clas, data=None, dtyp=Float, copy=False): """Create a new instance of :class:`Coords`.""" if data is None: # create an empty array : we need at least a 2D array # because we want the last axis to have length 3 and # we also need an axis with length 0 to have size 0 ar = ndarray((0,3),dtype=dtyp) else: # turn the data into an array, and copy if requested # DO NOT ADD ndmin=1 HERE ! (see below) ar = array(data,dtype=dtyp,copy=copy) # # The Coords object needs to be at least 1-D array, no a scalar # We could force 'ar' above to be at least 1-D, but that would # turn every scalar into a 1-D vector, which would circumvent # detection of input errors (e.g. with translation, where input # can be either a vector or an axis number) # if ar.ndim == 0: raise ValueError,"Expected array data, not a scalar" if ar.shape[-1] == 3: pass elif ar.shape[-1] in [1,2]: # make last axis length 3, adding 0 values ar = growAxis(ar,3-ar.shape[-1],-1) elif ar.shape[-1] == 0: # allow empty coords objects ar = ar.reshape(0,3) else: raise ValueError,"Expected a length 1,2 or 3 for last array axis" # Make sure dtype is a float type if ar.dtype.kind != 'f': ar = ar.astype(Float) # Transform 'subarr' from an ndarray to our new subclass. ar = ar.view(clas) return ar ########################################################################### # # Methods that return information about a Coords object or other # views on the object data, without changing the object itself. # General def points(self): """Returns the :class:`Coords` object as a simple set of points. This reshapes the array to a 2-dimensional array, flattening the structure of the points. """ return self.reshape((-1,3)) def pshape(self): """Returns the shape of the :class:`Coords` object. This is the shape of the `NumPy`_ array with the last axis removed. The full shape of the :class:`Coords` array can be obtained from its shape attribute. """ return self.shape[:-1] def npoints(self): """Return the total number of points.""" return asarray(self.shape[:-1]).prod() ncoords = npoints def x(self): """Return the X-coordinates of all points. Returns an array with all the X-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with :: self[...,0] """ return self[...,0] def y(self): """Return the Y-coordinates of all points. Returns an array with all the Y-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with :: self[...,1] """ return self[...,1] def z(self): """Return the Z-coordinates of all points. Returns an array with all the Z-coordinates in the Coords. The returned array has the same shape as the Coords array along its first ndim-1 axes. This is equivalent with :: self[...,0] """ return self[...,2] # Size def bbox(self): """Return the bounding box of a set of points. The bounding box is the smallest rectangular volume in the global coordinates, such that no point of the :class:`Coords` are outside that volume. Returns: A Coords object with shape(2,3): the first point contains the minimal coordinates, the second has the maximal ones. Example: >>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).bbox() [[ 0. 0. 0.] [ 3. 3. 0.]] """ if self.size > 0: s = self.points() bbox = row_stack([ s.min(axis=0), s.max(axis=0) ]) else: o = origin() bbox = [o,o] return Coords(bbox) def center(self): """Return the center of the :class:`Coords`. The center of a :class:`Coords` is the center of its bbox(). The return value is a (3,) shaped :class:`Coords` object. Example: >>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).center() [ 1.5 1.5 0. ] See also: :meth:`centroid` """ X0,X1 = self.bbox() return 0.5 * (X0+X1) def average(self,wts=None,axis=0): """Return a (weighted) average of the :class:`Coords`. The average of a :class:`Coords` is a :class:`Coords` with one axis less than the original, obtained by averaging all the points along that axis. The weights array can either be 1-D (in which case its length must be the size along the given axis) or of the same shape as a. Weights can be specified as a 1-D array with the length of that axis, or as an array with the same shape as the :class:`Coords`. The sum of the weights (along the specified axis if not 1-D) will generally be equal to 1.0. If wts=None, then all points are assumed to have a weight equal to one divided by the length of the specified axis. Example: >>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],\ [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average() [[ 2. 0. 0.] [ 3. 0. 0.] [ 4. 0. 0.]] >>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],\ [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average(axis=1) [[ 1. 0. 0.] [ 5. 0. 0.]] >>> print Coords([[[0.,0.,0.],[1.,0.,0.],[2.,0.,0.]],\ [[4.,0.,0.],[5.,0.,0.],[6.,0.,0.]]]).average(wts=[0.5,0.25,0.25],axis=1) [[ 0.75 0. 0. ] [ 4.75 0. 0. ]] """ return average(self,weights=wts,axis=axis) def centroid(self): """Return the centroid of the :class:`Coords`. The centroid of a :class:`Coords` is the point whose coordinates are the mean values of all points. The return value is a (3,) shaped :class:`Coords` object. Example: >>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).centroid() [ 1. 1. 0.] See also: :meth:`center` """ return self.points().mean(axis=0) def centroids(self): return self def sizes(self): """Return the sizes of the :class:`Coords`. Return an array with the length of the bbox along the 3 axes. Example: >>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).sizes() [ 3. 3. 0.] """ X0,X1 = self.bbox() return X1-X0 def dsize(self): """Return an estimate of the global size of the :class:`Coords`. This estimate is the length of the diagonal of the bbox(). Example: >>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).dsize() 4.24264 """ X0,X1 = self.bbox() return length(X1-X0) def bsphere(self): """Return the diameter of the bounding sphere of the :class:`Coords`. The bounding sphere is the smallest sphere with center in the center() of the :class:`Coords`, and such that no points of the :class:`Coords` are lying outside the sphere. Example: >>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).bsphere() 2.12132 """ return self.distanceFromPoint(self.center()).max() # Inertia def inertia(self,mass=None): """Return inertia related quantities of the :class:`Coords`. This returns the center of gravity, the principal axes of inertia, the principal moments of inertia and the inertia tensor. """ from plugins import inertia if mass is not None: mass = mass.reshape(self.npoints(),1) ctr,I = inertia.inertia(self.points(),mass) Iprin,Iaxes = inertia.principal(I,sort=True,right_handed=True) return (ctr,Iaxes,Iprin,I) # Distance def distanceFromPlane(self,p,n): """Return the distance of all points from the plane (p,n). p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components. The return value is a float array with shape ``self.pshape()`` with the distance of each point to the plane through p and having normal n. Distance values are positive if the point is on the side of the plane indicated by the positive normal. Example: >>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromPlane([0.,0.,0.],[1.,0.,0.]) [[ 0. 3. 0.]] """ p = asarray(p).reshape((3)) n = asarray(n).reshape((3)) n = normalize(n) d = inner(self,n) - inner(p,n) return d def distanceFromLine(self,p,n): """Return the distance of all points from the line (p,n). p,n are (1,3) or (npts,3) arrays defining 1 or npts lines p is a point on the line specified by 3 coordinates. n is a vector specifying the direction of the line through p. The return value is a [...] shaped array with the distance of each point to the line through p with direction n. All distance values are positive or zero. Example: >>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromLine([0.,0.,0.],[1.,0.,0.]) [[ 0. 0. 3.]] """ p = asarray(p)#.reshape((3)) n = asarray(n)#.reshape((3)) t = cross(n,p-self) d = sqrt(sum(t*t,-1)) / length(n) return d def distanceFromPoint(self,p): """Return the distance of all points from the point p. p is a single point specified by 3 coordinates. The return value is a [...] shaped array with the distance of each point to point p. All distance values are positive or zero. Example: >>> print Coords([[[0.,0.,0.],[3.,0.,0.],[0.,3.,0.]]]).distanceFromPoint([0.,0.,0.]) [[ 0. 3. 3.]] """ p = asarray(p).reshape((3)) d = self-p return sqrt(sum(d*d,-1)) def closestToPoint(self,p): """Return the point closest to point p. """ d = self.distanceFromPoint(p) return self.points()[d.argmin()] def directionalSize(self,n,p=None,_points=False): """Return the extreme distances from the plane p,n. The direction n can be specified by a 3 component vector or by a single integer 0..2 designing one of the coordinate axes. p is any point in space. If not specified, it is taken as the center() of the Coords. The return value is a tuple of two float values specifying the extreme distances from the plane p,n. """ n = unitVector(n) if p is None: p = self.center() else: p = Coords(p) d = self.distanceFromPlane(p,n) dmin,dmax = d.min(),d.max() if _points: return [p+dmin*n, p+dmax*n] else: return dmin,dmax def directionalExtremes(self,n,p=None): """Return extremal planes in the direction n. `n` and `p` have the same meaning as in `directionalSize`. The return value is a list of two points on the line (p,n), such that the planes with normal n through these points define the extremal planes of the Coords. """ return self.directionalSize(n,p,_points=True) def directionalWidth(self,n): """Return the width of a Coords in the given direction. The direction can be specified by a 3 component vector or by a single integer 0..2 designating one of the coordinate axes. The return value is the thickness of the object in the direction n. """ dmin,dmax = self.directionalSize(n) return dmax-dmin # Test position def test(self,dir=0,min=None,max=None,atol=0.): """Flag points having coordinates between min and max. Tests the position of the points of the :class:`Coords` with respect to one or two planes. This method is very convenient in clipping a :class:`Coords` in a specified direction. In most cases the clipping direction is one of the global cooordinate axes, but a general direction may be used as well. Parameters: - `dir`: either a global axis number (0, 1 or 2) or a direction vector consisting of 3 floats. It specifies the direction in which the distances are measured. Default is the 0 (or x) direction. - `min`,`max`: position of the minimum and maximum clipping planes. If `dir` was specified as an integer (0,1,2), this is a single float value corresponding with the coordinate in that axis direction. Else, it is a point in the clipping plane with normal direction `dir`. One of the two clipping planes may be left unspecified. Returns: a 1D integer array with same length as the number of points. For each point the value is 1 (True) if the point is above the minimum clipping plane and below the maximum clipping plane, or 0 (False) otherwise. An unspecified clipping plane corresponds with an infinitely low or high value. The return value can directly be used as an index to obtain a :class:`Coords` with the points satisfying the test (or not). See the examples below. Example: >>> x = Coords([[0.,0.],[1.,0.],[0.,1.],[0.,2.]]) >>> print x.test(min=0.5) [False True False False] >>> t = x.test(dir=1,min=0.5,max=1.5) >>> print x[t] [[ 0. 1. 0.]] >>> print x[~t] [[ 0. 0. 0.] [ 1. 0. 0.] [ 0. 2. 0.]] """ if min is None and max is None: raise ValueError,"At least one of min or max have to be specified." if type(dir) == int: if not min is None: T1 = self[...,dir] > min - atol if not max is None: T2 = self[...,dir] < max + atol else: if not min is None: T1 = self.distanceFromPlane(min,dir) > - atol if not max is None: T2 = self.distanceFromPlane(max,dir) < atol if min is None: T = T2 elif max is None: T = T1 else: T = T1 * T2 return T ## THIS IS A CANDIDATE FOR THE LIBRARY ## (possibly in a more general arrayprint form) ## (could be common with calpy) def fprint(self,fmt="%10.3e %10.3e %10.3e"): """Formatted printing of a :class:`Coords` object. The supplied format should contain 3 formatting sequences for the three coordinates of a point. """ for p in self.points(): print(fmt % tuple(p)) ############################################################################## def set(self,f): """Set the coordinates from those in the given array.""" self[...] = f # do not be tempted to use self = f ! ############################################################################## # # Transformations that preserve the topology (but change coordinates) # # A. Affine transformations # # Scaling # Translation # Central Dilatation = Scaling + Translation # Rotation # Shear # Reflection # Affine # # The following methods return transformed coordinates, but by default # they do not change the original data. If the optional argument inplace # is set True, however, the coordinates are changed inplace. def scale(self,scale,dir=None,center=None,inplace=False): """Return a copy scaled with scale[i] in direction i. The scale should be a list of 3 scaling factors for the 3 axis directions, or a single scaling factor. In the latter case, dir (a single axis number or a list) may be given to specify the direction(s) to scale. The default is to produce a homothetic scaling. The center of the scaling, if not specified, is the global origin. If a center is specified, the result is equivalent to:: self.translate(-center).scale(scale,dir).translate(center) Example: >>> print Coords([1.,1.,1.]).scale(2) [ 2. 2. 2.] >>> print Coords([1.,1.,1.]).scale([2,3,4]) [ 2. 3. 4.] """ if center is not None: center = asarray(center) return self.trl(-center).scale(scale,dir).translate(center) if inplace: out = self else: out = self.copy() if dir is None: out *= scale else: out[...,dir] *= scale return out def translate(self,dir,step=None,inplace=False): """Translate a :class:`Coords` object. Translates the Coords in the direction `dir` over a distance `step * length(dir)`. Parameters: - `dir`: specifies the direction and distance of the translation. It can be either - an axis number (0,1,2), specifying a unit vector in the direction of one of the coordinate axes. - a single translation vector, - an array of translation vectors, compatible with the Coords shape. - `step`: If specified, the translation vector specified by `dir` will be multiplied with this value. It is commonly used with unit `dir` vectors to set the translation distance. Example: >>> x = Coords([1.,1.,1.]) >>> print x.translate(1) [ 1. 2. 1.] >>> print x.translate(1,1.) [ 1. 2. 1.] >>> print x.translate([0,1,0]) [ 1. 2. 1.] >>> print x.translate([0,2,0],0.5) [ 1. 2. 1.] """ if inplace: out = self else: out = self.copy() if type(dir) is int: dir = unitVector(dir) dir = Coords(dir,copy=True) if step is not None: dir *= step out += dir return out def centered(self): """Return a centered copy of the Coords. Returns a Coords which is a translation thus that the center coincides with the origin. This is equivalent with:: self.trl(-self.center()) """ return self.trl(-self.center()) def align(self,alignment='---'): """Align the Coords along the global axes. Alignment involves a translation such that the bounding box of the Coords object becomes aligned on the origin of the global axes. The requested alignment is determined by a string of three characters, one for each of the coordinate axes. The character determines how the structure is aligned in the corresponding direction: - '-': aligned on the minimal value of the bounding box, - '+': aligned on the maximal value of the bounding box, - '0': aligned on the middle value of the bounding box. Any other value will make the alignment in that direction unchanged. The default alignment string '---' results in a translation which puts all the points in the octant with all positive coordinate values. A string '000' will center the object around the origin, just like the (slightly faster) :meth:`centered` method, which is . """ trl = zeros(3) bb = self.bbox() al = { '-': bb[0], '+': bb[1], '0': 0.5*(bb[0]+bb[1]) } for i,c in enumerate(alignment): if c in al: trl[i] = -al[c][i] return self.translate(trl) def rotate(self,angle,axis=2,around=None): """Return a copy rotated over angle around axis. The angle is specified in degrees. The axis is either one of (0,1,2) designating the global axes, or a vector specifying an axis through the origin. If no axis is specified, rotation is around the 2(z)-axis. This is convenient for working on 2D-structures. As a convenience, the user may also specify a 3x3 rotation matrix, in which case the function rotate(mat) is equivalent to affine(mat). All rotations are performed around the point [0,0,0], unless a rotation origin is specified in the argument 'around'. """ mat = asarray(angle) if mat.size == 1: mat = rotationMatrix(angle,axis) if mat.shape != (3,3): raise ValueError,"Rotation matrix should be 3x3" if around is not None: around = asarray(around) out = self.translate(-around) else: out = self return out.affine(mat,around) def shear(self,dir,dir1,skew,inplace=False): """Return a copy skewed in the direction dir of plane (dir,dir1). The coordinate dir is replaced with (dir + skew * dir1). """ if inplace: out = self else: out = self.copy() out[...,dir] += skew * out[...,dir1] return out # THIS SHOULD BE GENERALIZED TO TAKE SAME `dir` OPTIONS AS translate def reflect(self,dir=0,pos=0.,inplace=False): """Reflect the coordinates in direction dir against plane at pos. Parameters: - `dir`: int: direction of the reflection (default 0) - `pos`: float: offset of the mirror plane from origin (default 0.0) - `inplace`: boolean: change the coordinates inplace (default False) """ if inplace: out = self else: out = self.copy() out[...,dir] = 2*pos - out[...,dir] return out def affine(self,mat,vec=None): """Returns a general affine transform of the :class:`Coords` object. `mat`: a 3x3 float matrix `vec`: a length 3 list or array of floats The returned object has coordinates given by ``self * mat + vec``. """ out = dot(self,mat) if vec is not None: out += vec return out def position(self,x,y): """Position an object so that points x are aligned with y. Parameters as for arraytools.trfMatrix """ r,t = trfMatrix(x,y) return self.affine(r,t) # # # B. Non-Affine transformations. # # These always return copies ! # # Cylindrical, Spherical, Isoparametric # def cylindrical(self,dir=[0,1,2],scale=[1.,1.,1.],angle_spec=Deg): """Converts from cylindrical to cartesian after scaling. dir specifies which coordinates are interpreted as resp. distance(r), angle(theta) and height(z). Default order is [r,theta,z]. scale will scale the coordinate values prior to the transformation. (scale is given in order r,theta,z). The resulting angle is interpreted in degrees. """ # We put in a optional scaling, because doing this together with the # transforming is cheaper than first scaling and then transforming. f = zeros_like(self) theta = (scale[1]*angle_spec) * self[...,dir[1]] r = scale[0] * self[...,dir[0]] f[...,0] = r*cos(theta) f[...,1] = r*sin(theta) f[...,2] = scale[2] * self[...,dir[2]] return f def hyperCylindrical(self,dir=[0,1,2],scale=[1.,1.,1.],rfunc=None,zfunc=None,angle_spec=Deg): if rfunc is None: rfunc = lambda x:1 if zfunc is None: zfunc = lambda x:1 f = zeros_like(self) theta = (scale[1]*angle_spec) * self[...,dir[1]] r = scale[0] * rfunc(theta) * self[...,dir[0]] f[...,0] = r * cos(theta) f[...,1] = r * sin(theta) f[...,2] = scale[2] * zfunc(theta) * self[...,dir[2]] return f def toCylindrical(self,dir=[0,1,2],angle_spec=Deg): """Converts from cartesian to cylindrical coordinates. dir specifies which coordinates axes are parallel to respectively the cylindrical axes distance(r), angle(theta) and height(z). Default order is [x,y,z]. The angle value is given in degrees. """ f = zeros_like(self) x,y,z = [ self[...,i] for i in dir ] f[...,0] = sqrt(x*x+y*y) f[...,1] = arctand2(y,x,angle_spec) f[...,2] = z return f def spherical(self,dir=[0,1,2],scale=[1.,1.,1.],angle_spec=Deg,colat=False): """Converts from spherical to cartesian after scaling. - `dir` specifies which coordinates are interpreted as resp. longitude(theta), latitude(phi) and distance(r). - `scale` will scale the coordinate values prior to the transformation. Angles are interpreted in degrees. Latitude, i.e. the elevation angle, is measured from equator in direction of north pole(90). South pole is -90. If colat=True, the third coordinate is the colatitude (90-lat) instead. """ f = self.reshape((-1,3)) theta = (scale[0]*angle_spec) * f[:,dir[0]] phi = (scale[1]*angle_spec) * f[:,dir[1]] r = scale[2] * f[:,dir[2]] if colat: phi = 90.0*angle_spec - phi rc = r*cos(phi) f = column_stack([rc*cos(theta),rc*sin(theta),r*sin(phi)]) return f.reshape(self.shape) def superSpherical(self,n=1.0,e=1.0,k=0.0, dir=[0,1,2],scale=[1.,1.,1.],angle_spec=Deg,colat=False): """Performs a superspherical transformation. superSpherical is much like spherical, but adds some extra parameters to enable the creation of virtually any surface. Just like with spherical(), the input coordinates are interpreted as the longitude, latitude and distance in a spherical coordinate system. `dir` specifies which coordinates are interpreted as resp. longitude(theta), latitude(phi) and distance(r). Angles are then interpreted in degrees. Latitude, i.e. the elevation angle, is measured from equator in direction of north pole(90). South pole is -90. If colat=True, the third coordinate is the colatitude (90-lat) instead. `scale` will scale the coordinate values prior to the transformation. The `n` and `e` parameters define exponential transformations of the north_south (latitude), resp. the east_west (longitude) coordinates. Default values of 1 result in a circle. `k` adds 'eggness' to the shape: a difference between the northern and southern hemisphere. Values > 0 enlarge the southern hemishpere and shrink the northern. """ def c(o,m): c = cos(o) return sign(c)*abs(c)**m def s(o,m): c = sin(o) return sign(c)*abs(c)**m f = self.reshape((-1,3)) theta = (scale[0]*angle_spec) * f[:,dir[0]] phi = (scale[1]*angle_spec) * f[:,dir[1]] r = scale[2] * f[:,dir[2]] if colat: phi = 90.0*angle_spec - phi rc = r*c(phi,n) if k != 0: # k should be > -1.0 !!!! x = sin(phi) rc *= (1-k*x)/(1+k*x) f = column_stack([rc*c(theta,e),rc*s(theta,e),r*s(phi,n)]) return f.reshape(self.shape) def toSpherical(self,dir=[0,1,2],angle_spec=Deg): """Converts from cartesian to spherical coordinates. `dir` specifies which coordinates axes are parallel to respectively the spherical axes distance(r), longitude(theta) and latitude(phi). Latitude is the elevation angle measured from equator in direction of north pole(90). South pole is -90. Default order is [0,1,2], thus the equator plane is the (x,y)-plane. The returned angle values are given in degrees. """ v = self[...,dir].reshape((-1,3)) dist = sqrt(sum(v*v,-1)) long = arctand2(v[:,0],v[:,2],angle_spec) lat = where(dist <= 0.0,0.0,arcsind(v[:,1]/dist,angle_spec)) f = column_stack([long,lat,dist]) return f.reshape(self.shape) def bump1(self,dir,a,func,dist): """Return a :class:`Coords` with a one-dimensional bump. - `dir` specifies the axis of the modified coordinates; - `a` is the point that forces the bumping; - `dist` specifies the direction in which the distance is measured; - `func` is a function that calculates the bump intensity from distance and should be such that ``func(0) != 0``. """ f = self.copy() d = f[...,dist] - a[dist] f[...,dir] += func(d)*a[dir]/func(0) return f def bump2(self,dir,a,func): """Return a :class:`Coords` with a two-dimensional bump. dir specifies the axis of the modified coordinates; a is the point that forces the bumping; func is a function that calculates the bump intensity from distance !! func(0) should be different from 0. """ f = self.copy() dist = [0,1,2] dist.remove(dir) d1 = f[...,dist[0]] - a[dist[0]] d2 = f[...,dist[1]] - a[dist[1]] d = sqrt(d1*d1+d2*d2) f[...,dir] += func(d)*a[dir]/func(0) return f # This is a generalization of both the bump1 and bump2 methods. # If it proves to be useful, it might replace them one day # An interesting modification might be to have a point for definiing # the distance and a point for defining the intensity (3-D) of the # modification def bump(self,dir,a,func,dist=None): """Return a :class:`Coords` with a bump. A bump is a modification of a set of coordinates by a non-matching point. It can produce various effects, but one of the most common uses is to force a surface to be indented by some point. dir specifies the axis of the modified coordinates; a is the point that forces the bumping; func is a function that calculates the bump intensity from distance (!! func(0) should be different from 0) dist is the direction in which the distance is measured : this can be one of the axes, or a list of one or more axes. If only 1 axis is specified, the effect is like function bump1 If 2 axes are specified, the effect is like bump2 This function can take 3 axes however. Default value is the set of 3 axes minus the direction of modification. This function is then equivalent to bump2. """ f = self.copy() if dist == None: dist = [0,1,2] dist.remove(dir) try: l = len(dist) except TypeError: l = 1 dist = [dist] d = f[...,dist[0]] - a[dist[0]] if l==1: d = abs(d) else: d = d*d for i in dist[1:]: d1 = f[...,i] - a[i] d += d1*d1 d = sqrt(d) f[...,dir] += func(d)*a[dir]/func(0) return f def flare (self,xf,f,dir=[0,2],end=0,exp=1.): """Create a flare at the end of a :class:`Coords` block. The flare extends over a distance ``xf`` at the start (``end=0``) or end (``end=1``) in direction ``dir[0]`` of the coords block, and has a maximum amplitude of ``f`` in the ``dir[1]`` direction. """ ix,iz = dir bb = self.bbox() if end == 0: xmin = bb[0][ix] endx = self.test(dir=ix,max=xmin+xf) func = lambda x: (1.-(x-xmin)/xf) ** exp else: xmax = bb[1][ix] endx = self.test(dir=ix,min=xmax-xf) func = lambda x: (1.-(xmax-x)/xf) ** exp x = self.copy() x[endx,iz] += f * func(x[endx,ix]) return x def map(self,func): """Return a :class:`Coords` mapped by a 3-D function. This is one of the versatile mapping functions. func is a numerical function which takes three arguments and produces a list of three output values. The coordinates [x,y,z] will be replaced by func(x,y,z). The function must be applicable to arrays, so it should only include numerical operations and functions understood by the numpy module. This method is one of several mapping methods. See also map1 and mapd. Example: >>> print Coords([[1.,1.,1.]]).map(lambda x,y,z: [2*x,3*y,4*z]) [[ 2. 3. 4.]] """ # flatten coordinate sets to ease use of complicated functions # we should probably do this for map1 and mapd too X = self.points() f = zeros_like(X) f[...,0],f[...,1],f[...,2] = func(X.x(),X.y(),X.z()) return f.reshape(self.shape) def map1(self,dir,func,x=None): """Return a :class:`Coords` where coordinate i is mapped by a 1-D function. `func` is a numerical function which takes one argument and produces one result. The coordinate dir will be replaced by func(coord[x]). If no x is specified, x is taken equal to dir. The function must be applicable on arrays, so it should only include numerical operations and functions understood by the numpy module. This method is one of several mapping methods. See also map and mapd. """ if x is None: x = dir f = self.copy() f[...,dir] = func(self[...,x]) return f def mapd(self,dir,func,point,dist=None): """Maps one coordinate by a function of the distance to a point. `func` a numerical function which takes one argument and produces one result. The coordinate `dir` will be replaced by ``func(d)``, where ``d`` is calculated as the distance to `point`. The function must be applicable on arrays, so it should only include numerical operations and functions understood by the :mod:`numpy` module. By default, the distance d is calculated in 3-D, but one can specify a limited set of axes to calculate a 2-D or 1-D distance. This method is one of several mapping methods. See also :meth:`map3` and :meth:`map1`. Example: E.mapd(2,lambda d:sqrt(10**2-d**2),f.center(),[0,1]) maps ``E`` on a sphere with radius 10. """ f = self.copy() if dist == None: dist = [0,1,2] try: l = len(dist) except TypeError: l = 1 dist = [dist] d = f[...,dist[0]] - point[dist[0]] if l==1: d = abs(d) else: d = d*d for i in dist[1:]: d1 = f[...,i] - point[i] d += d1*d1 d = sqrt(d) f[...,dir] = func(d) return f def egg(self,k): """Maps the coordinates to an egg-shape""" return (1-k*self)/(1+k*self) def replace(self,i,j,other=None): """Replace the coordinates along the axes i by those along j. i and j are lists of axis numbers or single axis numbers. replace ([0,1,2],[1,2,0]) will roll the axes by 1. replace ([0,1],[1,0]) will swap axes 0 and 1. An optionally third argument may specify another :class:`Coords` object to take the coordinates from. It should have the same dimensions. """ if other is None: other = self f = self.copy() f[...,i] = other[...,j] return f def swapAxes(self,i,j): """Swap coordinate axes i and j. Beware! This is different from numpy's swapaxes() method ! """ return self.replace([i,j],[j,i]) def rollAxes(self,n=1): """Roll the axes over the given amount. Default is 1, thus axis 0 becomes the new 1 axis, 1 becomes 2 and 2 becomes 0. """ return roll(self, int(n) % 3,axis=-1) def projectOnPlane(self,n=2,P=[0.,0.,0.]): """Project a :class:`Coords` on a plane (or planes). Parameters: - `n`: the normal direction to the plane. It can be specified either by a list of three floats, or by a single integer (0, 1 or 2) to use one of the global axes. - `P`: a point on the plane, by default the global origin. If an int, the plane is the coordinate plane perpendicular to the ..note: For planes parallel to a coordinate plane, it is far more efficient to specify the normal by an axis number than by a three component vector. .. note: This method will also work if any or both of P and n have a shape (ncoords,3), where ncoords is the total number of points in the :class:`Coords`. This allows to project each point on an individual plane. Returns: a :class:`Coords` with same shape as original, with all the points projected on the specified plane(s). """ if type(n) is int: x = self.copy() x[...,n] = P[n] return x n = normalize(Coords(n).reshape(-1,3)) x = self.reshape(-1,3) x = - dotpr(n,(x-P)) return self + outer(x,n).reshape(self.shape) def projectOnSphere(self,radius=1.,center=[0.,0.,0.]): """Project :class:`Coords` on a sphere. The default sphere is a unit sphere at the origin. The center of the sphere should not be part of the :class:`Coords`. """ d = self.distanceFromPoint(center) s = radius / d f = self - center for i in range(3): f[...,i] *= s f += center return f def projectOnCylinder(self,radius=1.,dir=0,center=[0.,0.,0.]): """Project :class:`Coords` on a cylinder with axis parallel to a global axis. The default cylinder has its axis along the x-axis and a unit radius. No points of the :class:`Coords` should belong to the axis.. """ d = self.distanceFromLine(center,unitVector(dir)) s = radius / d c = resize(asarray(center),self.shape) c[...,dir] = self[...,dir] f = self - c axes = range(3) del axes[dir] for i in axes: f[...,i] *= s f += c return f def projectOnSurface(self,S,n,ignore_errors=False): """Project the Coords on a triangulated surface. The points of the Coords are projected in the direction of the vector n onto the surface S. Parameters: - `S`: TriSurface: any triangulated surface - `n`: int or vector: specifies the direction of the projection - `ignore_errors`: if True, projective lines not cutting the surface will result in NaN values. The default is to raise an error. If successful, a Coords with the same structure as the input is returned. """ x = self.reshape(-1,3) # Create planes through x in direction n # WE SHOULD MOVE THIS TO arraytools? from geomtools import anyPerpendicularVector v1 = anyPerpendicularVector(n) v2 = cross(n,v1) # Create set of cuts with set of planes print type(S) cuts = [ S.intersectionWithPlane(xi,v1) for xi in x ] # cut the cuts with second set of planes points = [ c.toFormex().intersectionWithPlane(xi,v2) for c,xi in zip(cuts,x) ] if ignore_errors : points = [ p for p in points if p.shape[0] > 0 ] else: npts = [ p.shape[0] for p in points ] if min(npts) == 0: print npts raise RuntimeError,"Some line has no intersection point" # find the points closest to self points = [ p.closestToPoint(xi) for p,xi in zip(points,x) ] return Coords.concatenate(points) # Extra transformations implemented by plugins def isopar(self,eltype,coords,oldcoords): """Perform an isoparametric transformation on a Coords. This is a convenience method to transform a Coords object through an isoparametric transformation. It is equivalent to:: Isopar(eltype,coords,oldcoords).transform(self) See :mod:`plugins.isopar` for more details. """ from plugins.isopar import Isopar return Isopar(eltype,coords,oldcoords).transform(self) def transformCS(self,currentCS,initialCS=None): """Perform a CoordinateSystem transformation on the Coords. This method transforms the Coords object by the transformation that turns the initial CoordinateSystem into the currentCoordinateSystem. currentCS and initialCS are CoordinateSystem or (4,3) shaped Coords instances. If initialCS is None, the global (x,y,z) axes are used. E.g. the default initialCS and currentCS equal to:: 0. 1. 0. -1. 0. 0. 0. 0. 1. 0. 0. 0. result in a rotation of 90 degrees around the z-axis. This is a convenience function equivalent to:: self.isopar('tet4',currentCS,initialCS) """ # This is currently implemented using isopar, but could # obviously also be done using affine return self.isopar('tet4',currentCS,initialCS) def addNoise(self,rsize=0.1,asize=0.0): """Add random noise to a Coords. A random amount is added to eacho individual coordinate in the Coords. The difference of any coordinate from its original value will not be maximally ``asize + rsize * self.sizes().max()``. The default is to set it to 0.1 times the geometrical size of the structure. """ max = asize + rsize * self.sizes().max() return self + randomNoise(self.shape,-max,+max) ############################################################################ # # Transformations that change the shape of the Coords array # def replicate(self,n,dir=0,step=None): """Replicate a Coords n times with fixed step in any direction. Returns a Coords object with shape `(n,) + self.shape`, thus having an extra first axis. Each component along the axis 0 is equal to the previous component translated over `(dir,step)`, where `dir` and `step` are interpreted just like in the :meth:`translate` method. The first component along the axis 0 is identical to the original Coords. """ n = int(n) if type(dir) is int: dir = unitVector(dir) dir = Coords(dir,copy=True) if step is not None: dir *= step f = resize(self,(n,)+self.shape) for i in range(1,n): f[i] += i*dir return Coords(f) def split(self): """Split the coordinate array in blocks along first axis. The result is a sequence of arrays with shape self.shape[1:]. Raises an error if self.ndim < 2. """ if self.ndim < 2: raise ValueError,"Can only split arrays with dim >= 2" return [ self[i] for i in range(self.shape[0]) ] def fuse(self,nodesperbox=1,shift=0.5,rtol=1.e-5,atol=1.e-5,repeat=True): """Find (almost) identical nodes and return a compressed set. This method finds the points that are very close and replaces them with a single point. The return value is a tuple of two arrays: - the unique points as a :class:`Coords` object with shape (npoints,3) - an integer (nnod) array holding an index in the unique coordinates array for each of the original nodes. This index will have the same shape as the pshape() of the coords array. The procedure works by first dividing the 3D space in a number of equally sized boxes, with a mean population of nodesperbox. The boxes are numbered in the 3 directions and a unique integer scalar is computed, that is then used to sort the nodes. Then only nodes inside the same box are compared on almost equal coordinates, using the numpy allclose() function. Two coordinates are considered close if they are within a relative tolerance rtol or absolute tolerance atol. See numpy for detail. The default atol is set larger than in numpy, because pyformex typically runs with single precision. Close nodes are replaced by a single one. Running the procedure once does not guarantee to find all close nodes: two close nodes might be in adjacent boxes. The performance hit for testing adjacent boxes is rather high, and the probability of separating two close nodes with the computed box limits is very small. Therefore, the most sensible way is to run the procedure twice, with a different shift value (they should differ more than the tolerance). Specifying repeat=True will automatically do this. """ if self.size == 0: # allow empty coords sets return self,array([],dtype=Int).reshape(self.pshape()) if repeat: # Aplly twice with different shift value coords,index = self.fuse(nodesperbox,shift,rtol,atol,repeat=False) coords,index2 = coords.fuse(nodesperbox,shift+0.25,rtol,atol,repeat=False) index = index2[index] return coords,index # This is the single pass x = self.points() nnod = x.shape[0] # Calculate box size lo = array([ x[:,i].min() for i in range(3) ]) hi = array([ x[:,i].max() for i in range(3) ]) sz = hi-lo esz = sz[sz > 0.0] # only keep the nonzero dimensions if esz.size == 0: # All points are coincident x = x[:1] e = zeros(nnod,dtype=int32) return x,e vol = esz.prod() # avoid error message on the global sz/nx calculation errh = seterr(all='ignore') nboxes = nnod // nodesperbox # ideal total number of boxes boxsz = (vol/nboxes) ** (1./esz.shape[0]) nx = (sz/boxsz).astype(int32) dx = where(nx>0,sz/nx,boxsz) seterr(**errh) # nx = array(nx) + 1 ox = lo - dx*shift # origin : 0 < shift < 1 # Create box coordinates for all nodes ind = floor((x-ox)/dx).astype(int32) # Create unique box numbers in smallest direction first o = argsort(nx) val = ( ind[:,o[2]] * nx[o[2]] + ind[:,o[1]] ) * nx[o[1]] + ind[:,o[0]] # sort according to box number srt = argsort(val) # rearrange the data according to the sort order val = val[srt] x = x[srt] # now compact # make sure we use int32 (for the fast fuse function) # Using int32 limits this procedure to 10**9 points, which is more # than enough for all practical purposes x = x.astype(float32) val = val.astype(int32) tol = float32(max(abs(rtol*self.sizes()).max(),atol)) nnod = val.shape[0] flag = ones((nnod,),dtype=int32) # 1 = new, 0 = existing node # new fusing algorithm sel = arange(nnod).astype(int32) # replacement unique node nr misc._fuse(x,val,flag,sel,tol) # fuse the close points x = x[flag>0] # extract unique nodes s = sel[argsort(srt)] # and indices for old nodes return (x,s.reshape(self.shape[:-1])) def match(self,coords,**kargs): """Match points form another Coords object. This method finds the points from `coords` that coincide with (or are very close to) points of `self`. Parameters: - `coords`: a Coords object - `**kargs`: keyword arguments that you want to pass to the :meth:`fuse` method. This method works by concatenating the serialized point sets of both Coords and then fusing them. Returns: - `matches`: an Int array with shape (nmatches,2) - `coords`: a Coords with the fused coordinate set - `index`: an index with the position of each of the serialized points of the concatenation in the fused coordinate set. To find the index of the points of the orginal coordinate sets, split this index at the position self.npoints() and reshape the resulting parts to `self.pshape()`, resp. `coords.pshape()`. """ x = Coords.concatenate([self.points(),coords.points()]) c,e = x.fuse(**kargs) e0,e1 = e[:self.npoints()],e[self.npoints():] matches = matchIndex(e0,e1) return matches def append(self,coords): """Append coords to a Coords object. The appended coords should have matching dimensions in all but the first axis. Returns the concatenated Coords object, without changing the current. This is comparable to :func:`numpy.append`, but the result is a :class:`Coords` object, the default axis is the first one instead of the last, and it is a method rather than a function. """ return Coords(append(self,coords,axis=0)) @classmethod def concatenate(clas,L,axis=0): """Concatenate a list of :class:`Coords` object. All :class:`Coords` object in the list L should have the same shape except for the length of the specified axis. This function is equivalent to the numpy concatenate, but makes sure the result is a :class:`Coords` object,and the default axis is the first one instead of the last. """ return Coords(concatenate(atleast_2d(*L),axis=axis)) @classmethod def fromstring(clas,fil,sep=' ',ndim=3,count=-1): """Create a :class:`Coords` object with data from a string. This convenience function uses the :func:`numpy.fromstring` function to read coordinates from a string. `fil`: a string containing a single sequence of float numbers separated by whitespace and a possible separator string. `sep`: the separator used between the coordinates. If not a space, all extra whitespace is ignored. `ndim`: number of coordinates per point. Should be 1, 2 or 3 (default). If 1, resp. 2, the coordinate string only holds x, resp. x,y values. `count`: total number of coordinates to read. This should be a multiple of 3. The default is to read all the coordinates in the string. count can be used to force an error condition if the string does not contain the expected number of values. The return value is Coords object. """ x = fromstring(fil,dtype=Float,sep=sep,count=count) if count > 0 and x.size != count : raise RuntimeError,"Number of coordinates read: %s, expected %s!" % (x.size,count) if x.size % ndim != 0 : raise RuntimeError,"Number of coordinates read: %s, expected a multiple of %s!" % (x.size,ndim) return Coords(x.reshape(-1,ndim)) @classmethod def fromfile(clas,fil,**kargs): """Read a :class:`Coords` from file. This convenience function uses the numpy fromfile function to read the coordinates from file. You just have to make sure that the coordinates are read in order (X,Y,Z) for subsequent points, and that the total number of coordinates read is a multiple of 3. """ x = fromfile(fil,dtype=Float,**kargs) if x.size % 3 != 0 : raise RuntimeError,"Number of coordinates read: %s, should be multiple of 3!" % x.size return Coords(x.reshape(-1,3)) def interpolate(self,X,div): """Create interpolations between two :class:`Coords`. Parameters: - `X`: a :class:`Coords` with same shape as `self`. - `div`: a list of floating point values, or an int. If an int is specified, a list with (div+1) values for `div` is created by dividing the interval [0..1] into `div` equal distances. Returns: A :class:`Coords` with an extra (first) axis, containing the concatenation of the interpolations of `self` and `X` at all values in `div`. Its shape is (n,) + self.shape, where n is the number of values in `div`. An interpolation of F and G at value v is a :class:`Coords` H where each coordinate Hijk is obtained from: Fijk = Fijk + v * (Gijk-Fijk). Thus, X.interpolate(Y,[0.,0.5,1.0]) will contain all points of X and Y and all points with mean coordinates between those of X and Y. F.interpolate(G,n) is equivalent with F.interpolate(G,arange(0,n+1)/float(n)) """ if self.shape != X.shape: raise RuntimeError,"`X` should have same shape as `self`" if type(div) == int: div = arange(div+1) / float(div) else: div = array(div).ravel() return self + outer(div,X-self).reshape((-1,)+self.shape) # Convenient shorter notations rot = rotate trl = translate rep = replicate def actor(self,**kargs): """_This allows a Coords object to be drawn directly""" if self.npoints() == 0: return None from gui.actors import GeomActor from formex import Formex return GeomActor(Formex(self.reshape(-1,3)),**kargs) class BoundVectors(Coords): """A collection of bound vectors in a 3D Cartesian space. Parameters: - `coords`: a (...,2,3) shaped array of bound vectors defined by their initial and terminal points. - `origins`,`vectors`: (...,3) shaped arrays defining the initial points and vectors from initial to terminal points. The default constructs a unit vector along the global x-axis. """ def __new__(clas,coords=None,origins=None,vectors=None): """Initialize the BoundVectors.""" if coords is None: coords = eye(2,3,-1) if vectors is not None: coords = resize(coords,vectors.shape[:-1]+(2,3)) coords[...,1,:] = vectors if origins is not None: coords += origins[...,newaxis,:] elif coords.shape[-2:] != (2,3): raise ValueError,"Expected shape (2,3) for last two array axes." return Coords.__new__(clas,coords) def origins(self): """Return the initial points of the BoundVectors.""" return Coords(self[...,0,:]) def heads(self): """Return the endpoints of the BoundVectors.""" return Coords(self[...,1,:]) def vectors(self): """Return the vectors of the BoundVectors.""" return Coords(self.heads()-self.origins()) class CoordinateSystem(Coords): """A CoordinateSystem defines a coordinate system in 3D space. The coordinate system is defined by and stored as a set of four points: three endpoints of the unit vectors along the axes at the origin, and the origin itself as fourth point. The constructor takes a (4,3) array as input. The default constructs the standard global Cartesian axes system: 1. 0. 0. 0. 1. 0. 0. 0. 1. 0. 0. 0. """ def __new__(clas,coords=None,origin=None,axes=None): """Initialize the CoordinateSystem""" if coords is None: coords = eye(4,3) if axes is not None: coords[:3] = axes if origin is not None: coords += origin else: coords = checkArray(coords,(4,3),'f','i') coords = Coords.__new__(clas,coords) return coords def origin(self): """Return the origin of the CoordinateSystem.""" return Coords(self[3]) def axes(self): """Return the axes of the CoordinateSystem.""" return Coords(self[:3]-self[3]) def actor(self,**kargs): """_This allows a CoordinateSystem object to be drawn directly.""" from gui.actors import AxesActor return AxesActor(self,**kargs) # Creating special coordinate sets def origin(): """Return a single point with coordinates [0.,0.,0.]. Returns: A :class:`Coords` object with shape(3,) holding three zero coordinates. """ return Coords(zeros((3),dtype=Float)) def pattern(s,connect=True): """Return a series of points lying on a regular grid. This function creates a series of points that lie on a regular grid with unit step. These points are created from a string input, interpreting each character as a code specifying how to move to the next point. The start position is always the origin (0.,0.,0.). Currently the following codes are defined: - 0: goto origin (0.,0.,0.) - 1..8: move in the x,y plane - 9: remain at the same place (and duplicate the last point) - A..I: same as 1..9 plus step +1. in z-direction - a..i: same as 1..9 plus step -1. in z-direction - /: do not insert the next point Any other character raises an error. When looking at the x,y-plane with the x-axis to the right and the y-axis up, we have the following basic moves: 1 = East, 2 = North, 3 = West, 4 = South, 5 = NE, 6 = NW, 7 = SW, 8 = SE. Adding 16 to the ordinal of the character causes an extra move of +1. in the z-direction. Adding 48 causes an extra move of -1. This means that 'ABCDEFGHI', resp. 'abcdefghi', correspond with '123456789' with an extra z +/-= 1. This gives the following schema:: z+=1 z unchanged z -= 1 F B E 6 2 5 f b e | | | | | | C----I----A 3----9----1 c----i----a | | | | | | G D H 7 4 8 g d h The special character '/' can be put before any character to make the move without inserting the new point. You need to start the string with a '0' or '9' to include the origin in the output. Returns a list with the generated points as integers. Example: >>> pattern('0123') [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)] """ warn("warn_pattern") x = y = z = 0 l = [] insert = True for c in s: if c == "/": insert = False continue if c == "0": x = y = z = 0 else: i = ord(c) d = i/16 if d == 3: pass elif d == 4: z += 1 elif d == 6: z -= 1 else: raise RuntimeError,"Unknown character '%c' in pattern input" % c i %= 16 if i == 1: x += 1 elif i == 2: y += 1 elif i == 3: x -= 1 elif i == 4: y -= 1 elif i == 5: x += 1 y += 1 elif i == 6: x -= 1 y += 1 elif i == 7: x -= 1 y -= 1 elif i == 8: x += 1 y -= 1 elif i == 9: pass else: raise RuntimeError,"Unknown pattern character %c ignored" % c if insert: l.append((x,y,z)) insert = True return l def xpattern(s,nplex=1): """Create a Coords object from a string pattern. This is like pattern, but allows grouping the points into elements. First, the string is expanded to a list of points by calling pattern(s). Then the resulting list of points is transformed in a 2D table of points where each row has the length `nplex`. If the number of points produced by `s` is not a multiple of `nplex`, an error is raised. """ x = Coords(pattern(s)) try: return x.reshape(-1,nplex,3) except: raise ValueError,"Could not reshape points list to plexitude %s" % nplex def sweepCoords(self,path,origin=[0.,0.,0.],normal=0,upvector=2,avgdir=False,enddir=None,scalex=None,scaley=None): """ Sweep a Coords object along a path, returning a series of copies. origin and normal define the local path position and direction on the mesh. At each point of the curve, a copy of the Coords object is created, with its origin in the curve's point, and its normal along the curve's direction. In case of a PolyLine, directions are pointing to the next point by default. If avgdir==True, average directions are taken at the intermediate points avgdir can also be an array like sequence of shape (N,3) to explicitely set the the directions for ALL the points of the path Missing end directions can explicitely be set by enddir, and are by default taken along the last segment. enddir is a list of 2 array like values of shape (3). one of the two can also be an empty list If the curve is closed, endpoints are treated as any intermediate point, and the user should normally not specify enddir. At each point of the curve, the original Coords object can be scaled in x and y direction by specifying scalex and scaley. The number of values specified in scalex and scaly should be equal to the number of points on the curve. The return value is a sequence of the transformed Coords objects. """ points = path.coords if isinstance(avgdir,bool): if avgdir: directions = path.avgDirections() else: directions = path.directions() else: directions=asarray(avgdir).reshape(len(avgdir),-1) missing = points.shape[0] - directions.shape[0] if missing == 1: lastdir = (points[-1] - points[-2]).reshape(1,3) directions = concatenate([directions,lastdir],axis=0) elif missing == 2: lastdir = (points[-1] - points[-2]).reshape(1,3) firstdir = (points[1] - points[0]).reshape(1,3) directions = concatenate([firstdir,directions,lastdir],axis=0) if enddir: for i,j in enumerate([0,-1]): if enddir[i]: directions[j] = Coords(enddir[i]) directions = normalize(directions) if type(normal) is int: normal = unitVector(normal) if type(upvector) is int: upvector = Coords(unitVector(upvector)) if scalex is not None: if len(scalex) != points.shape[0]: raise ValueError,"The number of scale values in x-direction differs from the number of copies that will be created." else: scalex = ones(points.shape[0]) if scaley is not None: if len(scaley) != points.shape[0]: raise ValueError,"The number of scale values in y-direction differs from the number of copies that will be created." else: scaley = ones(points.shape[0]) base = self.translate(-Coords(origin)) sequence = [ base.scale([scx,scy,1.]).rotate(vectorRotation(normal,d,upvector)).translate(p) for scx,scy,d,p in zip(scalex,scaley,directions,points) ] return sequence ############################################################################## # # Testing # # Some of the docstrings above hold test examples. They should be careflly # crafted to test the functionality of the Formex class. # # Ad hoc test examples during development can be added to the test() function # below. # # python formex.py # will execute the docstring examples silently. # python formex.py -v # will execute the docstring examples verbosely. # In both cases, the ad hoc tests are only run if the docstring tests # are passed. # if __name__ == "__main__": def testX(X): """Run some tests on:class:`Coords` X.""" def prt(s,v): """Print a statement 's = v' and return v""" if isinstance(v,ndarray): sep = '\n' else: sep = ' ' print("%s =%s%s" % (s,sep,v)) return v prt("###################################\nTests for Coords X",X) # Info prt("points",X.points()) prt("pshape",X.pshape()) prt("npoints",X.npoints()) prt("y",X.y()) prt("bbox",X.bbox()) prt("center",X.center()) prt("centroid",X.centroid()) prt("sizes",X.sizes()) prt("dsize",X.dsize()) prt("bsphere",X.bsphere()) prt("distanceFromPlane",X.distanceFromPlane([0.,0.,1.],[0.,0.,1.])) prt("distanceFromLine",X.distanceFromLine([0.,0.,1.],[0.,0.,1.])) prt("distanceFromPoint",X.distanceFromPoint([0.,0.,1.])) prt("test",X.test(dir=1,min=0.5,max=1.5)) prt("test2",X.test(dir=[1.,1.,0.],min=[0.,0.5,0.],max=[0.,1.5,0.])) # Transforms prt("X_scl",X.scale(2,False)) prt("X",X) prt("X_scl",X.scale(2,True)) prt("X",X) prt("X_scl2",X.scale([0.5,1.,0.])) prt("X_trl",X.copy().translate(0,6)) prt("X_trl2",X.translate([10.,100.,1000.])) prt("X_rot",X.rotate(90.)) prt("X_rot2",X.rotate(90.,0)) Y=prt("Y = X_reflect",X.reflect(1,2)) prt("X_bbox",X.bbox()) prt("Y_bbox",Y.bbox()) prt("(X+Y)bbox",bbox([X,Y])) Z = X.copy().reflect(1,1.5).translate(1,2) prt("X",X) prt("Y",Y) prt("Z",Z) G = Coords.concatenate([X,Z,Y,Z],axis=0) prt("X+Z+Y+Z",G) return def test(): """Run the tests. This is intended for tests during development and can be changed at will. """ testX(Coords([[1,0,0],[0,1,0]])) testX(Coords([[[0,0,0],[1,0,0]],[[0,1,0],[1,1,0]]])) testX(Coords([1,0,0])) try: testX(Coords()) except: print "Some test(s) failed for an empty Coords" print "But that surely is no surprise" return f = 0 #import doctest, formex #f,t = doctest.testmod(formex) if f == 0: test() ### End pyformex-0.8.6/pyformex/mydict.py0000644000211500021150000003041711705104656016760 0ustar benebene00000000000000#!/usr/bin/python # $Id: mydict.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """ Extensions to Pythons built-in dictionary class: Dict is a dictionary with default values and alternate attribute syntax. CDict is a Dict with lookup cascading into the next level Dict's if the key is not found in the CDict itself. (C) 2005,2008 Benedict Verhegghe Distributed under the GNU GPL version 3 or later """ import copy def cascade(d, key): """Cascading lookup in a dictionary. This is equivalent to the dict lookup, except that when the key is not found, a cascading lookup through lower level dict's is started and the first matching key found is returned. """ try: return dict.__getitem__(d,key) except KeyError: for v in d.itervalues(): if isinstance(v,dict): try: return cascade(v,key) except KeyError: pass raise KeyError def returnNone(key): """Always returns None.""" return None def raiseKeyError(key): """Raise a KeyError.""" raise KeyError,"Not found: %s" % key class Dict(dict): """A Python dictionary with default values and attribute syntax. :class:`Dict` is functionally nearly equivalent with the builtin Python :class:`dict`, but provides the following extras: - Items can be accessed with attribute syntax as well as dictionary syntax. Thus, if ``C`` is a :class:`Dict`, ``C['foo']`` and ``C.foo`` are equivalent. This works as well for accessing values as for setting values. In the following, the terms *key* or *attribute* therefore have the same meaning. - Lookup of a nonexisting key/attribute does not automatically raise an error, but calls a ``_default_`` lookup method which can be set by the user. The default is to raise a KeyError, but an alternative is to return None or some other default value. There are a few caveats though: - Keys that are also attributes of the builtin dict type, can not be used with the attribute syntax to get values from the Dict. You should use the dictionary syntax to access these items. It is possible to set such keys as attributes. Thus the following will work:: C['get'] = 'foo' C.get = 'foo' print(C['get']) but this will not:: print(C.get) This is done so because we want all the dict attributes to be available with their normal binding. Thus, :: print(C.get('get')) will print ``foo`` To avoid name clashes with user defines, many Python internal names start and end with '__'. The user should avoid such names. The Python dict has the following attributes not enclosed between '__', so these are the ones to watch out for: 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'. """ def __init__(self,data={},default=None): """Create a new Dict instance. The Dict can be initialized with a Python dict or a Dict. If defined, default is a function that is used for alternate key lookup if the key was not found in the dict. """ dict.__init__(self,data.items()) if default is None: default = raiseKeyError if not callable(default): raise ValueError,"'default' should be a callable function" self.__dict__['_default_'] = default def __repr__(self): """Format the Dict as a string. We use the format Dict({}), so that the string is a valid Python representation of the Dict. """ return "Dict(%s)" % dict.__repr__(self) def __getitem__(self, key): """Allows items to be addressed as self[key]. This is equivalent to the dict lookup, except that we provide a default value if the key does not exist. """ try: return dict.__getitem__(self, key) except KeyError: return self._default_(key) def __delitem__(self,key): """Allow items to be deleted using del self[key]. Silently ignore if key is nonexistant. """ try: dict.__delitem__(self,key) except KeyError: pass def __getattr__(self,key): """Allows items to be addressed as self.key. This makes self.key equivalent to self['key'], except if key is an attribute of the builtin type 'dict': then we return that attribute instead, so that the 'dict' methods keep their binding. """ try: return dict.__getattribute__(self,key) except AttributeError: if key == '_default_': return self.__dict__['_default_'] else: return self.__getitem__(key) def __setattr__(self,key,value=None): """Allows items to be set as self.key=value. This works even if the key is an existing attribute of the builtin dict class: the key,value pair is stored in the dict, leaving the dict's attributes unchanged. """ self.__setitem__(key,value) def __delattr__(self,key): """Allow items to be deleted using del self.key. This works even if the key is an existing attribute of the builtin dict class: the item is deleted from the dict, leaving the dict's attributes unchanged. """ self.__delitem__(key) def update(self,data={}): """Add a dictionary to the Dict object. The data can be a dict or Dict type object. """ dict.update(self,data) def get(self, key, default): """Return the value for key or a default. This is the equivalent of the dict get method, except that it returns only the default value if the key was not found in self, and there is no _default_ method or it raised a KeyError. """ try: return self[key] except KeyError: return default # Added this to keep pydoc happy. Probably we should redefine # this one instead of get? #__get__ = get ### !!!!!!!! We had to remove it to keep the functionality ### of the module!!!!! def setdefault(self, key, default): """Replaces the setdefault function of a normal dictionary. This is the same as the get method, except that it also sets the default value if get found a KeyError. """ try: return self[key] except KeyError: self[key] = default return default def __deepcopy__(self,memo): """Create a deep copy of ourself.""" newdict = self.__class__(default=self._default_) for k,v in self.items(): newdict[k] = copy.deepcopy(v,memo) return newdict ## def __getstate__(self): ## d = copy.copy(self.__dict__) ## d.update(self) ## return d ## def __setstate__(self,d): ## self.__dict__['_default_'] = d.pop('_default_') ## self.update(d) def __reduce__(self): state = (dict(self), self.__dict__) return (__newobj__, (self.__class__,), state) def __setstate__(self,state): if type(state) == tuple: self.update(state[0]) self.__dict__.update(state[1]) elif type(state) == dict: self.__dict__['_default_'] = state.pop('_default_') self.update(state) def __newobj__(cls, *args): return cls.__new__(cls, *args) _indent = 0 # number of spaces to indent in __str__ formatting # incremented by 2 on each level class CDict(Dict): """A cascading Dict: properties not in Dict are searched in all Dicts. This is equivalent to the Dict class, except that if a key is not found and the CDict has items with values that are themselves instances of Dict or CDict, the key will be looked up in those Dicts as well. As you expect, this will make the lookup cascade into all lower levels of CDict's. The cascade will stop if you use a Dict. There is no way to guarantee in which order the (Cascading)Dict's are visited, so if multiple Dicts on the same level hold the same key, you should know yourself what you are doing. """ def __init__(self,data={},default=returnNone): Dict.__init__(self,data,default) def __repr__(self): """Format the CDict as a string. We use the format Dict({}), so that the string is a valid Python representation of the Dict. """ return "CDict(%s)" % dict.__repr__(self) def __str__(self): """Format a CDict into a string.""" global _indent s = "" _indent += 2 for i in self.items(): s += '\n' + (' '*_indent) + "%s = %s" % i _indent -= 2 return s def __getitem__(self, key): """Allows items to be addressed as self[key]. This is equivalent to the dict lookup, except that we cascade through lower level dict's. """ try: return cascade(self, key) except KeyError: return self._default_(key) if __name__ == '__main__': import cPickle as pickle global C,Cr,Cs def val(s,typ='s'): """Returns a string assigning the value of s to the name s.""" try: return ("%s = %"+typ) % (s,eval(s)) except: return "Error in %s" % s def show(): """Print C with '%r' and '%s' formatting.""" global C,Cr,Cs Cr = val('C','r') Cs = val('C','s') print(Cr) print(Cs) print(C.get('y','yorro')) print(C.get('z','zorro')) print(C.setdefault('w','worro')) print("%s = %s" % (C['y']['c'],C.y.c)) def testpickle(): global C print("Test (un)pickle") f = open('test.pickle','w') print(type(C)) print(C._default_) pickle.dump(C,f) f.close() f = open('test.pickle','r') C = pickle.load(f) print(type(C)) print(C._default_) f.close() C = Dict({'x':Dict({'a':1,'y':Dict({'b':5,'c':6})}),'y':Dict({'c':3,'d':4}),'d':0}) show() testpickle() show() # now exec this to check if we get the same exec(Cr) show() # now replace Dict with CDict Cr = Cr.replace('Dict','CDict') exec(Cr) show() testpickle() show() # show some items print(val("C['a'],C['b'],C['c'],C['d'],C['x']['c']")) print(val("C['e']")) print(val("C.a,C.b,C.c,C.d,C.x.c")) print(val("C.e")) C = CDict({'a':'aa','d':{'a':'aaa','b':'bbb'}}) print(C) print(C['a']) print(C['d']) print(C['b']) D = copy.deepcopy(C) print(D) print(D.__dict__) print("%s == %s" % (C['b'],D['b']) ) C = Dict({'a':'aa','d':{'a':'aaa','b':'bbb'}}) print(C) print(C['a']) print(C['d']) try: print(C['b']) print("This should provoke a KeyError, so you should not see this text") except: print("Correctly received the intended KeyError") D = copy.deepcopy(C) print(D) print(D.__dict__) # End pyformex-0.8.6/pyformex/collection.py0000644000211500021150000001207311705104656017620 0ustar benebene00000000000000# $Id: collection.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Tools for handling collections of elements belonging to multiple parts. This module defines the Collection class. """ import pyformex as pf from arraytools import * ################# Collection of Actors or Actor Elements ############### class Collection(object): """A collection is a set of (int,int) tuples. The first part of the tuple has a limited number of values and are used as the keys in a dict. The second part can have a lot of different values and is implemented as an integer array with unique values. This is e.g. used to identify a set of individual parts of one or more OpenGL actors. """ def __init__(self): self.d = {} self.obj_type = None def setType(self,obj_type): self.obj_type = obj_type def clear(self,keys=[]): if keys: for k in keys: k = int(k) if k in self.d.keys(): del self.d[k] else: self.d = {} def add(self,data,key=-1): """Add new data to the collection. data can be a 2d array with (key,val) tuples or a 1-d array of values. In the latter case, the key has to be specified separately, or a default value will be used. """ if len(data) == 0: return data = asarray(data) if data.ndim == 2: for key in unique(data[:,0]): self.add(data[data[:,0]==key,1],key) else: key = int(key) data = unique(data) if self.d.has_key(key): self.d[key] = union1d(self.d[key],data) elif data.size > 0: self.d[key] = data def set(self,data,key=-1): """Set the collection to the specified data. This is equivalent to clearing the corresponding keys before adding. """ self.clear() self.add(data,key) def remove(self,data,key=-1): """Remove data from the collection.""" data = asarray(data) if data.ndim == 2: for key in unique(data[:,0]): self.remove(data[data[:,0]==key,1],key) else: key = int(key) if self.d.has_key(key): data = setdiff1d(self.d[key],unique(data)) if data.size > 0: self.d[key] = data else: del self.d[key] else: pf.debug("Not removing from non-existing selection for actor %s" % key) def has_key(self,key): """Check whether the collection has an entry for the key.""" return self.d.has_key(key) def __setitem__(self,key,data): """Set new values for the given key.""" key = int(key) data = unique(data) if data.size > 0: self.d[key] = data else: del self.d[key] def __getitem__(self,key): """Return item with given key.""" return self.d[key] def get(self,key,default=[]): """Return item with given key or default.""" key = int(key) return self.d.get(key,default) def keys(self): """Return a sorted array with the keys""" k = asarray(self.d.keys()) k.sort() return k def items(self): """Return a zipped list of keys and values.""" return self.d.items() def __str__(self): s = '' keys = self.d.keys() keys.sort() for k in keys: s += "%s %s; " % (k,self.d[k]) return s ################# Testing ############### if __name__ == "__main__": print("Testing the Collection object") a = Collection() a.add(range(7),3) a.add(range(4)) a.remove([2,4],3) print(a) a.add([[2,0],[2,3],[-1,7],[3,88]]) print(a) a[2] = [1,2,3] print(a) a[2] = [] print(a) a.set([[2,0],[2,3],[-1,7],[3,88]]) print(a) # End pyformex-0.8.6/pyformex/formex.py0000644000211500021150000023714011705104656016771 0ustar benebene00000000000000# $Id: formex.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Formex algebra in Python This module defines the :class:`Formex` class, which is the major class for representing geometry in pyFormex. The Formex class implements most functionality of Formex algebra in a consistent and easy to understand syntax. """ from coords import * from utils import deprecation,functionBecameMethod from geometry import Geometry import re def lpattern(s,connect=True): """Return a line segment pattern created from a string. This function creates a list of line segments where all points lie on a regular grid with unit step. The first point of the list is [0,0,0]. Each character from the input string is interpreted as a code specifying how to move to the next point. Currently defined are the following codes: 1..8 move in the x,y plane 9 remains at the same place 0 = goto origin [0,0,0] + = go back to origin without creating a line segment When looking at the plane with the x-axis to the right, 1 = East, 2 = North, 3 = West, 4 = South, 5 = NE, 6 = NW, 7 = SW, 8 = SE. Adding 16 to the ordinal of the character causes an extra move of +1 in the z-direction. Adding 48 causes an extra move of -1. This means that 'ABCDEFGHI', resp. 'abcdefghi', correspond with '123456789' with an extra z +/-= 1. This gives the following schema:: z+=1 z unchanged z -= 1 F B E 6 2 5 f b e | | | | | | C----I----A 3----9----1 c----i----a | | | | | | G D H 7 4 8 g d h The special character '/' can be put before any character to make the move without inserting an element. The effect of any other character is undefined. The resulting list is directly suited to initialize a Formex. """ # We do not allow the '+' anymore s = s.replace('+','/0') x = y = z = 0 l = [] insert = True for c in s: if c == "/": insert = False continue ## if c == "+": ## x = y = z = 0 ## continue pos = [x,y,z] if c == "0": x = y = z = 0 else: i = ord(c) d = i/16 if d == 3: pass elif d == 4: z += 1 elif d == 6: z -= 1 else: raise RuntimeError,"Unknown pattern character %c ignored" % c i %= 16 if i == 1: x += 1 elif i == 2: y += 1 elif i == 3: x -= 1 elif i == 4: y -= 1 elif i == 5: x += 1 y += 1 elif i == 6: x -= 1 y += 1 elif i == 7: x -= 1 y -= 1 elif i == 8: x += 1 y -= 1 elif i == 9: pass else: raise RuntimeError,"Unknown pattern character %c ignored" % c if insert: if connect: element = [pos,[x,y,z]] else: element = [[x,y,z]] l.append(element) insert = True return l @deprecation("\nFunction mpattern() is deprecated: use xpattern() instead.") def mpattern(s): """This is like pattern, but allowing lists with more than 2 points. Subsequent points are included in the same list until a '-' occurs. A '+' or '-' character splits lists. After a '-', the list starts at the last point of the previous list. After a '+', the list starts again at the origin. All lists should have equal length if you want to use the resulting list to initialize a Formex. """ x = y = z = 0 li = [[x,y,z]] l = [] connect=True for c in s: if c == '/': connect = False continue elif c == '+': l.append(li) li = [] x = y = z = 0 elif c == '-': l.append(li) li = [] elif c == '0': x = y = z = 0 else: i = ord(c) d = i/16 if d == 3: pass elif d == 4: z += 1 elif d == 6: z -= 1 else: raise RuntimeError,"Unknown pattern character %c ignored" % c i %= 16 if i == 1: x += 1 elif i == 2: y += 1 elif i == 3: x -= 1 elif i == 4: y -= 1 elif i == 5: x += 1 y += 1 elif i == 6: x -= 1 y += 1 elif i == 7: x -= 1 y -= 1 elif i == 8: x += 1 y -= 1 elif i == 9: pass else: raise RuntimeError,"Unknown pattern character %c ignored" % c if connect: li.append([x,y,z]) l.append(li) return l # Intersection functions # REMOVED in 0.9 ## @deprecation("\nUse Formex.intersectionWithPlane() or geomtools functions instead.") ## def intersectionWithPlane(F,p,n): ## """Return the intersection of a Formex F with the plane (p,n). ## The Formex should have plexitude 2. ## p is a point specified by 3 coordinates. ## n is the normal vector to a plane, specified by 3 components. ## The return value is a [n] shaped array of parameter values t, ## such that for each segment L the intersection point is given ## by (1-t)*L[0]+ t*L[1]. ## """ ## f = F.coords ## if f.shape[1] != 2: ## raise RuntimeError,"Formex should have plexitude 2." ## p = asarray(p).reshape((3)) ## n = asarray(n).reshape((3)) ## n = normalize(n) ## t = (inner(p,n) - inner(f[:,0,:],n)) / inner((f[:,1,:]-f[:,0,:]),n) ## return t def pointsAt(F,t): """Return the points of a plex-2 Formex at times t. F is a plex 2 Formex and t is an array with F.nelems() float values which are interpreted as local parameters along the edges of the Formex, such that the first node has value 0.0 and the last has value 1.0. The return value is a :class:`coords.Coords` array with the points at values t. """ f = F.coords t = t[:,newaxis] return Coords((1.-t) * f[:,0,:] + t * f[:,1,:]) # REMOVED in 0.9 ## @deprecation("\nUse Formex.intersectionWithPlane() or geomtools functions instead.") ## def intersectionPointsWithPlane(F,p,n): ## """Return the intersection points of a Formex with plane p,n. ## The Formex should have plexitude 2. ## p is a point specified by 3 coordinates. ## n is the normal vector to a plane, specified by 3 components. ## The result is a plex-1 Formex with the same number of elements as the ## original. Some of the points may be NaN's. ## """ ## f = F.coords ## t = intersectionWithPlane(F,p,n).reshape((-1,1)) ## #print t.shape ## from geomtools import intersectionTimesSWP ## t = intersectionTimesSWP(f,p,n,mode='pair').reshape((-1,1)) ## #print t.shape ## return Formex((1.-t) * f[:,0,:] + t * f[:,1,:]) def intersectionLinesWithPlane(F,p,n,atol=1.e-4): """Return the intersection lines of a plex-3 Formex with plane (p,n). F is a Formex of plexitude 3. p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components. atol is a tolerance factor defining whether an edge is intersected by the plane. """ n = asarray(n) p = asarray(p) F = F.cclip(F.test('all',n,p)) # remove elements at the negative side if F.nelems() == 0: return Formex(empty((0,2,3,),dtype=float)) F = F.cclip(F.test('all',-n,p)) # select elements that will be cut by plane if F.nelems() == 0: return Formex(empty((0,2,3,),dtype=float)) F1 = F21 = F22 = F31 = F32 = F41 = F42= F43 = Formex(empty((0,2,3,),dtype=float)) # Create a Formex with the edges C = Formex.concatenate([ F.selectNodes(e) for e in [[0,1],[1,2],[2,0]] ]) t = C.intersectionWithPlane(p,n) P = pointsAt(C,t) t = t.reshape(3,-1).transpose() Pb = P.reshape(3,-1,3).swapaxes(0,1) Pf = F.coords Ps = roll(F.coords,-1,axis=1) t1 = t >= 0.+atol t2 = t <= 1.-atol t3 = t >= 0.-atol t4 = t <= 1.+atol Tb = t1 * t2 Tf = (1-t1).astype(bool)*t3 Ts = (1-t2).astype(bool)*t4 Nb = Tb.sum(axis=-1) Nf = Tf.sum(axis=-1) Ns = Ts.sum(axis=-1) # Get the triangles with 2 edge intersections w1 = where(Nb==2)[0] if w1.size > 0: P = Pb[w1][Tb[w1]].reshape(-1,2,3) F1 = Formex(P) # Get the triangles with 1 edge intersection and 1 vertex intersection w21 = where( (Nb==1) * (Nf==1) * (Ns==0) )[0] if w21.size > 0: P1 = Pb[w21][Tb[w21]].reshape(-1,1,3) P2 = Pf[w21][Tf[w21]].reshape(-1,1,3) P = column_stack([P1,P2]) F21 = Formex(P) w22 = where( (Nb==1) * (Nf==0) * (Ns==1) )[0] if w22.size > 0: P1 = Pb[w22][Tb[w22]].reshape(-1,1,3) P2 = Ps[w22][Ts[w22]].reshape(-1,1,3) P = column_stack([P1,P2]) F22 = Formex(P) # Get the triangles with 1 edge intersection and 2 vertex intersections w3 = where( (Nb==1) * (Nf==1) * (Ns==1) )[0] if w3.size > 0: Tb3 = Tb[w3] Tf3 = Tf[w3] Ts3 = Ts[w3] Pb3 = Pb[w3] Pf3 = Pf[w3] Ps3 = Ps[w3] i = where(Ts3)[1] - where(Tf3)[1] w31 = where((i == 1)+(i==-2))[0] # different vertices if w31.size > 0: P1 = Pf3[w31][Tf3[w31]].reshape(-1,1,3) P2 = Ps3[w31][Ts3[w31]].reshape(-1,1,3) P = column_stack([P1,P2]) F32 = Formex(P) w32 = where((i == -1)+(i==2))[0] # equal vertices if w32.size > 0: P1 = Pb3[w32][Tb3[w32]].reshape(-1,1,3) P2 = Pf3[w32][Tf3[w32]].reshape(-1,1,3) P = column_stack([P1,P2]) F31 = Formex(P) # Get the triangles with 0 edge intersections and 2 or 3 vertex intersections w41 = where( (Nb==0) * (Nf==2) )[0] if w41.size > 0: P = Pf[w41][Tf[w41]].reshape(-1,2,3) F41 = Formex(P) w42 = where( (Nb==0) * (Ns==2) )[0] if w42.size > 0: P = Ps[w42][Ts[w42]].reshape(-1,2,3) F42 = Formex(P) w43 = where( (Nb==0) * (Nf==1) * (Ns==1) )[0] if w43.size > 0: Tf43 = Tf[w43] Ts43= Ts[w43] Pf43 = Pf[w43] Ps43 = Ps[w43] i = where(Ts43)[1] - where(Tf43)[1] w43 = where((i == 1)+(i==-2))[0] # different vertices if w43.size > 0: P1 = Pf43[w43][Tf43[w43]].reshape(-1,1,3) P2 = Ps43[w43][Ts43[w43]].reshape(-1,1,3) P = column_stack([P1,P2]) F43 = Formex(P) # join all the pieces Ft = F1 + F21 + F22 + F31 + F32 + F41 + F42+ F43 return Ft def _sane_side(side): """_Allow some old variants of arguments_""" if type(side) == str: if side.startswith('pos'): side = '+' if side.startswith('neg'): side = '-' if not (side == '+' or side == '-'): side = '' return side def _select_side(side,alist): """_Return selected parts dependent on side_""" if side == '+': return alist[0] elif side == '-': return alist[1] else: return alist def cut2AtPlane(F,p,n,side='',atol=None,newprops=None): """Returns all elements of the Formex cut at plane. F is a Formex of plexitude 2. p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components. The return value is: - with side = '+' or '-' or 'positive'or 'negative' : a Formex of the same plexitude with all elements located completely at the positive/negative side of the plane(s) (p,n) retained, all elements lying completely at the negative/positive side removed and the elements intersecting the plane(s) replaced by new elements filling up the parts at the positive/negative side. - with side = '': two Formices of the same plexitude, one representing the positive side and one representing the negative side. To avoid roundoff errors and creation of very small elements, a tolerance can be specified. Points lying within the tolerance distance will be considered lying in the plane, and no cutting near these points. """ side = _sane_side(side) dist = F.distanceFromPlane(p,n) if atol is None: atol = 1.e-5*dist.max() above = sum(dist>atol,-1) below = sum(dist<-atol,-1) A = F.clip(below==0) B = F.clip(above==0) cutting = (above>0)*(below>0) if newprops: A.setProp(newprops[0]) B.setProp(newprops[1]) ## print("Elements in F: %s" % F.nelems()) ## print("Elements in A: %s" % A.nelems()) ## print("Elements in B: %s" % B.nelems()) if cutting.any(): G = F.clip(cutting) H = G.copy() g = G.intersectionWithPlane(p,n) dist = dist[cutting] i0 = dist[:,0] < 0. i1 = dist[:,1] < 0. G[i0,0,:] = H[i0,1,:] = g[i0].reshape(-1,3) G[i1,1,:] = H[i1,0,:] = g[i1].reshape(-1,3) if newprops: G.setProp(newprops[2]) H.setProp(newprops[3]) A += G B += H ## print("Elements in G: %s" % G.nelems()) ## print("Elements in A: %s" % A.nelems()) ## print("Elements in B: %s" % B.nelems()) return _select_side(side,[ A,B ]) def cut3AtPlane(F,p,n,side='',atol=None,newprops=None): """Returns all elements of the Formex cut at plane(s). F is a Formex of plexitude 3. p is a point or a list of points. n is the normal vector to a plane or a list of normal vectors. Both p and n have shape (3) or (npoints,3). The return value is: - with side = '+' or '-' or 'positive'or 'negative' : a Formex of the same plexitude with all elements located completely at the positive/negative side of the plane(s) (p,n) retained, all elements lying completely at the negative/positive side removed and the elements intersecting the plane(s) replaced by new elements filling up the parts at the positive/negative side. - with side = '': two Formices of the same plexitude, one representing the positive side and one representing the negative side. Let :math:`dist` be the signed distance of the vertices to a plane. The elements located completely at the positive or negative side of a plane have three vertices for which :math:`|dist| > atol`. The elements intersecting a plane can have one or more vertices for which :math:`|dist| < atol`. These vertices are projected on the plane so that their distance is zero. If the Formex has a property set, the new elements will get the property numbers defined in newprops. This is a list of 7 property numbers flagging elements with following properties: 0) no vertices with :math:`|dist| < atol`, triangle after cut 1) no vertices with :math:`|dist| < atol`, triangle 1 from quad after cut 2) no vertices with :math:`|dist| < atol`, triangle 2 from quad after cut 3) one vertex with :math:`|dist| < atol`, two vertices at pos. or neg. side 4) one vertex with :math:`|dist| < atol`, one vertex at pos. side, one at neg. 5) two vertices with :math:`|dist| < atol`, one vertex at pos. or neg. side 6) three vertices with :math:`|dist| < atol` """ if atol is None: atol = 1.e-5*F.dsize() # make sure we have sane newprops if newprops is None: newprops = [None,]*7 else: try: newprops = newprops[:7] for prop in newprops: if not (prop is None or type(prop) is int): raise except: newprops = range(7) side = _sane_side(side) p = asarray(p).reshape(-1,3) n = asarray(n).reshape(-1,3) nplanes = len(p) test = [F.test('any',n[i], p[i],atol=atol) for i in range(nplanes)] # elements at positive side of plane i Test= asarray(test).prod(0) # elements at positive side of all planes F_pos = F.clip(Test) # save elements at positive side of all planes if side in '-': # Dirty trick: this also includes side='' ! F_neg = F.cclip(Test) # save elements completely at negative side of one of the planes else: F_neg = None if F_pos.nelems() != 0: test = [F_pos.test('all',n[i],p[i],atol=-atol) for i in range(nplanes)] # elements completely at positive side of plane i Test = asarray(test).prod(0) # elements completely at positive side of all planes F_cut = F_pos.cclip(Test) # save elements that will be cut by one of the planes F_pos = F_pos.clip(Test) # save elements completely at positive side of all planes if F_cut.nelems() != 0: if nplanes == 1: if side == '+': F_pos += cutElements3AtPlane(F_cut,p[i],n[i],newprops,side,atol) elif side == '-': F_neg += cutElements3AtPlane(F_cut,p[i],n[i],newprops,side,atol) elif side == '': cut_pos, cut_neg = cutElements3AtPlane(F_cut,p[i],n[i],newprops,side,atol) F_pos += cut_pos F_neg += cut_neg elif nplanes > 1: S = F_cut for i in range(nplanes): if i > 0: # due to the projection of vertices with |distance| < atol on plane i-1, some elements can be completely at negative side of plane i instead of cut by plane i t = S.test('any',n[i],p[i],atol=atol) if side in '-': F_neg += S.cclip(t) # save elements completely at negative side of plane i S = S.clip(t) # save elements at positive side of plane i t = S.test('all',n[i],p[i],atol=-atol) R = S.clip(t) # save elements completely at positive side of plane i S = S.cclip(t) # save elements that will be cut by plane i if side == '+': cut_pos = cutElements3AtPlane(S,p[i],n[i],newprops,'+',atol) elif side in '-': cut_pos, cut_neg = cutElements3AtPlane(S,p[i],n[i],newprops,'',atol) F_neg += cut_neg S = R + cut_pos F_pos += S return _select_side(side,[ F_pos, F_neg ]) def cutElements3AtPlane(F,p,n,newprops=None,side='',atol=0.): """This function needs documentation. Should it be called by the user? or only via cut3AtPlane? For now, lets suppose the last, so no need to check arguments here. newprops should be a list of 7 values: each an integer or None side is either '+', '-' or '' """ if atol is None: atol = 1.e-5*F.dsize() def get_new_prop(p,ind,newp): """Determines the value of the new props for a subset. p are the original props (possibly None) ind is the list of elements to treat newp is the new property value. The return value is determined as follows: - If p is None: return None (no property set) - If p is set, but newp is None: return p[ind] : keep original - if p is set, and newp is set: return newp (single value) """ if p is None: return None elif newp is None: return p[ind] else: return newp from geomtools import intersectionTimesSWP,intersectionPointsSWP C = [connect([F,F],nodid=ax) for ax in [[0,1],[1,2],[2,0]]] t = column_stack([intersectionTimesSWP(Ci.coords,p,n,mode='pair') for Ci in C]) P = stack([intersectionPointsSWP(Ci.coords,p,n,mode='pair',return_all=True) for Ci in C],axis=1) T = (t >= 0.)*(t <= 1.) d = F.coords.distanceFromPlane(p,n) U = abs(d) < atol V = U.sum(axis=-1) # number of vertices with |distance| < atol F1_pos = F2_pos = F3_pos = F4_pos = F5_pos = F6_pos = F7_pos = F1_neg = F2_neg = F3_neg = F4_neg = F5_neg = F6_neg = F7_neg = Formex() # No vertices with |distance| < atol => triangles with 2 intersections w1 = where(V==0)[0] if w1.size > 0: T1 = T[w1] P1 = P[w1][T1].reshape(-1,2,3) F1 = F[w1] d1 = d[w1] if F.prop is None: p1 = None else: p1 = F.prop[w1] # split problem in two cases w11 = where(d1[:,0]*d1[:,1]*d1[:,2] > 0.)[0] # case 1: triangle at positive side after cut w12 = where(d1[:,0]*d1[:,1]*d1[:,2] < 0.)[0] # case 2: quadrilateral at positive side after cut # case 1: triangle at positive side after cut if w11.size > 0: T11 = T1[w11] P11 = P1[w11] F11 = F1[w11] if side in '+': v1 = where(T11[:,0]*T11[:,2] == 1,0,where(T11[:,0]*T11[:,1] == 1,1,2)) K1 = asarray([F11[j,v1[j]] for j in range(shape(F11)[0])]).reshape(-1,1,3) E1_pos = column_stack([P11,K1]) F1_pos = Formex(E1_pos,get_new_prop(p1,w11,newprops[0])) if side in '-': #quadrilateral at negative side after cut v2 = where(T11[:,0]*T11[:,2] == 1,2,where(T11[:,0]*T11[:,1] == 1,2,0)) v3 = where(T11[:,0]*T11[:,2] == 1,1,where(T11[:,0]*T11[:,1] == 1,0,1)) K2 = asarray([F11[j,v2[j]] for j in range(shape(F11)[0])]).reshape(-1,1,3) K3 = asarray([F11[j,v3[j]] for j in range(shape(F11)[0])]).reshape(-1,1,3) E2_neg = column_stack([P11,K2]) F2_neg = Formex(E2_neg,get_new_prop(p1,w11,newprops[1])) E3_neg = column_stack([P11[:,0].reshape(-1,1,3),K2,K3]) F3_neg = Formex(E3_neg,get_new_prop(p1,w11,newprops[2])) # case 2: quadrilateral at positive side after cut if w12.size > 0: T12 = T1[w12] P12 = P1[w12] F12 = F1[w12] if side in '+': v2 = where(T12[:,0]*T12[:,2] == 1,2,where(T12[:,0]*T12[:,1] == 1,2,0)) v3 = where(T12[:,0]*T12[:,2] == 1,1,where(T12[:,0]*T12[:,1] == 1,0,1)) K2 = asarray([F12[j,v2[j]] for j in range(shape(F12)[0])]).reshape(-1,1,3) K3 = asarray([F12[j,v3[j]] for j in range(shape(F12)[0])]).reshape(-1,1,3) E2_pos = column_stack([P12,K2]) F2_pos = Formex(E2_pos,get_new_prop(p1,w12,newprops[1])) E3_pos = column_stack([P12[:,0].reshape(-1,1,3),K2,K3]) F3_pos = Formex(E3_pos,get_new_prop(p1,w12,newprops[2])) if side in '-': # triangle at negative side after cut v1 = where(T12[:,0]*T12[:,2] == 1,0,where(T12[:,0]*T12[:,1] == 1,1,2)) K1 = asarray([F12[j,v1[j]] for j in range(shape(F12)[0])]).reshape(-1,1,3) E1_neg = column_stack([P12,K1]) F1_neg = Formex(E1_neg,get_new_prop(p1,w12,newprops[0])) # One vertex with |distance| < atol w2 = where(V==1)[0] if w2.size > 0: F2 = F[w2] d2 = d[w2] U2 = U[w2] if F.prop is None: p2 = None else: p2 = F.prop[w2] # split problem in three cases W = (d2 > atol).sum(axis=-1) w21 = where(W == 2)[0] # case 1: two vertices at positive side w22 = where(W == 1)[0] # case 2: one vertex at positive side w23 = where(W == 0)[0] # case 3: no vertices at positive side # case 1: two vertices at positive side if w21.size > 0 and side in '+': F21 = F2[w21] U21 = U2[w21] K1 = F21[U21] # vertices with |distance| < atol n = normalize(n) K1 = (K1 - n*d2[w21][U21].reshape(-1,1)).reshape(-1,1,3) # project vertices on plane (p,n) K2 = F21[d2[w21]>atol].reshape(-1,2,3) # vertices with distance > atol E4_pos = column_stack([K1,K2]) F4_pos = Formex(E4_pos,get_new_prop(p2,w21,newprops[3])) # case 2: one vertex at positive side if w22.size > 0: F22 = F2[w22] U22 = U2[w22] K1 = F22[U22] # vertices with |distance| < atol K1 = (K1 - n*d2[w22][U22].reshape(-1,1)).reshape(-1,1,3) # project vertices on plane (p,n) P22 = P[w2][w22][roll(U22,1,axis=-1)].reshape(-1,1,3) # intersection points if side in '+': K2 = F22[d2[w22]>atol].reshape(-1,1,3) # vertices with distance > atol E5_pos = column_stack([P22,K1,K2]) F5_pos = Formex(E5_pos,get_new_prop(p2,w22,newprops[4])) if side in '-': K3 = F22[d2[w22]<-atol].reshape(-1,1,3) # vertices with distance < - atol E5_neg = column_stack([P22,K1,K3]) F5_neg = Formex(E5_neg,get_new_prop(p2,w22,newprops[4])) # case 3: no vertices at positive side if w23.size > 0 and side in '-': F23 = F2[w23] U23 = U2[w23] K1 = F23[U23] # vertices with |distance| < atol K1 = (K1 - n*d2[w23][U23].reshape(-1,1)).reshape(-1,1,3) # project vertices on plane (p,n) K2 = F23[d2[w23]<-atol].reshape(-1,2,3) # vertices with distance < - atol E4_neg = column_stack([K1,K2]) F4_neg = Formex(E4_neg,get_new_prop(p2,w23,newprops[3])) # Two vertices with |distance| < atol w3 = where(V==2)[0] if w3.size > 0: F3 = F[w3] d3 = d[w3] U3 = U[w3] # split problem in two cases W = (d3 > atol).sum(axis=-1) w31 = where(W == 1)[0] # case 1: one vertex at positive side w32 = where(W == 0)[0] # case 2: no vertices at positive side # case 1: one vertex at positive side if w31.size > 0 and side in '+': F31 = F3[w31] U31 = U3[w31] K1 = F31[U31] # vertices with |distance| < atol K1 = (K1 - n*d3[w31][U31].reshape(-1,1)).reshape(-1,2,3) # project vertices on plane (p,n) K2 = F31[d3[w31]>atol].reshape(-1,1,3) # vertices with distance > atol E6_pos = column_stack([K1,K2]) F6_pos = Formex(E6_pos,get_new_prop(F.prop,w31,newprops[5])) # case 2: no vertices at positive side if w32.size > 0 and side in '-': F32 = F3[w32] U32 = U3[w32] K1 = F32[U32] # vertices with |distance| < atol K1 = (K1 - n*d3[w32][U32].reshape(-1,1)).reshape(-1,2,3) # project vertices on plane (p,n) K2 = F32[d3[w32]<-atol].reshape(-1,1,3) # vertices with distance < - atol E6_neg = column_stack([K1,K2]) F6_neg = Formex(E6_neg,get_new_prop(F.prop,w32,newprops[5])) # Three vertices with |distance| < atol w4 = where(V==3)[0] if w4.size > 0: F4 = F[w4] d4 = d[w4] U4 = U[w4] if side in '+': K1 = F4[U4] # vertices with |distance| < atol K1 = (K1 - n*d4[U4].reshape(-1,1)).reshape(-1,3,3) # project vertices on plane (p,n) E7_pos = K1 F7_pos = Formex(E7_pos,get_new_prop(F.prop,w4,newprops[6])) if side in '-': E7_neg = K1 F7_neg = Formex(E7_neg,get_new_prop(F.prop,w4,newprops[6])) # join all the pieces if side in '+': cut_pos = F1_pos+F2_pos+F3_pos+F4_pos+F5_pos+F6_pos+F7_pos if side in '-': cut_neg = F1_neg+F2_neg+F3_neg+F4_neg+F5_neg+F6_neg+F7_neg if side == '+': return cut_pos elif side == '-': return cut_neg else: return [ cut_pos, cut_neg ] ########################################################################### ## ## Formex class ## ######################### # class Formex(Geometry): """A Formex is a numpy array of order 3 (axes 0,1,2) and type Float. A scalar element represents a coordinate (F:uniple). A row along the axis 2 is a set of coordinates and represents a point (node, vertex, F: signet). For simplicity's sake, the current implementation only deals with points in a 3-dimensional space. This means that the length of axis 2 is always 3. The user can create formices (plural of Formex) in a 2-D space, but internally these will be stored with 3 coordinates, by adding a third value 0. All operations work with 3-D coordinate sets. However, a method exists to extract only a limited set of coordinates from the results, permitting to return to a 2-D environment A plane along the axes 2 and 1 is a set of points (F: cantle). This can be thought of as a geometrical shape (2 points form a line segment, 3 points make a triangle, ...) or as an element in FE terms. But it really is up to the user as to how this set of points is to be interpreted. Finally, the whole Formex represents a set of such elements. Additionally, a Formex may have a property set, which is an 1-D array of integers. The length of the array is equal to the length of axis 0 of the Formex data (i.e. the number of elements in the Formex). Thus, a single integer value may be attributed to each element. It is up to the user to define the use of this integer (e.g. it could be an index in a table of element property records). If a property set is defined, it will be copied together with the Formex data whenever copies of the Formex (or parts thereof) are made. Properties can be specified at creation time, and they can be set, modified or deleted at any time. Of course, the properties that are copied in an operation are those that exist at the time of performing the operation. Because the :class:`Formex` class is derived from :class:`Geometry`, the following :class:`Formex` methods exist and return the value of the same method applied on the :attr:`coords` attribute: `x`, `y`, `z`, `bbox`, `center`, `centroid`, `sizes`, `dsize`, `bsphere`, `distanceFromPlane`, `distanceFromLine`, `distanceFromPoint`, `directionalSize`, `directionalWidth`, `directionalExtremes`, `__str__`. Refer to the correponding :class:`Coords` method for their usage. Also, the following :class:`Coords` transformation methods can be directly applied to a :class:`Formex` object or a derived class object. The return value is a new object identical to the original, except for the coordinates, which will have been transformed by the specified method. Refer to the correponding :class:`Coords` method for the usage of these methods: `scale`, `translate`, `rotate`, `shear`, `reflect`, `affine`, `cylindrical`, `hyperCylindrical`, `toCylindrical`, `spherical`, `superSpherical`, `toSpherical`, `bump`, `bump1`, `bump2`, `flare`, `map`, `map1`, `mapd`, `newmap`, `replace`, `swapAxes`, `rollAxes`, `projectOnSphere`, `projectOnCylinder`, `rot`, `trl`. """ ########################################################################### # # Create a new Formex # def __init__(self,data=[],prop=None,eltype=None): """Create a new Formex. The Formex data can be initialized by another Formex, by a 2D or 3D coordinate list, or by a string to be used in one of the pattern functions to create a coordinate list. If 2D coordinates are given, a 3-rd coordinate 0.0 is added. Internally, Formices always work with 3D coordinates. Thus: ``F = Formex([[[1,0],[0,1]],[[0,1],[1,2]]])`` creates a Formex with two elements, each having 2 points in the global z-plane. If a prop argument is specified, the setProp() function will be called to assign the properties. """ if isinstance(data,Formex): if prop is None: prop = data.prop if eltype is None: eltype = data.eltype data = data.coords else: if type(data) == str: d = re.compile("(((?P[^:]*):)?(?P.*))").match(data).groupdict() base,data = d['base'],d['data'] if base is None or base == 'l': data = lpattern(data) elif base == 'm': data = mpattern(data) else: try: nplex = int(base) data = xpattern(data,nplex) except: raise ValueError,"Invalid string data for Formex" data = asarray(data).astype(Float) if data.size == 0: ### MAYBE THIS SHOULD BE CHANGED ????? if len(data.shape) == 3: nplex = data.shape[1] elif len(data.shape) == 2: nplex = 1 else: nplex = 0 data = data.reshape(0,nplex,3) # An empty Formex else: # check dimensions of data if not len(data.shape) in [1,2,3]: raise RuntimeError,"Formex init: needs a 1-, 2- or 3-dim. data array, got shape %s" % str(data.shape) if len(data.shape) == 1: data = data.reshape(1,1,data.shape[0]) elif len(data.shape) == 2: data = data.reshape(data.shape[0],1,data.shape[1]) if not data.shape[-1] in [1,2,3]: raise RuntimeError,"Formex init: last axis dimension of data array should be 1, 2 or 3, got shape %s" % str(data.shape) # add 3-rd dimension if data are 1-d or 2-d # this will be done by Coords pass if data.shape[-1] == 2: z = zeros((data.shape[0],data.shape[1],1),dtype=Float) data = concatenate([data,z],axis=-1) # data should be OK now self.coords = Coords(data) # make sure coordinates are a Coords object self.setProp(prop) try: self.eltype = eltype.lower() except: if eltype is not None: utils.warn("Formex eltype currently needs to be a string!") self.eltype = None def _set_coords(self,coords): """Replace the current coords with new ones. """ coords = Coords(coords) if coords.shape == self.coords.shape: return Formex(coords,self.prop,self.eltype) else: raise ValueError,"Invalid reinitialization of Formex coords" def __getitem__(self,i): """Return element i of the Formex. This allows addressing element i of Formex F as F[i]. """ return self.coords[i] def __setitem__(self,i,val): """Change element i of the Formex. This allows writing expressions as F[i] = [[1,2,3]]. """ self.coords[i] = val def __setstate__(self,state): """Set the object from serialized state. This allows to read back old pyFormex Project files where the Formex class had 'f' and 'p' attributes. """ if "p" in state: state['prop'] = state['p'] del state['p'] if 'f' in state: state['coords'] = state['f'] del state['f'] self.__dict__.update(state) def element(self,i): """Return element i of the Formex""" return self.coords[i] def point(self,i,j): """Return point j of element i""" return self.coords[i,j] def coord(self,i,j,k): """Return coord k of point j of element i""" return self.coords[i,j,k] ########################################################################### # # Return information about a Formex # def nelems(self): """Return the number of elements in the formex.""" return self.coords.shape[0] __len__ = nelems # implements len(Formex) def nplex(self): """Return the number of points per element. Examples: 1: unconnected points, 2: straight line elements, 3: triangles or quadratic line elements, 4: tetraeders or quadrilaterals or cubic line elements. """ return self.coords.shape[1] def ndim(self): """Return the number of dimensions. This is the number of coordinates for each point. In the current implementation this is always 3, though you can define 2D Formices by given only two coordinates: the third will automatically be set to zero. """ return self.coords.shape[2] def npoints(self): """Return the number of points in the formex. This is the product of the number of elements in the formex with the number of nodes per element. """ return self.coords.shape[0]*self.coords.shape[1] def shape(self): """Return the shape of the Formex. The shape of a Formex is the shape of its data array, i.e. a tuple (nelems, nplex, ndim). """ return self.coords.shape # Coordinates def view(self): """Return the Formex coordinates as a numpy array (ndarray). Since the ndarray object has a method view() returning a view on the ndarray, this method allows writing code that works with both Formex and ndarray instances. The results is always an ndarray. """ return self.coords.view() # Properties def getProp(self,index=None): """Return the property numbers of the element in index""" if index is None or self.prop is None: return self.prop else: return self.prop[index] def maxProp(self): """Return the highest property value used, or None""" if self.prop is None: return None else: return self.prop.max() def propSet(self): """Return a list with unique property values on this Formex.""" if self.prop is None: return None else: return unique(self.prop) def centroids(self): """Return the centroids of all elements of the Formex. The centroid of an element is the point whose coordinates are the mean values of all points of the element. The return value is a Coords object with nelems points. """ return self.coords.mean(axis=1) # Data conversion def fuse(self,repeat=True,nodesperbox=1,rtol=1.e-5,atol=None): """Return a tuple of nodal coordinates and element connectivity. A tuple of two arrays is returned. The first is float array with the coordinates of the unique nodes of the Formex. The second is an integer array with the node numbers connected by each element. The elements come in the same order as they are in the Formex, but the order of the nodes is unspecified. By the way, the reverse operation of ``coords,elems = fuse(F)`` is accomplished by ``F = Formex(coords[elems])`` There is a (very small) probability that two very close nodes are not equivalenced by this procedure. Use it multiple times with different parameters to check. You can also set the rtol /atol parameters to influence the equivalence checking of two points. The default settting for atol is rtol * self.dsize() """ if atol is None: atol = rtol * self.dsize() coords = reshape(self.coords,(self.nnodes(),3)) coords,index = coords.fuse(nodesperbox,0.5,rtol=rtol,atol=atol,repeat=repeat) index = index.reshape(self.coords.shape[:2]) return coords,index def toMesh(self,*args,**kargs): """Convert a Formex to a Mesh. Converts a geometry in Formex model to the equivalent Mesh model. In the Mesh model, all points with nearly identical coordinates are fused into a single point, and elements are defined by a connectivity table with integers pointing to the corresponding vertex. """ from mesh import Mesh x,e = self.fuse(*args,**kargs) return Mesh(x,e,prop=self.prop,eltype=self.eltype) def toSurface(self): """Convert a Formex to a Surface. If the plexitude of the Formex is 3, returns a TriSurface equivalent with the Formex. Else, an error is raised. """ from plugins.trisurface import TriSurface if self.nplex() == 3: return TriSurface(self) else: raise ValueError,"Only plexitude-3 Formices can be converted to TriSurface. Got plexitude %s" % self.nplex() def info(self): """Return formatted information about a Formex.""" bb = self.bbox() return """shape = %s bbox[lo] = %s bbox[hi] = %s center = %s maxprop = %s """ % (self.shape(),bb[0],bb[1],self.center(),self.maxProp()) ############################################################################## # Create string representations of a Formex # @classmethod def point2str(clas,point): """Return a string representation of a point""" s = "" if len(point)>0: s += str(point[0]) if len(point) > 1: for i in point[1:]: s += "," + str(i) return s @classmethod def element2str(clas,elem): """Return a string representation of an element""" s = "[" if len(elem) > 0: s += clas.point2str(elem[0]) if len(elem) > 1: for i in elem[1:]: s += "; " + clas.point2str(i) return s+"]" def asFormex(self): """Return string representation of a Formex as in Formian. Coordinates are separated by commas, points are separated by semicolons and grouped between brackets, elements are separated by commas and grouped between braces:: >>> F = Formex([[[1,0],[0,1]],[[0,1],[1,2]]]) >>> print(F) {[1.0,0.0,0.0; 0.0,1.0,0.0], [0.0,1.0,0.0; 1.0,2.0,0.0]} """ s = "{" if len(self.coords) > 0: s += self.element2str(self.coords[0]) if len(self.coords) > 1: for i in self.coords[1:]: s += ", " + self.element2str(i) return s+"}" def asFormexWithProp(self): """Return string representation as Formex with properties. The string representation as done by asFormex() is followed by the words "with prop" and a list of the properties. """ s = self.asFormex() if isinstance(self.prop,ndarray): s += " with prop " + self.prop.__str__() else: s += " no prop " return s def asArray(self): """Return string representation as a numpy array.""" return self.coords.__str__() #default print function __str__ = asFormex @classmethod def setPrintFunction (clas,func): """Choose the default formatting for printing formices. This sets how formices will be formatted by a print statement. Currently there are two available functions: asFormex, asArray. The user may create its own formatting method. This is a class method. It should be used asfollows: Formex.setPrintFunction(Formex.asArray). """ clas.__str__ = func def fprint(self,*args,**kargs): self.coords.fprint(*args,**kargs) ############################################################################## # # These are the only functions that change a Formex ! # ############################################################################## def setProp(self,p=None): """Create or destroy the property array for the Formex. A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored. If a value None is given, the properties are removed from the Formex. """ if p is None: self.prop = None else: p = array(p).astype(Int) self.prop = resize(p,self.coords.shape[:1]) return self def append(self,F): """Append the members of Formex F to this one. This function changes the original one! Use __add__ if you want to get a copy with the sum. >>> F = Formex([[[1.0,1.0,1.0]]]) >>> G = F.append(F) >>> print(F) {[1.0,1.0,1.0], [1.0,1.0,1.0]} """ if F.coords.size == 0: return self if self.coords.size == 0: self.coords = F.coords self.prop = F.prop return self self.coords = Coords(concatenate((self.coords,F.coords))) ## What to do if one of the formices has properties, the other one not? ## The current policy is to use zero property values for the Formex ## without props if self.prop is not None or F.prop is not None: if self.prop is None: self.prop = zeros(shape=self.coords.shape[:1],dtype=Int) if F.prop is None: p = zeros(shape=F.coords.shape[:1],dtype=Int) else: p = F.prop self.prop = concatenate((self.prop,p)) return self ############################################################################## ## ## All the following functions leave the original Formex unchanged and return ## a new Formex instead. ## This is a design decision intended so that the user can write statements as ## G = F.op1().op2().op3() ## without having an impact on F. If the user wishes, he can always change an ## existing Formex by a statement such as ## F = F.op() ## While this may seem to create a lot of intermediate array data, Python and ## numpy are clever enough to release the memory that is no longer used. ## ############################################################################## # # Create copies, concatenations, subtractions, connections, ... # ## def sort(self): ## """Sorts the elements of a Formex. ## Sorting is done according to the bbox of the elements. ## !! NOT FULLY IMPLEMENTED: CURRENTLY ONLY SORTS ACCORDING TO ## !! THE 0-direction OF NODE 0 ## """ ## sel = argsort(self.x()[:,0]) ## f = self.coords[sel] ## if self.prop: ## p = self.prop[sel] ## return Formex(f,p,self.eltype) ## def copy(self): ## """Return a deep copy of itself.""" ## return Formex(self.coords,self.prop,self.eltype) ## ## IS THIS CORRECT? Shouldn't this be self.coords.copy() ??? ## ## In all examples it works, I think because the operations on ## ## the array data cause a copy to be made. Need to explore this. def __add__(self,other): """Return the sum of two formices. This returns a Formex with all elements of self and other. It allows us to write simple expressions as F+G to concatenate the Formices F and G. """ return self.copy().append(other) # BV: would Formex.concatenate([self,other]) be better? # @classmethod def concatenate(clas,Flist): """Concatenate all formices in Flist. This is a class method, not an instance method! >>> F = Formex([[[1,2,3]]],1) >>> print(Formex.concatenate([F,F,F])) {[1.0,2.0,3.0], [1.0,2.0,3.0], [1.0,2.0,3.0]} Formex.concatenate([F,G,H]) is functionally equivalent with F+G+H. The latter is simpler to write for a list with a few elements. If the list becomes large, or the number of items in the list is not fixed, the concatenate method is easier (and faster). We made it a class method and not a global function, because that would interfere with NumPy's own concatenate function. """ f = concatenate([ F.coords for F in Flist ]) plist = [ F.prop for F in Flist ] hasp = [ p is not None for p in plist ] nhasp = sum(hasp) if nhasp == 0: p = None # No Formices have properties else: if nhasp < len(Flist): # Add zero properties where missing for i in range(len(Flist)): if plist[i] is None: plist[i] = zeros(shape=(Flist[i].nelems(),),dtype=Int) p = concatenate(plist) return Formex(f,p,Flist[0].eltype) def select(self,idx): """Return a Formex which holds only element with numbers in ids. idx can be a single element number or a list of numbers or any other index mechanism accepted by numpy's ndarray """ if self.prop is None: return Formex(self.coords[idx],eltype=self.eltype) else: idx = asarray(idx) return Formex(self.coords[idx],self.prop[idx],self.eltype) def cselect(self,idx): """Return a Formex without the elements with numbers in ids. idx can be a single element number or a list of numbers or any other index mechanism accepted by numpy's ndarray This is the complementary operation of select """ keep = arange(self.nelems()) keep[idx] = -1 return self.select(keep[keep>=0]) def selectNodes(self,idx): """Return a Formex which holds only some nodes of the parent. idx is a list of node numbers to select. Thus, if F is a plex 3 Formex representing triangles, the sides of the triangles are given by F.selectNodes([0,1]) + F.selectNodes([1,2]) + F.selectNodes([2,0]) The returned Formex inherits the property of its parent. """ return Formex(self.coords[:,idx,:],self.prop,self.eltype) def points(self): """Return a Formex containing only the points. This is obviously a Formex with plexitude 1. It holds the same data as the original Formex, but in another shape: the number of points per element is 1, and the number of elements is equal to the total number of points. The properties are not copied over, since they will usually not make any sense. The vertices() method returns the same data, but as a Coords object. """ return Formex(self.coords.reshape((-1,1,3))) def vertices(self): """Return the points of a Formex as a 2dim Coords object. The return value holds the same coordinate data as the input Formex, but in another shape: (npoints,3). The points() method returns the same data, but as a Formex. """ return self.coords.reshape((-1,3)) def remove(self,F): """Return a Formex where the elements in F have been removed. This is also the subtraction of the current Formex with F. Elements are only removed if they have the same nodes in the same order. This is a slow operation: for large structures, you should avoid it where possible. """ flag = ones((self.coords.shape[0],)) for i in range(self.coords.shape[0]): for j in range(F.coords.shape[0]): if allclose(self.coords[i],F.coords[j]): # element i is same as element j of F flag[i] = 0 break if self.prop is None: p = None else: p = self.prop[flag>0] return Formex(self.coords[flag>0],p,self.eltype) def whereProp(self,val): """Return the numbers of the elements with property val. val is either a single integer, or a list/array of integers. The return value is an array holding all the numbers of all the elements that have the property val, resp. one of the values in val. If the Formex has no properties, a empty array is returned. """ if self.prop is not None: if type(val) == int: return where(self.prop==val)[0] else: return unique(concatenate([where(self.prop==v)[0] for v in val])) return array([],dtype=Int) def withProp(self,val): """Return a Formex which holds only the elements with property val. val is either a single integer, or a list/array of integers. The return value is a Formex holding all the elements that have the property val, resp. one of the values in val. The returned Formex inherits the matching properties. If the Formex has no properties, a copy with all elements is returned. """ if self.prop is None: return Formex(self.coords,eltype=self.eltype) elif type(val) == int: return Formex(self.coords[self.prop==val],val,self.eltype) else: t = zeros(self.prop.shape,dtype=bool) for v in asarray(val).flat: t += (self.prop == v) return Formex(self.coords[t],self.prop[t],self.eltype) def splitProp(self): """Partition a Formex according to its prop values. Returns a dict with the prop values as keys and the corresponding partitions as values. Each value is a Formex instance. It the Formex has no props, an empty dict is returned. """ if self.prop is None: return {} else: return dict([(p,self.withProp(p)) for p in self.propSet()]) def elbbox(self): """Return a Formex where each element is replaced by its bbox. The returned Formex has two points for each element: two corners of the bbox. """ ## Obviously, in the case of plexitude 1 and 2, ## there are shorter ways to perform this return Formex( [ [ [ self.coords[j,:,i].min() for i in range(self.coords.shape[2])], [ self.coords[j,:,i].max() for i in range(self.coords.shape[2])] ] for j in range(self.coords.shape[0]) ] ) def removeDuplicate(self,permutations=True,rtol=1.e-4,atol=1.e-6): """Return a Formex which holds only the unique elements. Two elements are considered equal when all its points are (nearly) coincident. By default any permutation of point order is also allowed. Two coordinate value are considered equal if they are both small compared to atol or if their difference divided by the second value is small compared to rtol. If permutations is set False, two elements are not considered equal if one's points are a permutation of the other's. """ M = self.toMesh(rtol=rtol,atol=atol) ind,ok = M.elems.testDuplicate() return self.select(ind[ok]) ## def nonzero(self): ## """Return a Formex which holds only the nonzero elements. ## A zero element is an element where all nodes are equal.""" ## # NOT IMPLEMENTED YET !!! FOR NOW, RETURNS A COPY ## return Formex(self.coords,self.prop,self.eltype) ############################# # Test and clipping functions def test(self,nodes='all',dir=0,min=None,max=None,atol=0.): """Flag elements having nodal coordinates between min and max. This function is very convenient in clipping a Formex in a specified direction. It returns a 1D integer array flagging (with a value 1 or True) the elements having nodal coordinates in the required range. Use where(result) to get a list of element numbers passing the test. Or directly use clip() or cclip() to create the clipped Formex. The test plane can be defined in two ways, depending on the value of dir. If dir == 0, 1 or 2, it specifies a global axis and min and max are the minimum and maximum values for the coordinates along that axis. Default is the 0 (or x) direction. Else, dir should be compaitble with a (3,) shaped array and specifies the direction of the normal on the planes. In this case, min and max are points and should also evaluate to (3,) shaped arrays. nodes specifies which nodes are taken into account in the comparisons. It should be one of the following: - a single (integer) point number (< the number of points in the Formex) - a list of point numbers - one of the special strings: 'all', 'any', 'none' The default ('all') will flag all the elements that have all their nodes between the planes x=min and x=max, i.e. the elements that fall completely between these planes. One of the two clipping planes may be left unspecified. """ if min is None and max is None: raise ValueError,"At least one of min or max have to be specified." f = self.coords if type(nodes)==str: nod = range(f.shape[1]) else: nod = nodes if type(dir) == int: if min is not None: T1 = f[:,nod,dir] > (min - atol) if max is not None: T2 = f[:,nod,dir] < (max + atol) else: if min is not None: T1 = f.distanceFromPlane(min,dir) > (-atol) if max is not None: T2 = f.distanceFromPlane(max,dir) < (atol) if min is None: T = T2 elif max is None: T = T1 else: T = T1 * T2 if len(T.shape) == 1: return T if nodes == 'any': T = T.any(1) elif nodes == 'none': T = (1-T.any(1)).astype(bool) else: T = T.all(1) return T def clip(self,t): """Return a Formex with all the elements where t>0. t should be a 1-D integer array with length equal to the number of elements of the formex. The resulting Formex will contain all elements where t > 0. This is a convenience function for the user, equivalent to F.select(t>0). """ return self.select(t>0) def cclip(self,t): """This is the complement of clip, returning a Formex where t<=0. """ return self.select(t<=0) ############################################################################## # # Transformations that preserve the topology (but change coordinates) # def circulize(self,angle): """Transform a linear sector into a circular one. A sector of the (0,1) plane with given angle, starting from the 0 axis, is transformed as follows: points on the sector borders remain in place. Points inside the sector are projected from the center on the circle through the intersection points of the sector border axes and the line through the point and perpendicular to the bisector of the angle. See Diamatic example.""" e = tand(0.5*angle) return self.map(lambda x,y,z:[where(y==0,x,(x*x+x*y*e)/sqrt(x*x+y*y)),where(x==0,y,(x*y+y*y*e)/sqrt(x*x+y*y)),0]) def circulize1(self): """Transforms the first octant of the 0-1 plane into 1/6 of a circle. Points on the 0-axis keep their position. Lines parallel to the 1-axis are transformed into circular arcs. The bisector of the first quadrant is transformed in a straight line at an angle Pi/6. This function is especially suited to create circular domains where all bars have nearly same length. See the Diamatic example. """ return self.map(lambda x,y,z:[where(x>0,x-y*y/(x+x),0),where(x>0,y*sqrt(4*x*x-y*y)/(x+x),y),0]) def shrink(self,factor): """Shrinks each element with respect to its own center. Each element is scaled with the given factor in a local coordinate system with origin at the element center. The element center is the mean of all its nodes. The shrink operation is typically used (with a factor around 0.9) in wireframe draw mode to show all elements disconnected. A factor above 1.0 will grow the elements. """ c = self.coords.mean(1).reshape((self.coords.shape[0],1,self.coords.shape[2])) return Formex(factor*(self.coords-c)+c,self.prop,self.eltype) ############################################################################## # # Transformations that change the topology # def reverse(self): """Return a Formex where all elements have been reversed. Reversing an element means reversing the order of its points. This is equivalent to:: self.selectNodes(arange(self.nplex()-1,-1,-1)) """ return Formex(self.coords[:,::-1],self.prop,self.eltype) def mirror(self,dir=2,pos=0,keep_orig=True): """Reflect a Formex in one of the coordinate directions This method behaves like reflect(), but adds the reflected part to the original. Setting keep_orig=False makes it behave just like reflect(). """ if keep_orig: return self+self.reflect(dir,pos) else: return self.reflect(dir,pos) def replicate(self,n,dir=0,step=1.0): """Replicate a Formex n times with fixed step in any direction. Returns a Formex which is the concatenation of n copies, where each copy is equal to the previous one translated over `(dir,step)`, where `dir` and `step` are interpreted just like in the :meth:`translate` method. The first of the copies is equal to the original. """ f = self.coords.replicate(n,dir,step=step) f.shape = (f.shape[0]*f.shape[1],f.shape[2],f.shape[3]) ## the replication of the properties is automatic! return Formex(f,self.prop,self.eltype) rep = replicate def replic(self,n,step=1.0,dir=0): """Return a Formex with n replications in direction dir with step. The original Formex is the first of the n replicas. """ n = int(n) f = array( [ self.coords for i in range(n) ] ) for i in range(1,n): f[i,:,:,dir] += i*step f.shape = (f.shape[0]*f.shape[1],f.shape[2],f.shape[3]) ## the replication of the properties is automatic! return Formex(f,self.prop,self.eltype) def replic2(self,n1,n2,t1=1.0,t2=1.0,d1=0,d2=1,bias=0,taper=0): """Replicate in two directions. n1,n2 number of replications with steps t1,t2 in directions d1,d2 bias, taper : extra step and extra number of generations in direction d1 for each generation in direction d2 """ P = [ self.translatem((d1,i*bias),(d2,i*t2)).replic(n1+i*taper,t1,d1) for i in range(n2) ] ## We should replace the Formex concatenation here by ## separate data and prop concatenations, because we are ## guaranteed that either none or all formices in P have props. return Formex.concatenate(P) def rosette(self,n,angle,axis=2,point=[0.,0.,0.]): """Return a Formex with n rotational replications with angular step angle around an axis parallel with one of the coordinate axes going through the given point. axis is the number of the axis (0,1,2). point must be given as a list (or array) of three coordinates. The original Formex is the first of the n replicas. """ f = self.coords - point f = array( [ f for i in range(n) ] ) for i in range(1,n): m = array(rotationMatrix(i*angle,axis)) f[i] = dot(f[i],m) f.shape = (f.shape[0]*f.shape[1],f.shape[2],f.shape[3]) return Formex(f + point,self.prop,self.eltype) def translatem(self,*args,**kargs): """Multiple subsequent translations in axis directions. The argument list is a sequence of tuples (axis, step). Thus translatem((0,x),(2,z),(1,y)) is equivalent to translate([x,y,z]). This function is especially conveniant to translate in calculated directions. """ tr = [0.,0.,0.] for d,t in args: tr[d] += t return self.translate(tr) ############################################################################## # # Transformations that change the plexitude # def extrude(self,n,step=1.,dir=0): """Extrude a Formex in one of the axis directions. Returns a Formex with doubled plexitude. First the original Formex is translated over n steps of length step in direction dir. Then each pair of subsequent Formices is connected to form a higher plexitude structure. Currently, this function correctly transforms: point1 to line2, line2 to quad4, tri3 to wedge6, quad4 to hex8. See the 'connect' function for a more versatile tool. """ return self.toMesh().extrude(n,step,dir).toFormex() ############################################################################## # # Transformations that work only for some plexitudes # # !! It is not clear if they really belong here, or should go to a subclass def divide(self,div): """Divide a plex-2 Formex at the values in div. Replaces each member of the Formex by a sequence of members obtained by dividing the Formex at the relative values specified in div. The values should normally range from 0.0 to 1.0. As a convenience, if an integer is specified for div, it is taken as a number of divisions for the interval [0..1]. This function only works on plex-2 Formices (line segments). """ if self.nplex() != 2: raise RuntimeError,"Can only divide plex-2 Formices" div = asarray(div).ravel() if div.size == 1 and div.dtype.kind=='i': div = div[0] div = arange(div+1) / float(div) A = interpolate(self.selectNodes([0]),self.selectNodes([1]),div[:-1],swap=True) B = interpolate(self.selectNodes([0]),self.selectNodes([1]),div[1:],swap=True) return connect([A,B]) def intersectionWithPlane(self,p,n): """Return the intersection of a Formex with the plane (p,n). Currently this only works for plex-2 and plex-3 Formices. The intersection of the Formex with a plane specified by a point p and normal n is returned. For a plex-2 Formex (lines), the returned Formex will be of plexitude 1 (points). For a plex-3 Formex (triangles) the returned Fomrex has plexitude 2 (lines). """ if self.nplex() == 2: from geomtools import intersectionPointsSWP return intersectionPointsSWP(self.coords,p,n,mode='pair')[0] elif self.nplex() == 3: return Formex(intersectionLinesWithPlane(self,p,n)) else: # OTHER PLEXITUDES NEED TO BE IMPLEMENTED raise ValueError,"Formex should be plex-2 or plex-3" # Removed in 0.9 ## @deprecation("\nUse Formex.intersectionWithPlane() instead.") ## def intersectionPointsWithPlane(self,p,n): ## return self.intersectionWithPlane(p,n) ## @deprecation("\nUse Formex.intersectionWithPlane() instead.") ## def intersectionLinesWithPlane(self,p,n): ## return self.intersectionWithPlane(p,n) def cutWithPlane(self,p,n,side='',atol=None,newprops=None): """Cut a Formex with the plane(s) (p,n). ..warning :: This method currently only works for plexitude 2 or 3! - `p`,`n`: a point and normal vector defining the cutting plane. In case of plexitude 3, p and n can be sequences of points and vector, allowing to cut with multiple planes. Both p and n have shape (3) or (npoints,3). The default return value is a tuple of two Formices of the same plexitude as the input: (Fpos,Fneg), where Fpos is the part of the Formex at the positive side of the plane (as defined by the normal vector), and Fneg is the part at the negative side. Elements of the input Formex that are lying completely on one side of the plane will return unaltered. Elements that are crossing the plane will be cut and split up into multiple parts. When side = '+' or '-' (or 'positive'or 'negative'), only one of the sides is returned. The other arguments (atol,newprops) are currently specific for the plexitude. See the cut2AtPlane and cut3AtPlane, which hold the actual implementation of this method. """ if atol is None: atol = 1.e-5*self.dsize() if self.nplex() == 2: return cut2AtPlane(self,p,n,side,atol,newprops) elif self.nplex() == 3: return cut3AtPlane(self,p,n,side,atol,newprops) else: # OTHER PLEXITUDES NEED TO BE IMPLEMENTED raise ValueError,"Formex should be plex-2 or plex-3" #################### Misc Operations ######################################### def split(self,n=1): """Split a Formex in subFormices containing n elements. The number of elements in the Formex should be a multiple of n. Returns a list of Formices each comprising n elements. """ if self.nelems() % n != 0: raise RuntimeError,"Number of elements should be integer multiple of n" m = self.nelems()/n if self.prop is None: return [ Formex(self.coords[n*i:n*(i+1)],self.eltype) for i in range(m) ] else: return [ Formex(self.coords[n*i:n*(i+1)],self.prop[n*i:n*(i+1)],self.eltype) for i in range(m) ] #################### Read/Write Formex File ################################## def write(self,fil,sep=' ',mode='w'): """Write a Formex to file. If fil is a string, a file with that name is opened. Else fil should be an open file. The Formex is then written to that file in a native format, using sep as separator between the coordinates. If fil is a string, the file is closed prior to returning. """ from geomfile import GeometryFile f = GeometryFile(fil,mode='w',sep=sep) f.write(self) if f.isname and mode[0]=='w': f.close() @classmethod def read(clas,fil,sep=' '): """Read a Formex from file. fil is a filename or a file object. If the file is in a valid Formex file format, the Formex is read and returned. Otherwise, None is returned. Valid Formex file formats are described in the manual. """ from geomfile import GeometryFile f = GeometryFile(fil,mode='r',sep=sep) res = f.read(count=1) return res.values()[0] @classmethod def fromstring(clas,fil,sep=' ',nplex=1,ndim=3,count=-1): """Create a :class:`Formex` from coodinates in a string. This uses the :meth:`Coords.fromstring` method to read coordinates from a string and restructures them into a Formex of the specified plexitude. fil: a string containing a single sequence of float numbers separated by whitespace and a possible separator string. sep: the separator used between the coordinates. If not a space, all extra whitespace is ignored. ndim: number of coordinates per point. Should be 1, 2 or 3 (default). If 1, resp. 2, the coordinate string only holds x, resp. x,y values. count: total number of coordinates to read. This should be a multiple of 3. The default is to read all the coordinates in the string. ``count`` can be used to force an error condition if the string does not contain the expected number of values. The return value is a :class:`Coords` object. """ x = Coords.fromstring(fil,sep=sep,ndim=ndim,count=count) if x.shape[0] % nplex != 0: raise RuntimeError,"Number of points read: %s, expected a multiple of %s!" % (x.shape[0],nplex) return Formex(x.reshape(-1,nplex,3)) @classmethod def fromfile(clas,fil,sep=' ',nplex=1): """Read the coordinates of a Formex from a file""" x = Coords.fromfile(fil,sep=sep) if x.shape[0] % nplex != 0: raise RuntimeError,"Number of points read: %s, should be multiple of %s!" % (x.shape[0],nplex) return Formex(x.reshape(-1,nplex,3)) def actor(self,**kargs): if self.nelems() == 0: return None from gui.actors import GeomActor return GeomActor(self,**kargs) ######################################################################### # # Obsolete and deprecated functions # nnodes = npoints ######################################################################### # # Convenient short notations and aliases # rep = replic ros = rosette unique = removeDuplicate ############################################################################## # # Functions which are not Formex class methods # def connect(Flist,nodid=None,bias=None,loop=False): """Return a Formex which connects the formices in list. Flist is a list of formices, nodid is an optional list of nod ids and bias is an optional list of element bias values. All lists should have the same length. The returned Formex has a plexitude equal to the number of formices in list. Each element of the Formex consist of a node from the corresponding element of each of the formices in list. By default this will be the first node of that element, but a nodid list may be given to specify the node id to be used for each of the formices. Finally, a list of bias values may be given to specify an offset in element number for the subsequent formices. If loop==False, the order of the Formex will be the minimum order of the formices in Flist, each minus its respective bias. By setting loop=True however, each Formex will loop around if its end is encountered, and the order of the result is the maximum order in Flist. """ try: m = len(Flist) for i in range(m): if isinstance(Flist[i],Formex): pass elif isinstance(Flist[i],ndarray): Flist[i] = Formex(Flist[i]) else: raise TypeError except TypeError: raise TypeError,'connect(): first argument should be a list of formices' if not nodid: nodid = [ 0 for i in range(m) ] if not bias: bias = [ 0 for i in range(m) ] if loop: n = max([ Flist[i].nelems() for i in range(m) ]) else: n = min([ Flist[i].nelems() - bias[i] for i in range(m) ]) f = zeros((n,m,3),dtype=Float) for i,j,k in zip(range(m),nodid,bias): v = Flist[i].coords[k:k+n,j,:] if loop and k > 0: v = concatenate([v,Flist[i].coords[:k,j,:]]) f[:,i,:] = resize(v,(n,3)) return Formex(f) def interpolate(F,G,div,swap=False): """Create interpolations between two formices. F and G are two Formices with the same shape. v is a list of floating point values. The result is the concatenation of the interpolations of F and G at all the values in div. An interpolation of F and G at value v is a Formex H where each coordinate Hijk is obtained from: Hijk = Fijk + v * (Gijk-Fijk). Thus, a Formex interpolate(F,G,[0.,0.5,1.0]) will contain all elements of F and G and all elements with mean coordinates between those of F and G. As a convenience, if an integer is specified for div, it is taken as a number of divisions for the interval [0..1]. Thus, interpolate(F,G,n) is equivalent with interpolate(F,G,arange(0,n+1)/float(n)) The swap argument sets the order of the elements in the resulting Formex. By default, if n interpolations are created of an m-element Formex, the element order is in-Formex first (n sequences of m elements). If swap==True, the order is swapped and you get m sequences of n interpolations. """ r = F.coords.interpolate(G.coords,div) # r is a 4-dim array if swap: r = r.swapaxes(0,1) return Formex(r.reshape((-1,) + r.shape[-2:])) ############################################################################## # # Testing # # Some of the docstrings above hold test examples. They should be carefully # crafted to test the functionality of the Formex class. # # Ad hoc test examples during development can be added to the test() function # below. # # python formex.py # will execute the docstring examples silently. # python formex.py -v # will execute the docstring examples verbosely. # In both cases, the ad hoc tests are only run if the docstring tests # are passed. # if __name__ == "__main__": def test(): """Run some additional examples. This is intended for tests during development. This can be changed at will. """ Formex.setPrintFunction(Formex.asFormexWithProp) A = Formex() print("An empty formex",A) F = Formex([[[1,0],[0,1]],[[0,1],[1,2]]]) print("F =",F) F1 = F.translate(0,6) F1.setProp(5) print("F1 =",F1) F2 = F.reflect(0,2.) print("F2 =",F2) F3 = F.reflect(0,1.5).translate(1,2) F3.setProp([1,2]) G = F1+F3+F2+F3 print("F1+F3+F2+F3 =",G) H = Formex.concatenate([F1,F3,F2,F3]) print("F1+F3+F2+F3 =",H) print("elbbox:",G.elbbox()) print("met prop 1:",G.withProp(1)) print("unique:",G.unique()) print("vertices:",G.vertices()) print("points:",G.points()) print("unique points:",G.points().unique()) print("diagonal size:",G.dsize()) F = Formex([[[0,0]],[[1,0]],[[1,1]],[[0,1]]]) G = connect([F,F],bias=[0,1]) print(G) G = connect([F,F],bias=[0,1],loop=True) print(G) print(G[1]) print(G.feModel()) print(F) print(F.bbox()) print(F.center(),F.centroid()) print(F.bsphere()) F = Formex([[[0,0],[1,0],[0,1]],[[1,0],[1,1],[0,1]]]) print(F) print(F.reverse()) Formex.setPrintFunction(Formex.asArray) print(F) F.fprint() F.fprint("%10.3f %10.4f %10.5f") F.fprint(fmt="%10.4f %10.5f %10.6f") print(type(F)) f = 0 (f,t) = _test() if f == 0: test() # End pyformex-0.8.6/pyformex/project.py0000644000211500021150000003446011705104656017137 0ustar benebene00000000000000# $Id: project.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """project.py Functions for managing a project in pyFormex. """ import pyformex as pf from pyformex import utils import os,sys import cPickle import gzip _signature_ = 'pyFormex 0.8.6' module_relocations = { 'plugins.mesh' : 'mesh', 'plugins.surface' : 'plugins.trisurface', } def find_global(module,name): """Override the import path of some classes""" pf.debug("I want to import %s from %s" % (name,module)) if module in module_relocations: module = module_relocations[module] pf.debug(" I will try module %s instead" % module) __import__(module) mod = sys.modules[module] clas = getattr(mod, name) return clas def pickle_load(f,try_resolve=True): """Load data from pickle file f.""" pi = cPickle.Unpickler(f) if try_resolve: pi.find_global = find_global return pi.load() highest_format = 3 class Project(dict): """Project: a persistent storage of pyFormex data. A pyFormex Project is a regular Python dict that can contain named data of any kind, and can be saved to a file to create persistence over different pyFormex sessions. The :class:`Project` class is used by pyFormex for the ``pyformex.PF`` global variable that collects variables exported from pyFormex scripts. While projects are mostly handled through the pyFormex GUI, notably the *File* menu, the user may also create and handle his own Project objects from a script. Because of the way pyFormex Projects are written to file, there may be problems when trying to read a project file that was created with another pyFormex version. Problems may occur if the project contains data of a class whose implementation has changed, or whose definition has been relocated. Our policy is to provide backwards compatibility: newer versions of pyFormex will normally read the older project formats. Saving is always done in the newest format, and these can generally not be read back by older program versions (unless you are prepared to do some hacking). .. warning:: Compatibility issues. Occasionally you may run into problems when reading back an old project file, especially when it was created by an unreleased (development) version of pyFormex. Because pyFormex is evolving fast, we can not test the full compatibility with every revision You can file a support request on the pyFormex `support tracker`_. and we will try to add the required conversion code to pyFormex. The project files are mainly intended as a means to easily save lots of data of any kind and to restore them in the same session or a later session, to pass them to another user (with the same or later pyFormex version), to store them over a medium time period. Occasionally opening and saving back your project files with newer pyFormex versions may help to avoid read-back problems over longer time. For a problemless long time storage of Geometry type objects you may consider to write them to a pyFormex Geometry file (.pgf) instead, since this uses a stable ascii based format. It can (currently) not deal with other data types however. Parameters: - `filename`: the name of the file where the Project data will be saved. If the file exists (and `access` is not `w`), it should be a previously saved Project and an attempt will be made to load the data from this file into the Project. If this fails, an error is raised. If the file exists and `access` is `w`, it will be overwritten, destroying any previous contents. If no filename is specified, a temporary file will be created when the Project is saved for the first time. The file with not be automatically deleted. The generated name can be retrieved from the filename attribute. - `access`: One of 'wr' (default), 'rw', 'w' or 'r'. If the string contains an 'r' the data from an existing file will be read into the dict. If the string starts with an 'r', the file should exist. If the string contains a 'w', the data can be written back to the file. The 'r' access mode is a read-only mode. ====== =============== ============ =================== access File must exist File is read File can be written ====== =============== ============ =================== r yes yes no rw yes yes yes wr no if it exists yes w no no yes ====== =============== ============ =================== - `convert`: if True (default), and the file is opened for reading, an attempt is made to open old projects in a compatibility mode, doing the necessary conversions to new data formats. If convert is set False, only the latest format can be read and older formats will generate an error. - `signature`: A text that will be written in the header record of the file. This can e.g. be used to record format version info. - `compression`: An integer from 0 to 9: compression level. For large data sets, compression leads to much smaller files. 0 is no compression, 9 is maximal compression. The default is 4. - `binary`: if False and no compression is used, storage is done in an ASCII format, allowing to edit the file. Otherwise, storage uses a binary format. Using binary=False is deprecated. - `data`: a dict-like object to initialize the Project contents. These data may override values read from the file. """ def __init__(self,filename=None,access='wr',convert=True,signature=_signature_,compression=5,binary=True,data={},**kargs): """Create a new project.""" if 'create' in kargs: utils.warn("The create=True argument should be replaced with access='w'") if 'legacy' in kargs: utils.warn("The legacy=True argument has become superfluous") self.filename = filename self.access = access self.signature = str(signature) self.gzip = compression if compression in range(1,10) else 0 self.mode = 'b' if binary or compression > 0 else '' dict.__init__(self) if filename and os.path.exists(filename) and 'r' in self.access: # read existing contents self.load(convert) self.update(data) if filename and access=='w': # destroy existing contents self.save() def header_data(self): """Construct the data to be saved in the header.""" store_attr = ['signature','gzip','mode','autofile','_autoscript_'] store_vals = [getattr(self,k,None) for k in store_attr] return dict([(k,v) for k,v in zip(store_attr,store_vals) if v is not None]) def save(self): """Save the project to file.""" if 'w' not in self.access: #print "NOT saving to readonly Project file access" return if self.filename is None: import tempfile fd,fn = tempfile.mkstemp(prefix='pyformex_',suffix='.pyf') self.filename = fn else: print "Saving project %s with mode %s and compression %s" % (self.filename,self.mode,self.gzip) #print(" Contents: %s" % self.keys()) f = open(self.filename,'w'+self.mode) # write header f.write("%s\n" % self.header_data()) f.flush() if self.mode == 'b': # When using binary, can as well use highest protocol protocol = cPickle.HIGHEST_PROTOCOL else: protocol = 0 if self.gzip: pyf = gzip.GzipFile(mode='w'+self.mode,compresslevel=self.gzip,fileobj=f) cPickle.dump(self,pyf,protocol) pyf.close() else: cPickle.dump(self,f,protocol) f.close() def readHeader(self): """Read the header from a project file. Tries to read the header from different legacy formats, and if succesfull, adjusts the project attributes according to the values in the header. Returns the open file if succesfull. """ self.format = -1 print("Reading project file: %s" % self.filename) f = open(self.filename,'rb') fpos = f.tell() s = f.readline() # Try subsequent formats try: # newest format has header in text format header = eval(s) self.__dict__.update(header) self.format = 3 except: # try OLD new format: the first pickle contains attributes try: p = pickle_load(f) self.__dict__.update(p) self.format = 2 except: s = s.strip() print "Header = '%s'" % s if s=='gzip' or s=='' or 'pyFormex' in s: # transitional format self.gzip = 5 self.format = 1 # NOT SURE IF THIS IS OK, NEED EXAMPLE FILE f.seek(fpos) else: # headerless format f.seek(0) self.gzip = 0 self.format = 0 return f def load(self,try_resolve=False): """Load a project from file. The loaded definitions will update the current project. """ f = self.readHeader() if self.format < highest_format: print "Format looks like %s" % self.format utils.warn('warn_old_project') with f: try: print "Unpickling gzip" pyf = gzip.GzipFile(fileobj=f,mode='rb') p = pickle_load(pyf,try_resolve) pyf.close() except: print "Unpickling clear" p = pickle_load(f,try_resolve) self.update(p) def convert(self,filename=None): """Convert an old format project file. The project file is read, and if successfull, is immediately saved. By default, this will overwrite the original file. If a filename is specified, the converted data are saved to that file. In both cases, access is set to 'wr', so the tha saved data can be read back immediately. """ self.load(True) print "GOT KEYS %s" % self.keys() if filename is not None: self.filename = filename self.access = 'w' print "Will now save to %s" % self.filename self.save() def uncompress(self): """Uncompress a compressed project file. The project file is read, and if successfull, is written back in uncompressed format. This allows to make conversions of the data inside. """ f = self.readHeader() print self.format,self.gzip if f: if self.gzip: try: pyf = gzip.GzipFile(self.filename,'r',self.gzip,f) except: self.gzip = 0 if self.gzip: fn = self.filename.replace('.pyf','_uncompressed.pyf') fu = open(fn,'w'+self.mode) h = self.header_data() h['gzip'] = 0 fu.write("%s\n" % h) while True: x = pyf.read() if x: fu.write(x) else: break fu.close() print "Uncompressed %s to %s" % (self.filename,fn) else: utils.warn("The contents of the file does not appear to be compressed.") f.close() def delete(self): """Unrecoverably delete the project file.""" os.remove(self.filename) # Test if __name__ == '__main__': d = dict(a=1,b=2,c=3,d=[1,2,3],e={'f':4,'g':5}) from numpy import random d['r'] = random.randint(0,100,(3,3)) print('DATA',d) P = Project('testa.pyf') P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) P = Project('testb.pyf',access='w') P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) P = Project() P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) for i in [0,1,3,5,7,9]: P = Project('testc%s.pyf'%i,access='w',compression=i) P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) P = Project('testl.pyf',access='w',legacy=True) P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) P = Project('testr.pyf',access='r') P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) # # End pyformex-0.8.6/pyformex/pyformex0000755000211500021150000000531211705104656016710 0ustar benebene00000000000000#!/usr/bin/env python # $Id: pyformex 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """pyFormex, a free program for creating and manipulating 3D geometry. pyFormex is a powerful tool for generating, manipulating, transforming and displaying large structural models of 3D geometry. Based on a powerful scripting language, pyFormex is exceptionally suited for generating parametric models and for the automization of tedious and recurring tasks in the handling of geometrical models. Built around a fully open architecture pyFormex allows the user to combine the program with nearly any other software and to extend the program to suit his own needs. pyFormex is being developed at the IBiTech, Ghent University, and can be distributed under the GNU General Public License, version 3 or later. (C) 2004-2009 Benedict Verhegghe (benedict.verhegghe@ugent.be)) """ # Get the pyformex dir and put it on the head of sys.path import sys,os _scriptdir = sys.path[0] # In case we execute the pyformex script from inside the # pyformex package dir: add the parent to the front of sys.path # to pick up this package by preference if _scriptdir.endswith('pyformex'): sys.path[:0] = [ os.path.dirname(_scriptdir) ] try: import pyformex pyformex.scriptdir = _scriptdir # we could remove the sys.path[0] again? except: print("Could not import pyformex.") print("This probably means that pyFormex was not properly installed.") # Put the pyformex path in front. sys.path = [ pyformex.__path__[0] ] + sys.path #from pyformex.main import run from main import run if __name__ == "__main__": sys.exit(run(sys.argv[1:])) # End pyformex-0.8.6/pyformex/connectivity.py0000644000211500021150000014626211705104656020213 0ustar benebene00000000000000# $Id: connectivity.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A class and functions for handling nodal connectivity. This module defines a specialized array class for representing nodal connectivity. This is e.g. used in mesh models, where geometry is represented by a set of numbered points (nodes) and the geometric elements are described by refering to the node numbers. In a mesh model, points common to adjacent elements are unique, and adjacency of elements can easily be detected from common node numbers. """ from arraytools import * from utils import deprecation from messages import _future_deprecation # BV: Should we make an InverseConnectivity class? ############################################################################ ## ## class Connectivity ## ######################### # class Connectivity(ndarray): """A class for handling element/node connectivity. A connectivity object is a 2-dimensional integer array with all non-negative values. Each row of the array defines an element by listing the numbers of its lower entity types. A typical use is a :class:`Mesh` object, where each element is defined in function of its nodes. While in a Mesh the word 'node' will normally refer to a geometrical point, here we will use 'node' for the lower entity whatever its nature is. It doesn't even have to be a geometrical entity. The current implementation limits a Connectivity object to numbers that are smaller than 2**31. In a row (element), the same node number may occur more than once, though usually all numbers in a row are different. Rows containing duplicate numbers are called `degenerate` elements. Rows containing the same node sets, albeit different permutations thereof, are called duplicates. A new Connectivity object is created with the following syntax :: Connectivity(data=[],dtyp=None,copy=False,nplex=0) Parameters: - `data`: should be compatible with an integer array with shape `(nelems,nplex)`, where `nelems` is the number of elements and `nplex` is the plexitude of the elements. - `dtype`: can be specified to force an integer type but is set by default from the passed `data`. - `copy`: can be set True to force copying the data. By default, the specified data will be used without copying, if possible. - `nplex`: can be specified to force a check on the plexitude of the data, or to set the plexitude for an empty Connectivity. An error will be raised if the specified data do not match the specified plexitude. Example: >>> print Connectivity([[0,1,2],[0,1,3],[0,3,2],[0,5,3]]) [[0 1 2] [0 1 3] [0 3 2] [0 5 3]] """ # # :DEV # Because we have a __new__ constructor here and no __init__, # we have to list the arguments explicitely in the docstring above. # def __new__(self,data=[],dtyp=None,copy=False,nplex=0,allow_negative=False,eltype=None): """Create a new Connectivity object.""" if eltype is None: try: eltype = data.eltype except: eltype = None # Turn the data into an array, and copy if requested ar = array(data, dtype=dtyp, copy=copy) if ar.ndim < 2: if nplex > 0: ar = ar.reshape(-1,nplex) else: ar = ar.reshape(-1,1) elif ar.ndim > 2: raise ValueError,"Expected 2-dim data" # Make sure dtype is an int type if ar.dtype.kind != 'i': ar = ar.astype(Int) # Check values if ar.size > 0: maxval = ar.max() if maxval > 2**31-1 or (ar.min() < 0 and not allow_negative): raise ValueError,"Negative or too large positive value in data" if nplex > 0 and ar.shape[1] != nplex: raise ValueError,"Expected data of plexitude %s" % nplex else: maxval = -1 ar = ar.reshape(0,nplex) # Transform 'subarr' from an ndarray to our new subclass. ar = ar.view(self) ## # Other data ar.eltype = eltype ar.inv = None # inverse index return ar def __array_finalize__(self,obj): # reset the attributes from passed original object # all extra attributes added in __new__ should be reset here self.eltype = getattr(obj, 'eltype', None) self.inv = getattr(obj, 'inv', None) def nelems(self): """Return the number of elements in the Connectivity table. Example: >>> Connectivity([[0,1,2],[0,1,3],[0,3,2],[0,5,3]]).nelems() 4 """ return self.shape[0] def nplex(self): """Return the plexitude of the elements in the Connectivity table. Example: >>> Connectivity([[0,1,2],[0,1,3],[0,3,2],[0,5,3]]).nplex() 3 """ return self.shape[1] def report(self): """Format a Connectivity table""" s = "Conn %s, eltype=%s" % (self.shape,self.eltype) return s + '\n' + ndarray.__str__(self) ############### Detecting degenerates and duplicates ############## def testDegenerate(self): """Flag the degenerate elements (rows). A degenerate element is a row which contains at least two equal values. Returns: a boolean array with shape (self.nelems(),). The True values flag the degenerate rows. Example: >>> Connectivity([[0,1,2],[0,1,1],[0,3,2]]).testDegenerate() array([False, True, False], dtype=bool) """ srt = asarray(self.copy()) srt.sort(axis=1) return (srt[:,:-1] == srt[:,1:]).any(axis=1) def listDegenerate(self): """Return a list with the numbers of the degenerate elements. Example: >>> Connectivity([[0,1,2],[0,1,1],[0,3,2]]).listDegenerate() array([1]) """ return arange(self.nelems())[self.testDegenerate()] def listNonDegenerate(self): """Return a list with the numbers of the non-degenerate elements. Example: >>> Connectivity([[0,1,2],[0,1,1],[0,3,2]]).listNonDegenerate() array([0, 2]) """ return arange(self.nelems())[~self.testDegenerate()] def removeDegenerate(self): """Remove the degenerate elements from a Connectivity table. Degenerate elements are rows with repeating values. Returns a Connectivity with the degenerate elements removed. Example: >>> Connectivity([[0,1,2],[0,1,1],[0,3,2]]).removeDegenerate() Connectivity([[0, 1, 2], [0, 3, 2]]) """ return self[~self.testDegenerate()] def reduceDegenerate(self,target=None): """Reduce degenerate elements to lower plexitude elements. This will try to reduce the degenerate elements of the Connectivity to a lower plexitude. This is only possible if an element type was set in the Connectivity. This function uses the data of the Element database in :mod:`elements`. If a target element type is given, only the reductions to that element type are performed. Else, all the target element types for which a reduction scheme is available, will be tried. Returns: A list of Connectivities of which the first one contains the originally non-degenerate elements and the last one contains the elements that could not be reduced and may be empty. If the original Connectivity does not have an element type set, or the element type does not have any reduction schemes defined, a list with only the original is returned. Remark: If the Connectivity is part of a Mesh, you should use the Mesh.reduceDegenerate method instead, as that one will preserve the property numbers into the resulting Meshes. Example: >>> C = Connectivity([[0,1,2],[0,1,1],[0,3,2]],eltype='line3') >>> print C.reduceDegenerate() [Connectivity([[0, 1]]), Connectivity([[0, 1, 2], [0, 3, 2]])] """ from elements import elementType if self.eltype is None: return [ self ] eltype = elementType(self.eltype) if not hasattr(eltype,'degenerate'): return [ self ] # get all reductions for this eltype strategies = eltype.degenerate # if target, keep only those leading to target if target is not None: s = strategies.get(target,[]) if s: strategies = {target:s} else: strategies = {} if not strategies: return [self] e = self ML = [] for totype in strategies: elems = [] for conditions,selector in strategies[totype]: cond = array(conditions) w = (e[:,cond[:,0]] == e[:,cond[:,1]]).all(axis=1) sel = where(w)[0] if len(sel) > 0: elems.append(e[sel][:,selector]) # remove the reduced elems from m e = e[~w] if e.nelems() == 0: break if elems: elems = concatenate(elems) ML.append(Connectivity(elems,eltype=totype)) if e.nelems() == 0: break ML.append(e) return ML def testDuplicate(self,permutations=True): # This algorithm is faster than encode, # but for nplex=2 enmagic2 would probably still be faster. """Test the Connectivity list for duplicates. By default, duplicates are elements that consist of the same set of nodes, in any particular order. Setting permutations to False will only find the duplicate rows that have matching values at every position. This function returns a tuple with two arrays: - an index used to sort the elements - a flags array with the value True for indices of the unique elements and False for those of the duplicates. Example: >>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).testDuplicate() (array([0, 1, 2]), Connectivity([ True, False, True], dtype=bool)) """ if permutations: C = self.copy() C.sort(axis=1) else: C = self ind = sortByColumns(C) C = C.take(ind,axis=0) ok = (C != roll(C,1,axis=0)).any(axis=1) if not ok[0]: # all duplicates -> should result in one unique element ok[0] = True return ind,ok def listUnique(self,permutations=True): """Return a list with the numbers of the unique elements. Example: >>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).listUnique() array([0, 2]) """ ind,ok = self.testDuplicate(permutations) return ind[ok] def listDuplicate(self,permutations=True): """Return a list with the numbers of the duplicate elements. Example: >>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).listDuplicate() array([1]) """ ind,ok = self.testDuplicate(permutations) return ind[~ok] def removeDuplicate(self,permutations=True): """Remove duplicate elements from a Connectivity list. By default, duplicates are elements that consist of the same set of nodes, in any particular order. Setting permutations to False will only remove the duplicate rows that have matching values at matching positions. Returns a new Connectivity with the duplicate elements removed. Example: >>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).removeDuplicate() Connectivity([[0, 1, 2], [0, 3, 2]]) """ ind,ok = self.testDuplicate(permutations) return self[ind[ok]] def reorder(self,order='nodes'): """Reorder the elements of a Connectivity in a specified order. This does not actually reorder the elements itself, but returns an index with the order of the rows (elements) in the connectivity table that meets the specified requirements. Parameters: - `order`: specifies how to reorder the elements. It is either one of the special string values defined below, or else it is an index with length equal to the number of elements. The index should be a permutation of the numbers in `range(self.nelems()`. Each value gives of the number of the old element that should be placed at this position. Thus, the order values are the old element numbers on the position of the new element number. `order` can also take one of the following predefined values, resulting in the corresponding renumbering scheme being generated: - 'nodes': the elements are renumbered in order of their appearance in the inverse index, i.e. first are the elements connected to node 0, then the as yet unlisted elements connected to node 1, etc. - 'random': the elements are randomly renumbered. - 'reverse': the elements are renumbered in reverse order. Returns: A 1-D integer array which is a permutation of `arange(self.nelems()`, such that taking the elements in this order will produce a Connectivity reordered as requested. In case an explicit order was specified as input, this order is returned after checking that it is indeed a permutation of `range(self.nelems()`. Example: >>> A = Connectivity([[1,2],[2,3],[3,0],[0,1]]) >>> A[A.reorder('reverse')] Connectivity([[0, 1], [3, 0], [2, 3], [1, 2]]) >>> A.reorder('nodes') array([3, 2, 0, 1]) >>> A[A.reorder([2,3,0,1])] Connectivity([[3, 0], [0, 1], [1, 2], [2, 3]]) """ if order == 'nodes': a = sort(self,axis=-1) # first sort rows order = sortByColumns(a) elif order == 'reverse': order = arange(self.nelems()-1,-1,-1) elif order == 'random': order = random.permutation(self.nelems()) else: order = asarray(order) if not (order.dtype.kind == 'i' and \ (sort(order) == arange(order.size)).all()): raise ValueError,"order should be a permutation of range(%s)" % self.nelems() return order def inverse(self): """Return the inverse index of a Connectivity table. This returns the inverse index of the Connectivity, as computed by :func:`arraytools.inverseIndex`. See Example: >>> Connectivity([[0,1,2],[0,1,3],[0,3,2]]).inverse() array([[ 0, 1, 2], [-1, 0, 1], [-1, 0, 2], [-1, 1, 2]]) """ if self.inv is None: if self.size > 0: self.inv = inverseIndex(self) else: self.inv = Connectivity() return self.inv def adjacency(self,kind='e'): """Return a table of adjacent items. Returns an element adjacency table (kind='e') or node adjacency table (kind='n'). An element `i` is said to be ajacent to element `j`, if the two elements have at least one common node. A node `i` is said to be adjacent to node `j`, if there is at least one element containing both nodes. Parameters: - `kind`: 'e' or 'n', requesting resp. element or node adjacency. Returns: an integer array with shape (nr,nc), where row `i` holds a sorted list of all the items that are adjacent to item `i`, padded with -1 values to create an equal list length for all items. Example: >>> Connectivity([[0,1],[0,2],[1,3],[0,5]]).adjacency('e') array([[ 1, 2, 3], [-1, 0, 3], [-1, -1, 0], [-1, 0, 1]]) >>> Connectivity([[0,1],[0,2],[1,3],[0,5]]).adjacency('n') array([[ 1, 2, 5], [-1, 0, 3], [-1, -1, 0], [-1, -1, 1], [-1, -1, -1], [-1, -1, 0]]) >>> Connectivity([[0,1,2],[0,1,3],[2,4,5]]).adjacency('n') array([[-1, 1, 2, 3], [-1, 0, 2, 3], [ 0, 1, 4, 5], [-1, -1, 0, 1], [-1, -1, 2, 5], [-1, -1, 2, 4]]) """ inv = self.inverse() if kind == 'e': adj = inv[self].reshape((self.nelems(),-1)) elif kind == 'n': adj = concatenate([where(inv>=0,self[:,i][inv],inv) for i in range(self.nplex())],axis=1) else: raise ValueError,"kind should be 'e' or 'n', got %s" % str(kind) return reduceAdjacency(adj) ######### Creating intermediate levels ################### def selectNodes(self,selector): """Return a :class:`Connectivity` containing subsets of the nodes. Parameters: - `selector`: an object that can be converted to a 1-dim or 2-dim int array. Examples are a tuple of local node numbers, or a list of such tuples all having the same length. Each row of `selector` holds a list of the local node numbers that should be retained in the new Connectivity table. Returns a :class:`Connectivity` object with shape `(self.nelems*selector.nelems,selector.nplex)`. This function does not collapse the duplicate elements. The eltype of the result is equal to that of the selector, possibly None. Example: >>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).selectNodes([[0,1],[0,2]]) Connectivity([[0, 1], [0, 2], [0, 2], [0, 1], [0, 3], [0, 2]]) """ sel = Connectivity(selector) try: eltype = sel.eltype except: eltype = None if sel.size > 0: return Connectivity(self[:,sel].reshape(-1,sel.nplex()),eltype=eltype) else: return Connectivity() def insertLevel(self,selector,lower_only=False): """Insert an extra hierarchical level in a Connectivity table. A Connectivity table identifies higher hierchical entities in function of lower ones. This method inserts an extra hierarchical level. For example, if you have volumes defined in function of points, you can insert an intermediate level of edges, or faces. Multiple intermediate level entities may be created from each element. Parameters: - `selector`: an object that can be converted to a 1-dim or 2-dim integer array. Examples are a tuple of local node numbers, or a list of such tuples all having the same length. Each row of `selector` holds a list of the local node numbers that should be retained in the new Connectivity table. - `lower_only`: if True, only the definition of the new (lower) entities is returned, complete without removing duplicates. This is equivalent to using :meth:`selectNodes`, which is prefered when you do not need the higher level info. Return value: a tuple of two Connectivities `hi`,`lo`, where: - `hi`: defines the original elements in function of the intermediate level ones, - `lo`: defines the intermediate level items in function of the lowest level ones (the original nodes). If the `selector` has an `eltype` attribute, then `lo` will inherit the same `eltype` value. Intermediate level items that consist of the same items in any permutation order are collapsed to single items. The low level items respect the numbering order inside the original elements, but it is undefined which of the collapsed sequences is returned. Because the precise order of the data in the collapsed rows is lost, it is in general not possible to restore the exact original table from the two result tables. See however :meth:`Mesh.getBorder` for an application where an inverse operation is possible, because the border only contains unique rows. See also :meth:`Mesh.combine`, which is an almost inverse operation for the general case, if the selector is complete. The resulting rows may however be permutations of the original. Example: >>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).insertLevel([[0,1],[1,2],[2,0]]) (Connectivity([[0, 3, 1], [1, 3, 0], [2, 4, 1]]), Connectivity([[0, 1], [2, 0], [0, 3], [1, 2], [3, 2]])) """ sel = Connectivity(selector) lo = self.selectNodes(sel) if lo.size > 0: uniq,uniqid = uniqueRows(lo,permutations=True) hi = Connectivity(uniqid.reshape(-1,sel.nelems())) lo = lo[uniq] else: hi = lo = Connectivity() # # PUT THIS BEHIND THE SELECTION, BECAUSE IT LOOSES THE 'eltype' # ** PROBABLY SOLVED BY __finalize__, so could go up # if hasattr(sel,'eltype'): lo.eltype = sel.eltype return hi,lo # BV: This is currently far from general!!! # should probably be move to Mesh/TriSurface if needed there def combine(self,lo): """Combine two hierarchical Connectivity levels to a single one. self and lo are two hierarchical Connectivity tables, representing higher and lower level respectively. This means that the elements of self hold numbers which point into lo to obtain the lowest level items. *In the current implementation, the plexitude of lo should be 2!* As an example, in a structure of triangles, hi could represent triangles defined by 3 edges and lo could represent edges defined by 2 vertices. This method will then result in a table with plexitude 3 defining the triangles in function of the vertices. This is the inverse operation of :meth:`insertLevel` with a selector which is complete. The algorithm only works if all vertex numbers of an element are unique. Example: >>> hi,lo = Connectivity([[0,1,2],[0,2,1],[0,3,2]]).insertLevel([[0,1],[1,2],[2,0]]) >>> hi.combine(lo) Connectivity([[0, 1, 2], [0, 2, 1], [0, 3, 2]]) """ lo = Connectivity(lo) if self.shape[1] < 2 or lo.shape[1] != 2: raise ValueError,"Can only combine plex>=2 with plex==2" elems = lo[self] elems1 = roll(elems,-1,axis=1) for i in range(elems.shape[1]): flags = (elems[:,i,1] != elems1[:,i,0]) * (elems[:,i,1] != elems1[:,i,1]) elems[flags,i] = roll(elems[flags,i],1,axis=1) return Connectivity(elems[:,:,0]) def resolve(self): """Resolve the connectivity into plex-2 connections. Creates a Connectivity table with a plex-2 (edge) connection between any two nodes that are connected to a common element. There is no point in resolving a plexitude 2 structure. Plexitudes lower than 2 can not be resolved. Returns a plex-2 Connectivity with all connections between node pairs. In each element the nodes are sorted. Example: >>> print([ i for i in combinations(range(3),2) ]) [(0, 1), (0, 2), (1, 2)] >>> Connectivity([[0,1,2],[0,2,1],[0,3,2]]).resolve() Connectivity([[0, 1], [0, 2], [0, 3], [1, 2], [2, 3]]) """ ind = [ i for i in combinations(range(self.nplex()),2) ] hi,lo = self.insertLevel(ind) lo.sort(axis=1) ind = sortByColumns(lo) return lo[ind] # BV: UNTESTED ! def reorderNodes(schemes,reverse=False): """_Convert Connectivity to/from foreign node numbering schemes. The order in which the element's nodes are numbered internally in pyFormex may be different than the numbering scheme used in external software packages. To allow correct export/import to/from other software, the nodes have to be renumbered. This function provides such a facility. Parameters: - `schemes`: a dictionary having pyFormex element names as keys and the matching nodal permutation arrays as values. The length of the array should match the plexitude of the Connectivity. - `reverse`: if True, the conversion is from external to internal. In this case, the Connectivity's eltype is interpreted as the pyFormex target element type (and should be set beforehand). Returns: - If the Connectivity has an element type and `scheme` has a key matching the element's name, a Connectivity with the renumbered elements is returned. - If `reverse` is False (default), the renumbering is done according to the permutation given by the `scheme` value matching the element name and the returned Connectivity will have no element type. - If `reverse` is True, the permutation scheme is reversed prior to using it. The target element type is retained in the returned Connectivity. - If the Connectivity has no element type or `scheme` has no matching key, the input Connectivity is returned unchanged. """ if self.eltype is not None: eltype = ElementType(self.eltype) key = eltype.name() if scheme.haskey(key): print 'key = %s' % key trl = scheme[key] print 'trl = %s' % trl elems = self[trl] if not reverse: delattr(self,'eltype') return elems return self ####################################################################### # class and static methods # @staticmethod def connect(clist,nodid=None,bias=None,loop=False): """Connect nodes from multiple Connectivity objects. clist is a list of Connectivities, nodid is an optional list of node indices and bias is an optional list of element bias values. All lists should have the same length. The returned Connectivity has a plexitude equal to the number of Connectivities in clist. Each element of the new Connectivity consist of a node from the corresponding element of each of the Connectivities in clist. By default this will be the first node of that element, but a nodid list may be given to specify the node id to be used for each of the Connectivities. Finally, a list of bias values may be given to specify an offset in element number for the subsequent Connectivities. If loop==False, the length of the Connectivity will be the minimum length of the Connectivities in clist, each minus its respective bias. By setting loop=True however, each Connectivity will loop around if its end is encountered, and the length of the result is the maximum length in clist. Example: >>> a = Connectivity([[0,1],[2,3],[4,5]]) >>> b = Connectivity([[10,11,12],[13,14,15]]) >>> c = Connectivity([[20,21],[22,23]]) >>> print(Connectivity.connect([a,b,c])) [[ 0 10 20] [ 2 13 22]] >>> print(Connectivity.connect([a,b,c],nodid=[1,0,1])) [[ 1 10 21] [ 3 13 23]] >>> print(Connectivity.connect([a,b,c],bias=[1,0,1])) [[ 2 10 22]] >>> print(Connectivity.connect([a,b,c],bias=[1,0,1],loop=True)) [[ 2 10 22] [ 4 13 20] [ 0 10 22]] """ try: m = len(clist) for i in range(m): if isinstance(clist[i],Connectivity): pass elif isinstance(clist[i],ndarray): clist[i] = Connectivity(clist[i]) else: raise TypeError except TypeError: raise TypeError,'Connectivity.connect(): first argument should be a list of Connectivities' if not nodid: nodid = [ 0 for i in range(m) ] if not bias: bias = [ 0 for i in range(m) ] if loop: n = max([ clist[i].nelems() for i in range(m) ]) else: n = min([ clist[i].nelems() - bias[i] for i in range(m) ]) f = zeros((n,m),dtype=Int) for i,j,k in zip(range(m),nodid,bias): v = clist[i][k:k+n,j] if loop and k > 0: v = concatenate([v,clist[i][:k,j]]) f[:,i] = resize(v,(n)) return Connectivity(f) ###################################################################### # BV: the methods below should probably be removed, # after a check that they are not essential @deprecation("tangle has been renamed to combine") def tangle(self,*args,**kargs): return self.combine(*args,**kargs) @deprecation("untangle has been deprecated. Use insertLevel instead.") def untangle(self,ind): return self.insertLevel(ind) @deprecation(_future_deprecation) def encode(self,permutations=True,return_magic=False): """_Encode the element connectivities into single integer numbers. Each row of numbers is encoded into a single integer value, such that equal rows result in the same number and different rows yield different numbers. Furthermore, enough information can be kept to restore the original rows from these single integer numbers. This is seldom needed however, because the original data are available from the Connectivity table itself. - permutations: if True(default), two rows are considered equal if they contain the same numbers regardless of their order. If False, two rows are only equal if they contain the same numbers at the same position. - return_magic: if True, return a codes,magic tuple. The default is to return only the codes. Return value(s): - codes: an (nelems,) shaped array with the element code numbers, - magic: the information needed to restore the original rows from the codes. See Connectivity.decode() Example: >>> Connectivity([[0,1,2],[0,1,3],[0,3,2]]).encode(return_magic=True) (array([0, 1, 3]), [(2, array([0, 1]), array([2, 3])), (2, array([0]), array([1, 2]))]) *The use of this function is deprecated.* """ def compact_encode2(data): """Encode two columns of integers into a single column. This is like enmagic2 but results in smaller encoded values, because the original values are first replaced by indices into the sets of unique values. This encoding scheme is therefore usable for repeated application on multiple columns. The return value is the list of codes, the magic value used in encoding, and the two sets of uniq values for the columns, needed to restore the original data. Decoding can be done with compact_decode2. """ # We could use a single compaction vector? uniqa, posa = unique(data[:,0], return_inverse=True) uniqb, posb = unique(data[:,1], return_inverse=True) # We could insert the encoding directly here, # or use an encoding function with 2 arguments # to avoid the column_stack operation rt = column_stack([posa, posb]) codes, magic = enmagic2(rt) return codes,magic,uniqa,uniqb if permutations: data = self.copy() data.sort(axis=1) else: data = self magic = [] codes = data[:,0] for i in range(1,data.shape[1]): cols = column_stack([codes,data[:,i]]) codes,mag,uniqa,uniqb = compact_encode2(cols) # insert at the front so we can process in order magic.insert(0,(mag,uniqa,uniqb)) if return_magic: return codes,magic else: return codes @deprecation(_future_deprecation) @staticmethod def decode(codes,magic): """Decode element codes into a Connectivity table. This is the inverse operation of the Connectivity.encode() method. It recreates a Connectivity table from the (codes,magic) information. This is a static method, and should be invoked as ```Connectivity.decode(codes,magic)```. - codes: code numbers as returned by Connectivity.encode, or a subset thereof. - magic: the magic information as returned by Connectivity.encode, with argument return_magic=True. Returns a Connectivity table. Example: >>> Connectivity.decode(array([0,1,3]), [(2, array([0, 1]), array([2, 3])), (2, array([0]), array([1, 2]))]) Connectivity([[0, 1, 2], [0, 1, 3], [0, 2, 3]]) *The use of this function is deprecated.* """ def compact_decode2(codes,magic,uniqa,uniqb): """Decodes a single integer value into the original 2 values. This is the inverse operation of compact_encode2. Thus compact_decode2(*compact_encode(data)) will return data. codes can be a subset of the encoded values, but the other 3 arguments should be exactly those from the compact_encode2 result. """ # decoding returns the indices into the uniq numberings pos = demagic2(codes,magic) return column_stack([uniqa[pos[:,0]],uniqb[pos[:,1]]]) data = [] for mag in magic: cols = compact_decode2(codes,mag[0],mag[1],mag[2]) data.insert(0,cols[:,1]) codes = cols[:,0] data.insert(0,codes) return Connectivity(column_stack(data)) ############################################################################ def sortAdjacency(adj): """Sort an adjacency table. An adjacency table is an integer array where each row lists the numbers of the items that are connected to the item with number equal to the row index. Rows are padded with -1 value to create rows of equal length. This function sorts the rows of the adjacency table in ascending order and removes all columns containing only -1 values. Paramaters: - `adj`: an 2-D integer array with values >=0 or -1 Returns: an integer array with shape (adj.shape[0],maxc), with maxc <= adj.shape[1], where the rows are sorted in ascending order and where columns with only -1 values are removed. Example: >>> a = array([[ 0, 2, 1, -1], ... [-1, 3, 1, -1], ... [ 3, -1, 0, 1], ... [-1, -1, -1, -1]]) >>> sortAdjacency(a) array([[ 0, 1, 2], [-1, 1, 3], [ 0, 1, 3], [-1, -1, -1]]) """ adj.sort(axis=-1) # sort rows maxc = adj.max(axis=0) # find maximum per column adj = adj[:,maxc>=0] # retain columns with non-negative maximum return adj def reduceAdjacency(adj): """Reduce an adjacency table. An adjacency table is an integer array where each row lists the numbers of the items that are connected to the item with number equal to the row index. Rows are padded with -1 value to create rows of equal length. A reduced adjacency table is one where each row: - does not contain the row index itself, - does not contain duplicates, - is sorted in ascending order, and that has at least one row without -1 value. Paramaters: - `adj`: an 2-D integer array with value >=0 or -1 Returns: an integer array with shape (adj.shape[0],maxc), with maxc <= adj.shape[1], where row `i` retains the unique non-negative numbers of the original array except the valu `i`, and is possibly padded with -1 values. Example: >>> a = array([[ 0, 0, 0, 1, 2, 5], ... [-1, 0, 1, -1, 1, 3], ... [-1, -1, 0, -1, -1, 2], ... [-1, -1, 1, -1, -1, 3], ... [-1, -1, -1, -1, -1, -1], ... [-1, -1, 0, -1, -1, 5]]) >>> reduceAdjacency(a) array([[ 1, 2, 5], [-1, 0, 3], [-1, -1, 0], [-1, -1, 1], [-1, -1, -1], [-1, -1, 0]]) """ adj = checkArrayDim(adj,2) n = adj.shape[0] adj[adj == arange(n).reshape(n,-1)] = -1 # remove the item i adj = sortAdjacency(adj) adj[adj[:,:-1] == adj[:,1:]] = -1 #remove duplicate items adj = sortAdjacency(adj) return adj # BV: This could become one of the schemes of Connectivity.reorder def findConnectedLineElems(elems): """Find a single path of connected line elems. This function is intended as a helper function for :func:`connectedLineElems`. It should probably not be used directly, because, as a side-effect, it changes the data in the `elems` argument. :func:`connectedLineElems` does not have this inconvenience. The function searches a Connectivity table for a chain of elements in which the first node of all but the first element is equal to the last node of the previous element. To create such a chain, elements may be reordered and the node sequence of an element may be reversed. Parameters: - `elems`: Connectivity-like. Any plexitude is allowed, but only the first and the last columna are relevant. Returns: a Connectivity with a single chain extracted from the input Connectivity. The result will not necessarily be the longest path. It will however contain the first element of the input table. As a side-effect, all elements contained in the output chain will have their entries in the input table `elems` changed to -1. Example: >>> findConnectedLineElems([[0,1],[1,2],[0,4],[4,2]]) Connectivity([[0, 1], [1, 2], [2, 4], [4, 0]]) >>> findConnectedLineElems([[0,1],[1,2],[0,4]]) Connectivity([[2, 1], [1, 0], [0, 4]]) >>> C = Connectivity([[0,1],[0,2],[0,3],[4,5]]) >>> findConnectedLineElems(C) Connectivity([[ 1, 0], [ 0, 2], [-1, -1], [-1, -1]]) >>> print C [[-1 -1] [-1 -1] [ 0 3] [ 4 5]] """ if not isinstance(elems,Connectivity): elems = Connectivity(elems) srt = zeros_like(elems) - 1 ie = 0 je = 0 rev = False k = elems[0][0] # remember startpoint while True: # Store an element that has been found ok if rev: srt[ie] = elems[je,::-1] else: srt[ie] = elems[je] elems[je] = -1 # Done with this one j = srt[ie][-1] # remember endpoint if j == k: break ie += 1 # Look for the next connected element (only thru fist or last node!) w = where(elems[:,[0,-1]] == j) #print w if w[0].size == 0: # Try reversing w = where(elems[:,[0,-1]] == k) #print w if w[0].size == 0: break else: j,k = k,j # reverse the table (colums and rows) srt[:ie] = srt[ie-1::-1,::-1].copy() # copy needed!! je = w[0][0] rev = w[-1][0] > 0 #check if the target node is the first or last return srt # BV: this could become a Connectivity function splitByConnection def connectedLineElems(elems): """Partition a segmented curve into connected segments. The input argument is a (nelems,2) shaped array of integers. Each row holds the two vertex numbers of a single line segment. The return value is a list of (nsegi,2) shaped array of integers. Example: >>> connectedLineElems([[0,1],[1,2],[0,4],[4,2]]) [Connectivity([[0, 1], [1, 2], [2, 4], [4, 0]])] >>> connectedLineElems([[0,1],[1,2],[0,4]]) [Connectivity([[2, 1], [1, 0], [0, 4]])] >>> connectedLineElems([[0,1],[0,2],[0,3],[4,5]]) [Connectivity([[1, 0], [0, 2]]), Connectivity([[0, 3]]), Connectivity([[4, 5]])] >>> connectedLineElems([[0,1,2],[2,0,3],[0,3,1],[4,5,2]]) [Connectivity([[3, 0, 2], [2, 1, 0], [0, 3, 1]]), Connectivity([[4, 5, 2]])] """ elems = Connectivity(elems).copy() # make copy to avoid side effects parts = [] while elems.size != 0: loop = findConnectedLineElems(elems) parts.append(loop[(loop!=-1).any(axis=1)]) elems = elems[(elems!=-1).any(axis=1)] return parts ## # This is an experimental replacement for connectedLineElems ## def splitConnectedLines(elems): ## """Split and order a set of line segments into connected componenents. ## Parameters: ## - `elems`: Connectivity-like with plexitude 2 (for eltype line2 ) or 3 (for eltype line3) ## Returns: a tuple (return_code,table): ## - `return_code`: an integer with one of the following values: ## - 0: the segments form a closed loop ## - 1: the segments form a single non-closed path ## - 2: the segments form multiple not connected paths ## - `table`: ## - if return_code is 0 or 1: a Connectivity table equivalent ## to the input, but with the elements and their nodes sorted in order. ## - if return_code is 2: a table with a singly connected part in the ## top rows, followed by -1 values for the unconnected elements. ## Example: ## >>> splitConnectedLines([[0,1],[1,2],[0,4],[4,2]]) ## (0, Connectivity([[0, 1], ## [1, 2], ## [2, 4], ## [4, 0]])) ## >>> splitConnectedLines([[0,1],[1,2],[0,4]]) ## (1, Connectivity([[2, 1], ## [1, 0], ## [0, 4]])) ## >>> splitConnectedLines([[0,1],[0,2],[0,3],[4,5]]) ## (2, Connectivity([[ 1, 0], ## [ 0, 2], ## [-1, -1], ## [-1, -1]])) ## """ ## elems = Connectivity(elems) ## def findOneComponent(elems): ## # Sorted list of elements: -1 is unused, >= 0 is element number ## srt = zeros(elems.shape[0],dtype=Int) -1 ## # Status of elements: ## # -1 is unused, ## # 0 is used in forward direction ## # 1 is used in backward direction ## sta = zeros(elems.shape[0],dtype=Int) - 1 ## ie = 0 ## je = 0 ## rev = False ## k = elems[0][0] # startpoint ## while True: ## # Store an element that has been found ok ## # store new endpoint in j ## # disable the element for further searches ## srt[ie] = je ## ie += 1 ## if rev: ## sta[je] = 1 ## j = elems[je][0] ## else: ## sta[je] = 0 ## j = elems[je][-1] ## elems[je] = [ -1, -1 ] ## # check for a loop ## if j == k: ## break ## # Look for the next connected element ## w = where(elems == j) ## #print w ## if w[0].size == 0: ## # Try other end of chain ## w = where(elems == k) ## #print w ## if w[0].size == 0: ## break ## else: ## j,k = k,j ## srt[:ie] = srt[ie-1::-1].copy() # copy needed !!! ## sta[:ie] = 1-sta[:ie] ## je = w[0][0] ## rev = w[1][0] == 1 ## if any(srt == -1): ## ret = 2 ## else: ## ret = 0 ## return ret,srt,sta ############################################################################ # # Deprecated # @deprecation(_future_deprecation) def connected(index,i): """Return the list of elements connected to element i. index is a (nr,nc) shaped integer array. An element j of index is said to be connected to element i, if element j has at least one (non-negative) value in common with element i. The result is a sorted list of unique element numbers, not containing the element number i itself. """ adj = concatenate([ where(ind==j)[0] for j in ind[i] if j >= 0 ]) return unique(adj[adj != i]) @deprecation(_future_deprecation) def enmagic2(cols,magic=0): """Encode two integer values into a single integer. cols is a (n,2) array of non-negative integers smaller than 2**31. The result is an (n) array of type int64, where each value is unique for each row of values in the input. The original input can be restored with demagic2. If a magic value larger than the maximum integer in the table is given, it will be used. If not, it will be taken as the maximum+1. A negative magic value triggers a fastencode scheme. The return value is a tuple with the codes and the magic used. *The use of this function is deprecated.* """ cmax = cols.max() if cmax >= 2**31 or cols.min() < 0: raise ValueError,"Integer value too high (>= 2**31) in enmagic2" if cols.ndim != 2 or cols.shape[1] != 2: raise ValueError,"Invalid array (type %s, shape %s) in enmagic2" % (cols.dtype,cols.shape) if magic < 0: magic = -1 cols = array(cols,copy=True,dtype=int32,order='C') codes = cols.view(int64) else: if magic <= cmax: magic = cmax + 1 codes = cols[:,0].astype(int64) * magic + cols[:,1] return codes,magic @deprecation(_future_deprecation) def demagic2(codes,magic): """Decode an integer number into two integers. The arguments `codes` and `magic` are the result of an enmagic2() operation. This will restore the original two values for the codes. A negative magic value flags the fastencode option. *The use of this function is deprecated.* """ if magic < 0: cols = codes.view(int32).reshape(-1,2) else: cols = column_stack([codes/magic,codes%magic]).astype(int32) return cols @deprecation("partitionSegmentedCurve is deprecated. Use connectedLineElems instead.") def partitionSegmentedCurve(*args,**kargs): return connectedLineElems(*args,**karg) # # BV: the following functions have to be checked for their need # and opportunity, and replaced by more general infrastrucuture # @deprecation(_future_deprecation) def adjacencyList(elems): """Create adjacency lists for 2-node elements.""" if len(elems.shape) != 2 or elems.shape[1] != 2: raise ValueError,"""Expected a set of 2-node elements.""" elems = elems.astype(int) ok = [ where(elems==i) for i in range(elems.max()+1) ] return [ list(elems[w[0],1-w[1]]) for w in ok ] @deprecation("adjacencyArray is deprecated. Use Connectivity().adjaccency('n')") def adjacencyArray(index,maxcon=5): return Connectivity(index).adjacency('n') # BV: Can this be replaced with a nodefront walker? def adjacencyArrays(elems,nsteps=1): """Create adjacency arrays for 2-node elements. elems is a (nr,2) shaped integer array. The result is a list of adjacency arrays, where row i of adjacency array j holds a sorted list of the nodes that are connected to node i via a shortest path of j elements, padded with -1 values to create an equal list length for all nodes. This is: [adj0, adj1, ..., adjj, ... , adjn] with n=nsteps. Example: >>> adjacencyArrays([[0,1],[1,2],[2,3],[3,4],[4,0]],3) [array([[0], [1], [2], [3], [4]]), array([[1, 4], [0, 2], [1, 3], [2, 4], [0, 3]]), array([[2, 3], [3, 4], [0, 4], [0, 1], [1, 2]]), array([], shape=(5, 0), dtype=int64)] """ elems = Connectivity(elems) if len(elems.shape) != 2 or elems.shape[1] != 2: raise ValueError,"""Expected a set of 2-node elements.""" if nsteps < 1: raise ValueError, """The shortest path should be at least 1.""" # Construct table of nodes connected to each node adj1 = elems.adjacency('n') m = adj1.shape[0] adj = [ arange(m).reshape(-1,1), adj1 ] nodes = adj1 step = 2 while step <= nsteps and nodes.size > 0: # Determine adjacent nodes t = nodes < 0 nodes = adj1[nodes] nodes[t] = -1 nodes = nodes.reshape((m,-1)) nodes = reduceAdjacency(nodes) # Remove nodes of lower adjacency ladj = concatenate(adj[-2:],-1) t = [ in1d(n,l,assume_unique=True) for n,l in zip (nodes,ladj) ] t = asarray(t) nodes[t] = -1 nodes = sortAdjacency(nodes) # Store current nodes adj.append(nodes) step += 1 return adj if __name__ == "__main__": C = Connectivity([[0,1],[2,3]],eltype='line2') print(C) print(C.eltype) print(C.report()) print(C[0].report()) print(C.selectNodes([1])) print(C.selectNodes([])) print(Connectivity().report()) print connectedLineElems([[0,1],[0,2],[0,3],[4,5]]) # End pyformex-0.8.6/pyformex/simple.py0000644000211500021150000003355511705104656016766 0ustar benebene00000000000000# $Id: simple.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Predefined geometries with a simple shape. This module contains some functions, data and classes for generating Formex structures representing simple geometric shapes. You need to import this module in your scripts to have access to its contents. """ from formex import * # A collection of Formex string input patterns to construct some simple # geometrical shapes Pattern = { 'line' : 'l:1', 'angle' : 'l:1+2', 'square' : 'l:1234', 'plus' : 'l:1+2+3+4', 'cross' : 'l:5+6+7+8', 'diamond' : 'l:/45678', 'rtriangle' : 'l:164', 'cube' : 'l:1234I/aI/bI/cI/41234', 'star' : 'l:1+2+3+4+5+6+7+8', 'star3d' : 'l:1+2+3+4+5+6+7+8+A+B+C+D+E+F+G+H+a+b+c+d+e+f+g+h' } def shape(name): """Return a Formex with one of the predefined named shapes. This is a convenience function returning a plex-2 Formex constructed from one of the patterns defined in the simple.Pattern dictionary. Currently, the following pattern names are defined: 'line', 'angle', 'square', 'plus', 'cross', 'diamond', 'rtriangle', 'cube', 'star', 'star3d'. See the Pattern example. """ return Formex(Pattern[name]) def regularGrid(x0,x1,nx): """Create a regular grid between points x0 and x1. x0 and x1 are n-dimensional points (usually 1D, 2D or 3D). The space between x0 and x1 is divided in nx equal parts. nx should have the same dimension as x0 and x1. The result is a rectangular grid of coordinates in an array with shape ( nx[0]+1, nx[1]+1, ..., n ). """ x0 = asarray(x0).ravel() x1 = asarray(x1).ravel() nx = asarray(nx).ravel() if x0.size != x1.size or nx.size != x0.size: raise ValueError,"Expected equally sized 1D arrays x0,x1,nx" if any(nx < 0): raise ValueError,"nx values should be >= 0" n = x0.size ind = indices(nx+1).reshape((n,-1)) shape = append(tuple(nx+1),n) nx[nx==0] = 1 jnd = nx.reshape((n,-1)) - ind ind = ind.transpose() jnd = jnd.transpose() return ( (x0*jnd + x1*ind) / nx ).reshape(shape) def point(x=0.,y=0.,z=0.): """Return a Formex which is a point, by default at the origin. Each of the coordinates can be specified and is zero by default. """ return Formex([[[x,y,z]]]) def line(p1=[0.,0.,0.],p2=[1.,0.,0.],n=1): """Return a Formex which is a line between two specified points. p1: first point, p2: second point The line is split up in n segments. """ return Formex([[p1,p2]]).divide(n) def rect(p1=[0.,0.,0.],p2=[1.,0.,0.],nx=1,ny=1): """Return a Formex which is a the circumference of a rectangle. p1 and p2 are two opposite corner points of the rectangle. The edges of the rectangle are in planes parallel to the z-axis. There will always be two opposite edges that are parallel with the x-axis. The other two will only be parallel with the y-axis if both points have the same z-value, but in any case they will be parallel with the y-z plane. The edges parallel with the x-axis are divide in nx parts, the other ones in ny parts. """ p1 = Coords(p1) p2 = Coords(p2) p12 = Coords([p2[0],p1[1],p1[2]]) p21 = Coords([p1[0],p2[1],p2[2]]) return Formex.concatenate([ line(p1,p12,nx), line(p12,p2,ny), line(p2,p21,nx), line(p21,p1,ny) ]) def rectangle(nx,ny,b=None,h=None,bias=0.,diag=None): """Return a Formex representing a rectangluar surface. The rectangle has a size(b,h) divided into (nx,ny) cells. The default b/h values are equal to nx/ny, resulting in a modular grid. The rectangle lies in the (x,y) plane, with one corner at [0,0,0]. By default, the elements are quads. By setting diag='u','d' of 'x', diagonals are added in /, resp. \ and both directions, to form triangles. """ if diag == 'x': base = Formex([[[0.0,0.0,0.0],[1.0,-1.0,0.0],[1.0,1.0,0.0]]]).rosette(4,90.).translate([-1.0,-1.0,0.0]).scale(0.5) else: base = Formex({ 'u': '3:012934', 'd': '3:016823' }.get(diag,'4:0123')) if b is None: sx = 1. else: sx = float(b)/nx if h is None: sy = 1. else: sy = float(h)/ny return base.replic2(nx,ny,bias=bias).scale([sx,sy,0.]) def circle(a1=2.,a2=0.,a3=360.,r=None,n=None,c=None,eltype='line2'): """A polygonal approximation to a circle or arc. All points generated by this function lie on a circle with unit radius at the origin in the x-y-plane. - `a1`: the angle enclosed between the start and end points of each line segment (dash angle). - `a2`: the angle enclosed between the start points of two subsequent line segments (module angle). If ``a2==0.0``, `a2` will be taken equal to `a1`. - `a3`: the total angle enclosed between the first point of the first segment and the end point of the last segment (arc angle). All angles are given in degrees and are measured in the direction from x- to y-axis. The first point of the first segment is always on the x-axis. The default values produce a full circle (approximately). If $a3 < 360$, the result is an arc. Large values of `a1` and `a2` result in polygones. Thus `circle(120.)` is an equilateral triangle and `circle(60.)` is regular hexagon. Remark that the default a2 == a1 produces a continuous line, while a2 > a1 results in a dashed line. Three optional arguments can be added to scale and position the circle in 3D space: - `r`: the radius of the circle - `n`: the normal on the plane of the circle - `c`: the center of the circle """ if a2 == 0.0: a2 = a1 ns = int(round(a3/a2)) a1 *= pi/180. if eltype=='line2': F = Formex([[[1.,0.,0.],[cos(a1),sin(a1),0.]]]).rosette(ns,a2,axis=2,point=[0.,0.,0.]) elif eltype=='line3': F = Formex([[[1.,0.,0.],[cos(a1/2.),sin(a1/2.),0.],[cos(a1),sin(a1),0.]]],eltype=eltype).rosette(ns,a2,axis=2,point=[0.,0.,0.]) if r is not None: F = F.scale(r) if n is not None: F = F.swapAxes(0,2).rotate(rotMatrix(n)) if c is not None: F = F.trl(c) return F def polygon(n): """A regular polygon with n sides. Creates the circumference of a regular polygon with $n$ sides, inscribed in a circle with radius 1 and center at the origin. The first point lies on the axis 0. All points are in the (0,1) plane. The return value is a plex-2 Formex. This function is equivalent to circle(360./n). """ return circle(360./n) def triangle(): """An equilateral triangle with base [0,1] on axis 0. Returns an equilateral triangle with side length 1. The first point is the origin, the second points is on the axis 0. The return value is a plex-3 Formex. """ return Formex([[[0.,0.,0.],[1.,0.,0.],[0.5,0.5*sqrt(3.),0.]]]) def quadraticCurve(x=None,n=8): """Create a collection of curves. x is a (3,3) shaped array of coordinates, specifying 3 points. Return an array with 2*n+1 points lying on the quadratic curve through the points x. Each of the intervals [x0,x1] and [x1,x2] will be divided in n segments. """ #if x.shape != (3,3): # raise ValueError,"Expected a (3,3) shaped array." # Interpolation functions in normalized coordinates (-1..1) h = [ lambda x: x*(x-1)/2, lambda x: (1+x)*(1-x), lambda x: x*(1+x)/2 ] t = arange(-n,n+1) / float(n) H = column_stack([ hi(t) for hi in h ]) return dot(H,x) def sphere2(nx,ny,r=1,bot=-90,top=90): """Return a sphere consisting of line elements. A sphere with radius r is modeled by a regular grid of nx longitude circles, ny latitude circles and their diagonals. The 3 sets of lines can be distinguished by their property number: 1: diagonals, 2: meridionals, 3: horizontals. The sphere caps can be cut off by specifying top and bottom latitude angles (measured in degrees from 0 at north pole to 180 at south pole. """ base = Formex('l:543',[1,2,3]) # single cell d = base.select([0]).replic2(nx,ny,1,1) # all diagonals m = base.select([1]).replic2(nx,ny,1,1) # all meridionals h = base.select([2]).replic2(nx,ny+1,1,1) # all horizontals grid = m+d+h s = float(top-bot) / ny return grid.translate([0,bot/s,1]).spherical(scale=[360./nx,s,r]) def sphere3(nx,ny,r=1,bot=-90,top=90): """Return a sphere consisting of surface triangles A sphere with radius r is modeled by the triangles fromed by a regular grid of nx longitude circles, ny latitude circles and their diagonals. The two sets of triangles can be distinguished by their property number: 1: horizontal at the bottom, 2: horizontal at the top. The sphere caps can be cut off by specifying top and bottom latitude angles (measured in degrees from 0 at north pole to 180 at south pole. """ base = Formex( [[[0,0,0],[1,0,0],[1,1,0]], [[1,1,0],[0,1,0],[0,0,0]]], [1,2]) grid = base.replic2(nx,ny,1,1) s = float(top-bot) / ny return grid.translate([0,bot/s,1]).spherical(scale=[360./nx,s,r]) # TODO: This should be renamed and probably use mesh.connect # TODO: or polylines def connectCurves(curve1,curve2,n): """Connect two curves to form a surface. curve1, curve2 are plex-2 Formices with the same number of elements. The two curves are connected by a surface of quadrilaterals, with n elements in the direction between the curves. """ if curve1.nelems() != curve2.nelems(): raise ValueError,"Both curves should have same number of elements" # Create the interpolations curves = interpolate(curve1,curve2,n).split(curve1.nelems()) quads = [ connect([c1,c1,c2,c2],nodid=[0,1,1,0]) for c1,c2 in zip(curves[:-1],curves[1:]) ] return Formex.concatenate(quads) def sector(r,t,nr,nt,h=0.,diag=None): """Constructs a Formex which is a sector of a circle/cone. A sector with radius r and angle t is modeled by dividing the radius in nr parts and the angle in nt parts and then creating straight line segments. If a nonzero value of h is given, a conical surface results with its top at the origin and the base circle of the cone at z=h. The default is for all points to be in the (x,y) plane. By default, a plex-4 Formex results. The central quads will collapse into triangles. If diag='up' or diag = 'down', all quads are divided by an up directed diagonal and a plex-3 Formex results. """ r = float(r) t = float(t) p = Formex(regularGrid([0.,0.,0.],[r,0.,0.],[nr,0,0]).reshape(-1,3)) if h != 0.: p = p.shear(2,0,h/r) q = p.rotate(t/nt) if type(diag) is str: diag = diag[0] if diag == 'u': F = connect([p,p,q],bias=[0,1,1]) + \ connect([p,q,q],bias=[1,2,1]) elif diag == 'd': F = connect([q,p,q],bias=[0,1,1]) + \ connect([p,p,q],bias=[1,2,1]) else: F = connect([p,p,q,q],bias=[0,1,1,0]) F = Formex.concatenate([F.rotate(i*t/nt) for i in range(nt)]) return F def cylinder(D,L,nt,nl,D1=None,angle=360.,bias=0.,diag=None): """Create a cylindrical, conical or truncated conical surface. Returns a Formex representing (an approximation of) a cylindrical or (possibly truncated) conical surface with its axis along the z-axis. The resulting surface is actually a prism or pyramid, and only becomes a good approximation of a cylinder or cone for high values of `nt`. Parameters: - `D`: base diameter (at z=0) of the cylinder/cone, - `L`: length (along z-axis) of the cylinder/cone, - `nt`: number of elements along the circumference, - `nl`: number of elements along the length, - `D1`: diameter at the top (z=L) of the cylinder/cone: if unspecified, it is taken equal to `D` and a cylinder results. Setting either `D1` or `D` to zero results in a cone, other values will create a truncated cone. - `diag`: by default, the elements are quads. Setting `diag` to 'u' or 'd' will put in an 'up' or 'down' diagonal to create triangles. """ C = rectangle(nl,nt,L,angle,bias=bias,diag=diag).trl(2,D/2.) if D1 is not None and D1 != D: C = C.shear(2,0,(D1-D)/L/2) return C.cylindrical(dir=[2,1,0]) def cuboid(xmin=[0.,0.,0.],xmax=[1.,1.,1.]): """Create a rectangular prism Creates a rectangular prism with faces parallel to the global axes through the points xmin and xmax. Returns a single element Formex with eltype 'hex8'. """ x0,y0,z0 = xmin x1,y1,z1 = xmax x = Coords([[ [x0,y0,z0], [x1,y0,z0], [x1,y1,z0], [x0,y1,z0], [x0,y0,z1], [x1,y0,z1], [x1,y1,z1], [x0,y1,z1], ]]) return Formex(x,eltype='hex8') # End pyformex-0.8.6/pyformex/examples/0000755000211500021150000000000011705105304016715 5ustar benebene00000000000000pyformex-0.8.6/pyformex/examples/Isopar.py0000644000211500021150000000625311705104656020543 0ustar benebene00000000000000# $Id: Isopar.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Isopar level = 'normal' topics = ['geometry'] techniques = ['dialog', 'color','isopar'] """ from plugins import isopar import simple import elements wireframe() ttype = ask("Select type of transformation",['Cancel','1D','2D','3D']) if not ttype or ttype == 'Cancel': exit() tdim = int(ttype[0]) # create a unit quadratic grid in tdim dimensions x = Coords(simple.regularGrid([0.]*tdim, [1.]*tdim, [2]*tdim)).reshape(-1,3) x1 = Formex(x) x2 = x1.copy() # move a few points if tdim == 1: eltype = 'line3' x2[1] = x2[1].rot(-22.5) x2[2] = x2[2].rot(22.5) elif tdim == 2: eltype = 'quad9' x2[5] = x2[2].rot(-22.5) x2[8] = x2[2].rot(-45.) x2[7] = x2[2].rot(-67.5) x2[4] = x2[8] * 0.6 else: eltype = 'hex27' tol = 0.01 d = x2.distanceFromPoint(x2[0]) w = where((d > 0.5+tol) * (d < 1.0 - tol))[0] # avoid error messages during projection errh = seterr(all='ignore') x2[w] = x2.projectOnSphere(0.5)[w] w = where(d > 1.+tol)[0] x2[w] = x2.projectOnSphere(1.)[w] seterr(**errh) clear() message('This is the set of nodes in natural coordinates') draw(x1,color=blue) message('This is the set of nodes in cartesian coordinates') draw(x2,color=red) drawNumbers(x2,color=red) drawNumbers(x1) n = 8 stype = ask("Select type of structure",['Cancel','1D','2D','3D']) if stype == 'Cancel': exit() sdim = int(stype[0]) if sdim == 1: F = simple.line([0.,0.,0.],[1.,1.,0.],10) elif sdim == 2: F = simple.rectangle(1,1,1.,1.) else: ## v = array(elements.Hex8.vertices) ## f = array(elements.Hex8.faces[1]) ## F = Formex(v[f]) F = elements.Hex8.toFormex() if sdim > 1: for i in range(sdim): F = F.replic(n,1.,dir=i) if sdim < tdim: F = F.trl(2,0.5) clear() message('This is the initial Formex') FA=draw(F) sz = F.sizes() if sdim < tdim: sz[sdim:tdim] = 2. x1 = x1.scale(sz) x2 = x2.scale(sz) G=F.isopar(eltype,x2.points(),x1.points()) G.setProp(1) message('This is the transformed Formex') draw(G) pause() undraw(FA) # End pyformex-0.8.6/pyformex/examples/MeshSmoothing.py0000644000211500021150000000564011705104656022071 0ustar benebene00000000000000# $Id: MeshSmoothing.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """MeshSmoothing level = 'normal' topics = ['geometry', 'mesh','illustration'] techniques = ['dialog','smooth] .. Description MeshSmoothing ============== This example illustrates the use of the mesh smoothing algrithm. The smoothing is applied to a hexahedral, tetrahedral, quadrilateral, and triangular mesh. """ from simple import cuboid from mesh import * clear() n = 12 # Number of elements in each direction tol = 4 # Amount of noise added to the coordinates iter = 10 # Number of smoothing iterations res = askItems(items=[ _I('n', n, text='Number of elements', itemtype='slider', min=2, max=24), _I('tol', tol, text='Noise', itemtype='slider', min=0, max=10), _I('iter', iter, text='Smoothing iterations', itemtype='slider', min=1, max=20), ]) if not res: exit() globals().update(res) tol /= 10. cube = cuboid().replic2(n, n, 1., 1.).rep(n, 1., 2).toMesh() cubeTet = cuboid().toMesh().convert('tet4') cubeTet += cubeTet.reflect() #Reflecting is needed to align the edges of adjacent tetrahedrons cubeTet += cubeTet.reflect(dir=1) cubeTet += cubeTet.reflect(dir=2) cubeTet = cubeTet.trl([1., 1., 1.]) cubeTet = cubeTet.toFormex().replic2(n/2, n/2, 2., 2.).rep(n/2., 2., 2).toMesh().convert('tet4').trl(1, -2.*n) surf = Formex(xpattern('0123', nplex=4)).replic2(2*n, 2*n, 1., 1.).scale(0.5).bump(2, [n/2., n/2., 1.], lambda x: 1.-x**2/n).toMesh().trl(1, -4.*n) surfTri = surf.convert('tri3').trl(1, -2.*n) for part in [cube, cubeTet, surf, surfTri]: noise = tol * random.random(part.coords.shape) - tol/2. noisy = Mesh(part.coords + noise, part.elems, 1).trl(0, 2.*n) smoothed = noisy.smooth(iterations=iter).trl(0, 2.*n) smoothed.setProp(2) draw([part, noisy, smoothed]) zoomAll() #End pyformex-0.8.6/pyformex/examples/X_truss.py0000644000211500021150000001435611705104656020760 0ustar benebene00000000000000# $Id: X_truss.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """X-shaped truss level = 'normal' topics = ['geometry'] techniques = ['color'] """ # This is needed if we want to import this module in another script from formex import * class X_truss(object): """An X-shaped truss girder. The truss has a constant height and consists of n modules of the same length. By default all modules are delimited by vertical bars and all crossing diagonals are connected to each other. There are options to not connect the diagonals and to not have interior verticals. This yields four possible layouts (bars are connected at o symbols): with interior verticals without interior verticals o-----o-----o-- o-----o-----o-- |\ /|\ /|\ |\ / \ / \ diagonals | \ / | \ / | \ | \ / \ / \ connected | o | o | | o o | / \ | / \ | / | / \ / \ / |/ \|/ \|/ |/ \ / \ / o-----o-----o-- o-----o-----o-- o-----o-----o-- o-----o-----o-- |\ /|\ /|\ |\ / \ / \ diagonals | \ / | \ / | \ | \ / \ / \ not | X | X | | X X connected | / \ | / \ | / | / \ / \ / |/ \|/ \|/ |/ \ / \ / o-----o-----o-- o-----o-----o-- """ def __init__(self,n_mod,mod_length,height,diagonals_connected=True,interior_verticals=True): """Creates an X-shaped truss. The truss has n_mod modules, each of length mod_length and height. By default diagonals are connected and there are interior verticals. The instance will have the following attributes: bot_nodes: a Formex with the n_nod+1 bottom nodes top_nodes: a Formex with the n_mod+1 top nodes mid_nodes: a Formex with the n_mod central nodes (or None) bot : a Formex with the n_mod bottom bars top : a Formex with the n_mod top bars vert : a Formex with the (n_mod+1) or 2 vertical bars mid1 : a Formex with the n_mod climbing (SW-NE) diagonals mid2 : a Formex with the n_mod descending (NW-SE) diagonals All Formex instances have their members ordered from left to right. The bottom left node has coordinates [0.,0.,0.] All nodes have z-coordinate 0. """ total_length = n_mod * mod_length # Nodes bot_nodes = Formex([[[0.0,0.0]]]).replic(n_mod+1,mod_length) top_nodes = bot_nodes.translate([0.0,height,0.0]) if diagonals_connected: mid_nodes = Formex(bot_nodes[:n_mod]).translate([0.5*mod_length,0.5*height, 0.0]) else: mid_nodes = None # Truss Members bot = connect([bot_nodes,bot_nodes],bias=[0,1]) top = connect([top_nodes,top_nodes],bias=[0,1]) if interior_verticals: vert = connect([bot_nodes,top_nodes]) else: vert1 = connect([Formex(bot_nodes[:1]),Formex(top_nodes[:1])]) vert2 = vert1.translate([total_length,0.,0.]) vert = vert1+vert2 if diagonals_connected: dia1 = connect([bot_nodes,mid_nodes]) + connect([mid_nodes,top_nodes],bias=[0,1]) dia2 = connect([top_nodes,mid_nodes]) + connect([mid_nodes,bot_nodes],bias=[0,1]) else: dia1 = connect([bot_nodes,top_nodes],bias=[0,1]) dia2 = connect([top_nodes,bot_nodes],bias=[0,1]) # save attributes self.n_mod = n_mod self.mod_length = mod_length self.height = height self.diagonals_connected = diagonals_connected self.interior_verticals = interior_verticals self.total_length = total_length self.bot_nodes = bot_nodes self.top_nodes = top_nodes self.mid_nodes = mid_nodes self.bot = bot self.top = top self.vert = vert self.dia1 = dia1 self.dia2 = dia2 def allNodes(self): """Return a Formex with all nodes.""" all_nodes = self.top_nodes + self.bot_nodes if self.mid_nodes: all_nodes += self.mid_nodes return all_nodes def allBars(self): """Return a Formex with all nodes.""" return self.bot+self.top+self.vert+self.dia1+self.dia2 if __name__ == 'draw': # This is executed when the example is launched from the GUI wireframe() reset() def example(diag=True,vert=True): truss = X_truss(12,2.35,2.65,diag,vert) truss.bot.setProp(3) truss.top.setProp(3) truss.vert.setProp(0) truss.dia1.setProp(1) truss.dia2.setProp(1) clear() draw(truss.allNodes(),wait=False) draw(truss.allBars()) for diag in [True,False]: for vert in [True,False]: example(diag,vert) # End pyformex-0.8.6/pyformex/examples/Casteljou.py0000644000211500021150000000560511705104656021237 0ustar benebene00000000000000# $Id: Casteljou.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Casteljou level = 'normal' topics = ['geometry', 'curve'] techniques = ['nurbs'] .. Description Casteljou ========= This example illustrates the deCasteljou algorithm for constructing a point on a Bezier curve. It also draws Bezier points computed from Bernstein polynomials, as in example BezierCurve. """ from plugins.curve import * from plugins.nurbs import * predefined = [ '2584', '58', '214', '184', '514', '1234', '51414336', '5858585858', '12345678', 'custom'] pat = None custom = '' casteljou = 0.5 showNurbs = False res = askItems([ dict(name='pat',value=pat,text='pattern',choices=predefined), dict(name='custom',value=custom), dict(name='casteljou',value=casteljou), dict(name='showNurbs',value=showNurbs), ],enablers=[('pat','custom','custom')]) if not res: exit() globals().update(res) if pat == 'custom': pat = custom if not pat.startswith('l:'): pat = 'l:' + pat C = Formex(pat).toCurve() clear() linewidth(2) flat() delay(0) draw(C,bbox='auto',view='front') draw(C.coords) drawNumbers(C.coords) setDrawOptions({'bbox':None}) if showNurbs: n = min(len(C.coords),len(colormap())) for d,c in zip(range(1,n),colormap()[:n-1]): N = NurbsCurve(C.coords,degree=d) draw(N,color=c) draw(N.knotPoints(),color=c,marksize=15) print d print d else: u = casteljou Q = deCasteljou(C.coords,u) delay(1) for q in Q[1:-1]: draw(PolyLine(q),color=red) draw(Q[-1],marksize=10) delay(0) n = 100 u = arange(n+1)*1.0/n if showNurbs: N = NurbsCurve(C.coords) x = N.pointsAt(u) draw(x) else: P = pointsOnBezierCurve(C.coords,u) print P.shape draw(Coords(P)) # End pyformex-0.8.6/pyformex/examples/WireStent.py0000644000211500021150000001264311705104656021232 0ustar benebene00000000000000# $Id: WireStent.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Wire Stent level = 'normal' topics = ['geometry'] techniques = ['dialog', 'persistence', 'color'] """ # needed if we import this from another script from formex import * class DoubleHelixStent(object): """Constructs a double helix wire stent. A stent is a tubular shape such as used for opening obstructed blood vessels. This stent is made frome sets of wires spiraling in two directions. The geometry is defined by the following parameters: L : approximate length of the stent De : external diameter of the stent D : average stent diameter d : wire diameter be : pitch angle (degrees) p : pitch nx : number of wires in one spiral set ny : number of modules in axial direction ds : extra distance between the wires (default is 0.0 for touching wires) dz : maximal distance of wire center to average cilinder nb : number of elements in a strut (a part of a wire between two crossings), default 4 The stent is created around the z-axis. By default, there will be connectors between the wires at each crossing. They can be switched off in the constructor. The returned formex has one set of wires with property 1, the other with property 3. The connectors have property 2. The wire set with property 1 is winding positively around the z-axis. """ def __init__(self,De,L,d,nx,be,ds=0.0,nb=4,connectors=True): """Create the Wire Stent.""" D = De - 2*d - ds r = 0.5*D dz = 0.5*(ds+d) p = math.pi*D*tand(be) nx = int(nx) ny = int(round(nx*L/p)) # The actual length may differ a bit from L # a single bumped strut, oriented along the x-axis bump_z=lambda x: 1.-(x/nb)**2 base = Formex('l:1').replic(nb,1.0).bump1(2,[0.,0.,dz],bump_z,0) # scale back to size 1. base = base.scale([1./nb,1./nb,1.]) # NE and SE directed struts NE = base.shear(1,0,1.) SE = base.reflect(2).shear(1,0,-1.) NE.setProp(1) SE.setProp(3) # a unit cell of crossing struts cell1 = (NE+SE).rosette(2,180) # add a connector between first points of NE and SE if connectors: cell1 += Formex([[NE[0][0],SE[0][0]]],2) # create its mirror cell2 = cell1.reflect(2).translate([2.,2.,0.]) base = cell1 + cell2 # reposition to base to origin [0,0] base = base.translate(-base.bbox()[0]) # Create the full pattern by replication dx,dy = base.bbox()[1][:2] F = base.replic2(nx,ny,dx,dy) # fold it into a cylinder self.F = F.translate([0.,0.,r]).cylindrical(dir=[2,0,1],scale=[1.,360./(nx*dx),p/nx/dy]) self.ny = ny def getFormex(self): """Return the Formex with all bar elements. This includes the elements along all the wires, as well as the connectors between the wires. """ return self.F def getWireAxes(self): """Return the wire axes as curves. The return value is two lists of curves (PolyLines), representing the individual wire axes for each wire direction. """ import connectivity M = self.F.toMesh() ML = [ M.withProp(i) for i in [1,3] ] wires = [ connectivity.connectedLineElems(Mi.elems) for Mi in ML ] wireaxes = [ [ Formex(M.coords[wi]).toCurve() for wi in wiresi ] for wiresi in wires ] return wireaxes if __name__ == "draw": # show an example wireframe() reset() res = askItems([ _I('L',80.,text='Length of the stent'), _I('D',10.,text='Diameter of the stent'), _I('n',12 ,text='Total number of wires'), _I('b',30.,text='Pitch angle of the wires'), _I('d',0.2,text='Diameter of the wires'), _I('show',itemtype='radio',choices=['Formex','Curves']), ]) if not res: exit() globals().update(res) if (n % 2) != 0: warning('Number of wires must be even!') exit() H = DoubleHelixStent(D,L,d,n,b) clear() if show=='Formex': draw(H.getFormex(),view='iso') else: view('iso') for w,c in zip(H.getWireAxes(),['black','magenta']): draw(w,color=c) #End pyformex-0.8.6/pyformex/examples/BarrelVault.py0000644000211500021150000000372411705104656021531 0ustar benebene00000000000000# $Id: BarrelVault.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Barrel Vault level = 'beginner' topics = ['frame'] techniques = ['dialog'] """ reset() wireframe() res = askItems([ dict(name='m',value=10,text='number of modules in axial direction'), dict(name='n',value=8,text='number of modules in tangential direction'), dict(name='r',value=10.,text='barrel radius'), dict(name='a',value=180.,text='barrel opening angle'), dict(name='l',value=30.,text='barrel length'), ]) if not res: exit() globals().update(res) # Diagonals d = Formex('l:5',1).rosette(4,90).translate([1,1,0]).replic2(m,n,2,2) # Longitudinals h = Formex('l:1',3).replic2(2*m,2*n+1,1,1) # End bars e = Formex('l:2',0).replic2(2,2*n,2*m,1) # Create barrel barrel = (d+h+e).rotate(90,1).translate(0,r).scale([1.,a/(2*n),l/(2*m)]).cylindrical() draw(barrel) # End pyformex-0.8.6/pyformex/examples/ParabolicTower.py0000644000211500021150000000360611705104656022222 0ustar benebene00000000000000# $Id: ParabolicTower.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Parabolic Tower level = 'beginner' topics = ['geometry'] techniques = ['color'] """ clear() global a,b,c,d # constants in lambda need to be made global h = 25. # height of tower h1 = 18. # height at neck of tower r = 10. # radius at base of tower r1 = 5. # radius at neck of tower m = 10 # number of sides at the base n = 8 # number of levels a = (r-r1)/h1**2; b = -2*a*h1; c = r; d = h/n g = lambda i: a*(d*i)**2 + b*d*i + c f = concatenate([ [[[g(i),i,i], [g(i+1),i-1,i+1]], [[g(i),i,i], [g(i+1),i+1,i+1]], [[g(i+1),i-1,i+1], [g(i+1),i+1,i+1]]] for i in range(n) ]) F = Formex(f,[3,0,1]).replic(m,2,dir=1) T = F.cylindrical(scale=[1,360./(2*m),d]) draw(T,view='bottom') pyformex-0.8.6/pyformex/examples/Hyparcap.py0000644000211500021150000000530511705104656021052 0ustar benebene00000000000000# $Id: Hyparcap.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # This example created (C) by Bart Desloovere (bart.desloovere@telenet.be) # """Hyparcap level = 'beginner' topics = ['geometry'] techniques = ['color'] """ clear() wireframe() a = 5 # verdeelparameter x = -((1-sqrt(5))/2) # gulden getal s = 30. # overspanning m = 5; b = 360./m # pentacap (script nog vervolledigen zodat m andere waarden kan aannemen) k1 = 0.035 # steilte hoek = (90.-b)/2 d = 2. # laagdikte c = (x*s+k1*s*s/2*sin(radians(2*hoek)))/(k1*s*cos(radians(hoek))+k1*s*sin(radians(hoek))) # pentacapvoorwaarde # compret van 1 blad T = Formex([[[-a,0,d],[-a+2,0,d]],[[-a,0,d],[1-a,3,d]],[[1-a,3,d],[2-a,0,d]]],1) B = Formex([[[1-a,-1,0],[3-a,-1,0]],[[1-a,-1,0],[2-a,2,0]],[[2-a,2,0],[3-a,-1,0]]],2) W1 = Formex([[[2-a,2,0],[1-a,3,d]],[[2-a,2,0],[3-a,3,d]],[[2-a,2,0],[2-a,0,d]]]) W2 = Formex([[[1-a,-1,0],[-a,0,d]],[[1-a,-1,0],[2-a,0,d]],[[1-a,-1,0],[1-a,-3,d]]]) W3 = Formex([[[0,3*a,d],[0,3*(a-1)-1,0]]]) top = T.replic2(a,a,2,3,bias=1,taper=-1).reflect(1,0,True).unique() bot = B.replic2(a-1,a-1,2,3,bias=1,taper=-1).reflect(1,-1,True).unique() web = W1.replic2(a-1,a-1,2,3,bias=1,taper=-1) + W2.replic2(a,a,2,-3,bias=1,taper=-1) + W3 blad = (top+bot+web).scale([1.,1./3,1.]).translate([0,a,0]) # herschalen vlakblad = blad.scale([s*sin(radians(b/2))/a,s*cos(radians(b/2))/a,1.]).rotate(-45.) # transleren en mappen op hyperbolische paraboloide (z=k1*x*y) vlakblad2=vlakblad.translate([-c,-c,0]) j=vlakblad2.map(lambda x,y,z:[x,y,k1*x*y]) #overige bladen genereren hyparcap=j.translate([c,c,0]).rosette(m,360/m,2,[0.,0.,0.]) draw(hyparcap) pyformex-0.8.6/pyformex/examples/Random.py0000644000211500021150000000310611705104656020520 0ustar benebene00000000000000# $Id: Random.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Random level = 'beginner' topics = ['surface'] techniques = ['color'] Creates random points, bars, triangles, quads, ... """ reset() setDrawOptions(dict(clear=True)) npoints = 30 p = arange(120) P = Formex(random.random((npoints,1,3)),p) draw(P,alpha=0.5) for n in range(2,5): F = connect([P for i in range(n)],bias=[i*(n-1) for i in range(n)],loop=True) F.setProp(p) draw(F,alpha=0.5) # End pyformex-0.8.6/pyformex/examples/Widgets.py0000644000211500021150000000340711705104656020712 0ustar benebene00000000000000# $Id: Widgets.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## print error("This is a simulated error, to demonstrate how an error message would be shown to the user.\nJust click OK and the error will go away.") print warning("""

    This is a warning.

    A warning draws attention of the user on special conditions.
    Remark that we can use plain text or html. """) print showInfo(""".. A text in ReST ============== - The lowest level of message box is the *info* level. It just displays information for the user. - ReST text is automatically detected if it starts with '..'. """) print ask("Answer this question with yes or no",['Yes','No']) # End pyformex-0.8.6/pyformex/examples/Bezier.py0000644000211500021150000000610411705104656020521 0ustar benebene00000000000000# $Id: Bezier.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Bezier level = 'beginner' topics = ['geometry','curve'] techniques = ['connect','color','solve'] """ def build_matrix(atoms,vars): """Build a matrix of functions of coords. atoms is a list of text strings each representing a function of variables defined in vars. vars is a dictionary where the keys are variable names appearing in atoms and the values are 1-dim arrays of values. All variables should have the same shape ! A matrix is returned where each row contains the values of atoms evaluated for one set of the variables. """ nval = len(vars[vars.keys()[0]]) aa = zeros((nval,len(atoms)),Float) for k,a in enumerate(atoms): res = eval(a,vars) aa[:,k] = eval(a,vars) return aa class Bezier(object): """A class representing a Bezier curve""" atoms = { 1 : ('1-t','t'), 2 : ('(1-t)**2','2*t*(1-t)','t**2'), 3 : ('(1-t)**3','3*t*(1-t)**2','3*t**2*(1-t)','t**3'), } def __init__(self,pts): """Create a bezier curve. pts is an Coords array with at least 2 points. A Bezier curve of degree npts-1 is constructed between the first and the last points. """ self.pts = pts def at(self,t): """Returns the points of the curve for parameter values t""" deg = self.pts.shape[0] - 1 aa = build_matrix(Bezier.atoms[deg],{'t':t}) return dot(aa,self.pts) def drawNumberedPoints(x,color): x = Formex(x) draw(x,color=color) drawNumbers(x,color=color) n = 100 t = arange(n+1)/float(n) clear() for d in arange(4) * 0.2: #clear() x = Coords([ [0.,0.], [1./3.,d], [2./3.,4*d**2], [1.,0.] ]) drawNumberedPoints(x,red) H = Formex(x.reshape(2,2,3)) draw(H,color=red) curve = Bezier(x) F = Formex(curve.at(t)) #draw(F) G = connect([F,F],bias=[0,1]) draw(G) #zoomAll() # End pyformex-0.8.6/pyformex/examples/Demos/0000755000211500021150000000000011705105304017764 5ustar benebene00000000000000pyformex-0.8.6/pyformex/examples/Demos/README0000644000211500021150000000016310750024015020642 0ustar benebene00000000000000This directory is intended for examples that are to be run in step mode and explain step by step what is going on. pyformex-0.8.6/pyformex/examples/Demos/Horse.py0000644000211500021150000000430111705104656021425 0ustar benebene00000000000000# $Id: Horse.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Horse level = 'normal' topics = ['surface'] techniques = ['animation','colors'] This script reads horse.pgf, transforms it into a surface, loads the surface plugin and cuts the horse in a number of surfaces. """ from plugins.trisurface import TriSurface reset() wireframe() x = 20 y = pf.canvas.height()-20 def say(text): global y drawText(text,x,y) y -=20 pf.message('Click Step to continue') say('A Horse Story...') y -= 10 F = Formex.read(getcfg('datadir')+'/horse.pgf') A = draw(F) pause() say('It\'s rather sad, but') smooth() pause() say('the horse was badly cut;') T = F.cutWithPlane([0.,0.,0.],[-1.,0.,0.],'+') undraw(A) A = draw(T) pause() say('to keep it stable,') undraw(A) A = draw(T.rotate(-80)) pause() say('the doctors were able') undraw(A) A = draw(T) pause() say('to add a mirrored clone:') T += T.reflect(0) undraw(A) A = draw(T) pause() say('A method as yet unknown!') colors = 0.1 * random.random((10,3)) for color in colors: B = draw(T,color=color) undraw(A) A = B sleep(0.5) pyformex-0.8.6/pyformex/examples/Demos/BarrelVault2.py0000644000211500021150000000605611705104656022663 0ustar benebene00000000000000# $Id: BarrelVault2.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """Barrel Vault level = 'beginner' topics = ['geometry'] techniques = ['stepmode','cylindrical'] """ clear() m=10 # number of modules in axial direction n=8 # number of modules in tangential direction r=10. # barrel radius a=180. # barrel opening angle l=30. # barrel length # Diagonals d = Formex([[[0.,0.,0.],[1.,1.,0.]]],1) # a single diagonal draw(d,view='front') d += d.reflect(0,1.) # reflect in x-direction and add to the original draw(d) d += d.reflect(1,1.) # reflect in y-direction draw(d) da = d.replic(m,2,0) # replicate in x-direction draw(da) da = da.replic(n,2,1) # replicate in y-direction draw(da) # Longitudinals h = Formex('l:1',3) # same as Formex([[[0.,0.,0.],[1.,0.,0.]]],3) draw(h) ha = h.replic2(2*m,2*n+1,1,1) # replicate in x- and y-direction draw(ha) # End bars e = Formex('l:2',0) # a unit vertical line draw(e) ea = e.replic2(2,2*n,2*m,1) # verticals only at the ends! draw(ea) # Choose better viewing angle for 3D view('iso') drawAxes() # Rotate the grid to (y,z) plane and give it an offset from the z-axis grid = (da+ha+ea).rotate(90,1).translate(0,r) draw(grid) # Scale the grid to the requested length and circumference of the barrel # The current height of the grid is 2*n # As the angle a is given in degrees, the circumference is circum = a*Deg*r scaled_grid = grid.scale([1.,circum/(2*n),l/(2*m)]) draw(scaled_grid) # Create barrel # The cylindrical transformation by default expects angles in degrees barrel = scaled_grid.cylindrical(scale=[1.,(1./r)/Deg,1.]) draw(barrel) print("Het aantal elementen is %s (plexitude %s)" % (barrel.nelems(),barrel.nplex())) print("De grootte van de coordinatenarray is %s" % str(barrel.shape())) # Remark: if we did not want to show the scaled grid, the creation # of the barrel could be simplified by combining the last two transformations: # barrel = grid.cylindrical(scale=[1.,a/(2*n),l/(2*m)]) # That's all, folks! pyformex-0.8.6/pyformex/examples/Demos/WireStent_Demo.py0000644000211500021150000002340411705104656023242 0ustar benebene00000000000000# $Id: WireStent_Demo.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # -*- coding: utf-8 -*- ## ## This file is part of pyFormex 0.8.5 (Sun Dec 4 21:24:46 CET 2011) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Wire Stent Demo level = 'advanced' topics = ['geometry'] techniques = ['dialog','colors'] This demo is intended for educational purposes. It is a rewrite of the WireStent.py example, adding lots of drawing instructions and comments. More details regarding the used definitions can be found in the pyFormex reference manual. """ # needed if we import this from another script from formex import * class DoubleHelixStent(object): """Constructs a double helix wire stent. A stent is a tubular shape such as used for opening obstructed blood vessels. This stent is made frome sets of wires spiraling in two directions. The geometry is defined by the following parameters: L : length of the stent De : external diameter of the stent D : average stent diameter d : wire diameter be : pitch angle (degrees) p : pitch nx : number of wires in one spiral set ny : number of modules in axial direction ds : extra distance between the wires (default is 0.0 for touching wires) dz : maximal distance of wire center to average cilinder nb : number of elements in a strut (a part of a wire between two crossings), default 4 The stent is created around the z-axis. By default, there will be connectors between the wires at each crossing. They can be switched off in the constructor. The returned formex has one set of wires with property 1, the other with property 3. The connectors have property 2. The wire set with property 1 is winding positively around the z-axis. """ def __init__(self,De,L,d,nx,be,ds=0.0,nb=4,connectors=True): """Create the Wire Stent.""" D = De - 2*d - ds r = 0.5*D dz = 0.5*(ds+d) p = math.pi*D*tand(be) nx = int(nx) ny = int(round(nx*L/p)) # The actual length may differ a bit from L # a single bumped strut, oriented along the x-axis bump_z=lambda x: 1.-(x/nb)**2 A=Formex('l:1',3) pf.message("\nThis Demo is intended for educational purposes by rewriting the WireStent.py \n example and adding lots of drawing instructions and comments. More details \n regarding the used definitions can be found in the Pyformex reference manual.") pf.message("\nStep 1: Create a Formex: a line of length 1 (with property 3) oriented along the X-axis\n A = Formex('l:1',3)") draw(A,view='bottom') pause() B=Formex(A.replic(nb,1.0),1) pf.message("Step 2: Copy the Formex nb times in the X(0)-direction\n B = Formex(A.replic(nb,1.0),1)") draw(B,view='last') pause() clear() base = Formex(B.bump1(2,[0.,0.,dz],bump_z,0),1) pf.message("Step 3: Create a bump in the Z(2)-direction\n base = Formex(B.bump1(2,[0.,0.,dz],bump_z,0),1)") draw(base,view='last') pause() clear() # scale back to size 1. base = base.scale([1./nb,1./nb,1.]) pf.message("Step 4: Rescale the base line to size 1\n base = base.scale([1./nb,1./nb,1.])") draw(base,view='last') pause() clear() # NE and SE directed struts NE = base.shear(1,0,1.) NE.setProp(1) pf.message("Step 5: Skew the base line to NE-direction\n NE = base.shear(1,0,1.)") ## The nxt two lines serve to rotate the camera up over 30°, i.e. 6 times the rotUp definition from cameraMenu pf.canvas.camera.rotate(30,1,0,0) pf.canvas.update() draw(NE) pause() clear() SE = base.reflect(2).shear(1,0,-1.) SE.setProp(3) pf.message("Step 6: Create a mirrored base line and skew it to SE-direction\n SE = base.reflect(2).shear(1,0,-1.)") draw(SE,view='last') pause() clear() cell=(NE+SE) pf.message("Step 7: Create the base cell by combining the NE and SE formices\n cell = (NE+SE)") draw(cell,view='last') pause() clear() # a unit cell of crossing struts cell1 = (cell).rosette(2,180) pf.message("Step 8: Create the base module (cell1) of two crossing wires by replicating the base cell by an angular rotation\n cell1 = (cell).rosette(2,180)") draw(cell1,view='last') pause() clear() # add a connector between first points of NE and SE if connectors: cell1 += Formex([[NE[0][0],SE[0][0]]],2) pf.message("Step 9: Add a connector between the first points of NE and SE of the base module\n cell1 += Formex([[NE[0][0],SE[0][0]]],2)") draw(cell1,view='last') pause() clear() # and create its mirror cell2 = cell1.reflect(2) pf.message("Step 10: Create a mirror in Z(2)-direction of the base module\n cell2 = cell1.reflect(2)") draw(cell2,view='last') pause() clear() # and move both to appropriate place self.cell1 = cell1.translate([1.,1.,0.]) self.cell2 = cell2.translate([-1.,-1.,0.]) # the base pattern cell1+cell2 now has size [-2,-2]..[2,2] # Create the full pattern by replication dx = 4. dy = 4. module=(self.cell1+self.cell2) pf.message("Step 11: Extend the base module with its mirrored and translated copy\n module = (self.cell1+self.cell2)") draw(module,view='last') pause() clear() F = module.replic2(nx,ny,dx,dy) pf.message("Step 12: Replicate the base module in both directions of the base plane\n F = module.replic2(nx,ny,dx,dy)") draw(F,view='last') pause() clear() # fold it into a cylinder C=F.translate([0.,0.,r]) pf.message("Step 13: Translate the full patern over the stent radius in Z-direction\n C=F.translate([0.,0.,r])") draw(C,view='last') pause() clear() self.F = C.cylindrical(dir=[2,0,1],scale=[1.,360./(nx*dx),p/nx/dy]) pf.message("Step 14: Roll the nearly planar grid into a cylinder\n self.F = C.cylindrical(dir=[2,0,1],scale=[1.,360./(nx*dx),p/nx/dy])") draw(self.F,view='front') pause() clear() draw(self.F,view='left') pause() clear() self.ny = ny def all(self): """Return the Formex with all bar elements.""" return self.F if __name__ == "draw": # show an example ## The following default values are obtained from Jedwab and Clerc (except for L=87.5 and b-30.85) D = 16.71 L = 40. d = 0.22 n = 12 b = 40 res = askItems([['Diameter',D],['Length',L],['WireDiam',d],['NWires',n], ['Pitch',b]]) D = float(res['Diameter']) L = float(res['Length']) d = float(res['WireDiam']) n = int(res['NWires']) ####### The following 3 lines are commented by MDB as n seems to be the number of wires in one direction and thus, this number may be uneven!!! ####### ## if (n % 2) != 0: ## warning('Number of wires must be even!') ## exit() b = float(res['Pitch']) H = DoubleHelixStent(D,L,d,n,b).all() # clear() draw(H,view='iso') # and save it in a lot of graphics formats ## if ack("Do you want to save this image (in lots of formats) ?"): ## for ext in [ 'bmp', 'jpg', 'pbm', 'png', 'ppm', 'xbm', 'xpm', 'eps', 'ps', 'pdf', 'tex' ]: ## image.save('WireStent.'+ext) pyformex-0.8.6/pyformex/examples/Shapes.py0000644000211500021150000000670511705104656020533 0ustar benebene00000000000000# $Id: Shapes.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Shapes This example illustrates how pyFormex can be used in education. I started this example when my daughter learned programming at school. She had to create this image using Java(Beans). It took days of wrestling with complex programming environments, compiling multiple files, endless checking and debugging java declaraions and code. I showed her how I could get to the same result with just a few lines of pyFormex code. Later I added some nice utilities to make it worthwile as a programming example. It would be nice now to add a GUI to create and position the shapes. """ def circle(n=60): a1 = 360./n return Formex([[[cosd(i*a1),sind(i*a1),0.] for i in range(n)]]) def triangle(): return Formex([[[0.,0.,0.],[1.,0.,0.],[0.5,0.5*sqrt(3.),0.]]]) def square(): return Formex([[[0.,0.,0.],[1.,0.,0.],[1.,1.,0.],[0.,1.,0.]]]) class Shape(Geometry): def __init__(self,shape,size,position,color): self.shape = shape self.size = resize(size,(3)) self.position = position self.color = color self.F = None self.A = None self.make() def make(self): self.F = globals()[self.shape]().scale(self.size).translate(self.position) def draw(self): self.A = draw(self.F,color=self.color) def hide(self): if self.A: undraw(self.A) self.A = None def redraw(self): A = self.A self.draw() undraw(A) def setSize(size): self.size = size self.make() def setPosition(pos): self.position = pos self.make() def setColor(color): self.hide() self.color = color self.draw() def move(self,direction,step): self.F = self.F.trl(direction,step) if __name__ == 'draw': clear() smooth() wall = Shape('square',[80.,60.],[10.,0.],'red') window = Shape('square',[10.,10.],[30.,30.],'white') roof = Shape('triangle',[100.,40.],[0.,60.],'green') sun = Shape('circle',10,[110.,80.],'yellow') delay(0) window.draw() wall.draw() roof.draw() sun.draw() zoomAll() # lower the sun delay(2) setDrawOptions({'bbox':None}) sun.hide() for y in range(10): sun.move(1,-8) sun.redraw() sun.hide() # End pyformex-0.8.6/pyformex/examples/Hesperia.py0000644000211500021150000007610611705104656021052 0ustar benebene00000000000000# $Id: Hesperia.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Hesperia level = 'advanced' topics = ['geometry', 'FEA', 'domes', 'surface'] techniques = ['menu', 'dialog', 'persistence', 'color'] """ import simple,utils from connectivity import Connectivity from plugins.trisurface import TriSurface from plugins.properties import * from plugins.fe_abq import * from gui.colorscale import ColorScale,ColorLegend from gui import menu,decors import time smooth() lights(False) def howto(): showInfo(""" How to use this menu? 1. If you want to save your work, start by opening a new project (File menu). 2. Create the geometry: it will be put in a Formex named 'F'. 3. Add (or read) properties to be used for the snow loading: enter a property number, then select the corresponding facets. Save the properties if you want to use them again later. 4. Create a Finite Element model : either shell or frame. 5. Perform the calculation: For a shell model: 5a. Create an Abaqus input file 5b. Send the job to the cluster (job menu) 5c. Get the results back when finished (job menu) For a frame model: 5a. Directly calculate using the CALPY module 6. Postprocess: For an Abaqus job: use the postproc menu For a Calpy job: use the hesperia menu """) def createGeometry(): global F # Construct a triangle of an icosahedron oriented with a vertex in # the y-direction, and divide its edges in n parts n = 6 # Add a few extra rows to close the gap after projection nplus = n+3 clear() # Start with an equilateral triangle in the x-y-plane A = simple.triangle() A.setProp(1) draw(A) # Modular size a,b,c = A.sizes() pf.message("Cell width: %s; height: %s" % (a,b)) # Create a mirrored triangle B = A.reflect(1) B.setProp(2) draw(B) # Replicate nplus times in 2 directions to create triangular pattern F = A.replic2(1,nplus,a,-b,0,1,bias=-a/2,taper=1) G = B.replic2(1,nplus-1,a,-b,0,1,bias=-a/2,taper=1) clear() F += G draw(F) # Get the top vertex and make it the origin P = F[0,-1] draw(Formex([P]),bbox=None) F = F.translate(-P) draw(F) # Now rotate around the x axis over an angle so that the projection on the # x-y plane is an isosceles triangle with top angle = 360/5 = 72 degrees. # The base angles thus are (180-72)/2 = 54 degrees. # Ratio of the height of the isosceles triangle over the icosaeder edge length. c = 0.5*tand(54.) angle = arccosd(tand(54.)/sqrt(3.)) pf.message("Rotation Ratio: %s; Angle: %s degrees" % (c,angle)) F = F.rotate(angle,0) clear() draw(F,colormap=['black','magenta','yellow','black']) # Project it on the circumscribing sphere # The sphere has radius ru golden_ratio = 0.5 * (1. + sqrt(5.)) ru = 0.5 * a * sqrt(golden_ratio * sqrt(5.)) pf.message("Radius of circumscribed sphere: %s" % ru) ru *= n C = [0.,0.,-ru] F = F.projectOnSphere(ru,center=C) draw(F) hx,hy,h = F.sizes() pf.message("Height of the dome: %s" % h) # The base circle goes through bottom corner of n-th row, # which will be the first point of the first triangle of the n-th row. # Draw the point to check it. i = (n-1)*n/2 P = F[i][0] draw(Formex([P]),marksize=10,bbox=None) # Get the radius of the base circle from the point's coordinates x,y,z = P rb = sqrt(x*x+y*y) # Give the base points a z-coordinate 0 F = F.translate([0.,0.,-z]) clear() draw(F) # Draw the base circle H = simple.circle().scale(rb) draw(H) # Determine intersections with base plane P = [0.,0.,0.] N = [0.,0.,1.] newprops = [ 5,6,6,None,4,None,None ] F = F.cutWithPlane(P,N,side='+',newprops=newprops)#,atol=0.0001) #clear() draw(F) # Finally, create a rosette to make the circle complete # and rotate 90 degrees to orient it like in the paper clear() F = F.rosette(5,72.).rotate(90) def cutOut(F,c,r): """Remove all elements of F contained in a sphere (c,r)""" d = F.distanceFromPoint(c) return F.select((d < r).any(axis=-1) == False) # Cut out the door: remove all members having a point less than # edge-length a away from the base point p1 = [rb,0.,0.] F = cutOut(F,p1,1.1*a*n/6) # a was a good size with n = 6 # Scale to the real geometry scale = 7000. / F.sizes()[2] pf.message("Applying scale factor %s " % scale) print F.bbox() F = F.scale(scale) print F.bbox() clear() draw(F,alpha=0.4) export({'F':F}) def assignProperties(): """Assign properties to the structure's facets""" # make sure we have only one actor clear() FA = draw(F) #drawNumbers(F) p = 0 while True: res = askItems([('Property',p)]) if not res: break p = res['Property'] sel = pickElements() if sel.has_key(0): pf.debug("PICKED NUMBERS:%s" % sel) F.prop[sel[0]] = p undraw(FA) FA = draw(F,view=None,bbox=None) def exportProperties(): """Save the current properties under a name""" res = askItems([('Property Name','p')]) if res: p = res['Property Name'] if not p.startswith('prop:'): p = "prop:%s" % p export({p:F.prop}) def selectProperties(): """Select one of the saved properties""" res = askItems([('Property Name','p')]) if res: p = res['Property Name'] if pf.PF.has_key(p): F.setProp(pf.PF[p]) def saveProperties(fn = None): """Save the current properties.""" if not fn: fn = askNewFilename(dirname,filter="Property files (*.prop)") if fn: F.prop.tofile(fn,sep=',') def readProperties(fn = None): """Read properties from file.""" if not fn: fn = askFilename(filter="Property files (*.prop)") if fn: p = fromfile(fn,sep=',') F.setProp(p) clear() draw(F) def connections(elems): """Create lists of connections to lower entities. Elems is an array giving the numbers of lower entities. The result is a sequence of maxnr+1 lists, where maxnr is the highest lower entity number. Each (possibly empty) list contains the numbers of the rows of elems that contain (at least) one value equal to the index of the list. """ return [ (i,list(where(elems==i)[0])) for i in unique(elems.flat) ] ##################################################################### def createFrameModel(): """Create the Finite Element Model. It is supposed here that the Geometry has been created and is available as a global variable F. """ wireframe() lights(False) # Turn the Formex structure into a TriSurface # This guarantees that element i of the Formex is element i of the TriSurface S = TriSurface(F) nodes = S.coords elems = S.elems # the triangles # Create edges and faces from edges print "The structure has %s nodes, %s edges and %s faces" % (S.ncoords(),S.nedges(),S.nfaces()) # Create the steel structure E = Formex(nodes[S.getEdges()]) clear() draw(E) # Get the tri elements that are part of a quadrilateral: prop = F.prop quadtri = S.getElemEdges()[prop==6] nquadtri = quadtri.shape[0] print "%s triangles are part of quadrilateral faces" % nquadtri if nquadtri > 0: # Create triangle definitions of the quadtri faces tri = Connectivity.tangle(quadtri,S.getEdges()) D = Formex(nodes[tri]) clear() flatwire() draw(D,color='yellow') conn = connections(quadtri) print conn # Filter out the single connection edges internal = [ c[0] for c in conn if len(c[1]) > 1 ] print "Internal edges in quadrilaterals: %s" % internal E = Formex(nodes[S.getEdges()],1) E.prop[internal] = 6 wireframe() clear() draw(E) # Remove internal edges tubes = S.getEdges()[E.prop != 6] print "Number of tube elements after removing %s internals: %s" % (len(internal),tubes.shape[0]) D = Formex(nodes[tubes],1) clear() draw(D) # Beam section and material properties b = 60 h = 100 t = 4 b1 = b-2*t h1 = h-2*t A = b*h - b1*h1 print b*h**3 I1 = (b*h**3 - b1*h1**3) / 12 I2 = (h*b**3 - h1*b1**3) / 12 I12 = 0 J = 4 * A**2 / (2*(b+h)/t) tube = { 'name':'tube', 'cross_section': A, 'moment_inertia_11': I1, 'moment_inertia_22': I2, 'moment_inertia_12': I12, 'torsional_constant': J } steel = { 'name':'steel', 'young_modulus' : 206000, 'shear_modulus' : 81500, 'density' : 7.85e-9, } print tube print steel tubesection = ElemSection(section=tube,material=steel) # Calculate the nodal loads # Area of triangles area,normals = S.areaNormals() print "Area:\n%s" % area # compute bar lengths bars = nodes[tubes] barV = bars[:,1,:] - bars[:,0,:] barL = sqrt((barV*barV).sum(axis=-1)) print "Member length:\n%s" % barL ### DEFINE LOAD CASE (ask user) ### res = askItems([('Steel',True), ('Glass',True), ('Snow',False), ('Solver',None,'select',['Calpy','Abaqus']), ]) if not res: return nlc = 0 for lc in [ 'Steel','Glass','Snow' ]: if res[lc]: nlc += 1 NODLoad = zeros((nlc,S.ncoords(),3)) nlc = 0 if res['Steel']: # the STEEL weight lwgt = steel['density'] * tube['cross_section'] * 9810 # mm/s**2 print "Weight per length %s" % lwgt # assemble steel weight load for e,L in zip(tubes,barL): NODLoad[nlc,e] += [ 0., 0., - L * lwgt / 2 ] nlc += 1 if res['Glass']: # the GLASS weight wgt = 450e-6 # N/mm**2 # assemble uniform glass load for e,a in zip(S.elems,area): NODLoad[nlc,e] += [ 0., 0., - a * wgt / 3 ] nlc += 1 if res['Snow']: # NON UNIFORM SNOW fn = 'hesperia-nieve.prop' snowp = fromfile(fn,sep=',') snow_uniform = 320e-6 # N/mm**2 snow_non_uniform = { 1:333e-6, 2:133e-6, 3:133e-6, 4:266e-6, 5:266e-6, 6:667e-6 } # assemble non-uniform snow load for e,a,p in zip(S.elems,area,snowp): NODLoad[nlc,e] += [ 0., 0., - a * snow_non_uniform[p] / 3] nlc += 1 # For Abaqus: put the nodal loads in the properties database print NODLoad PDB = PropertyDB() for lc in range(nlc): for i,P in enumerate(NODLoad[lc]): PDB.nodeProp(tag=lc,set=i,cload=[P[0],P[1],P[2],0.,0.,0.]) # Get support nodes botnodes = where(isClose(nodes[:,2], 0.0))[0] bot = nodes[botnodes] pf.message("There are %s support nodes." % bot.shape[0]) # Upper structure nnodes = nodes.shape[0] # node number offset ntubes = tubes.shape[0] # element number offset PDB.elemProp(set=arange(ntubes),section=tubesection,eltype='FRAME3D') # Create support systems (vertical beams) bot2 = bot + [ 0.,0.,-200.] # new nodes 200mm below bot botnodes2 = arange(botnodes.shape[0]) + nnodes # node numbers nodes = concatenate([nodes,bot2]) supports = column_stack([botnodes,botnodes2]) elems = concatenate([tubes,supports]) ## !!! ## THIS SHOULD BE FIXED !!! supportsection = ElemSection(material=steel,section={ 'name':'support', 'cross_section': A, 'moment_inertia_11': I1, 'moment_inertia_22': I2, 'moment_inertia_12': I12, 'torsional_constant': J }) PDB.elemProp(set=arange(ntubes,elems.shape[0]),section=supportsection,eltype='FRAME3D') # Finally, the botnodes2 get the support conditions botnodes = botnodes2 ## # Radial movement only ## np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## # No movement, since we left out the ring beam ## for i in botnodes: ## NodeProperty(i,bound=[1,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1]) # Draw the supports S = connect([Formex(bot),Formex(bot2)]) draw(S,color='black') if res['Solver'] == 'Calpy': fe_model = Dict(dict(solver='Calpy',nodes=nodes,elems=elems,prop=PDB,loads=NODLoad,botnodes=botnodes,nsteps=nlc)) else: fe_model = Dict(dict(solver='Abaqus',nodes=nodes,elems=elems,prop=PDB,botnodes=botnodes,nsteps=nlc)) export({'fe_model':fe_model}) print "FE model created and exported as 'fe_model'" #################### SHELL MODEL ######################################## def createShellModel(): """Create the Finite Element Model. It is supposed here that the Geometry has been created and is available as a global variable F. """ # Turn the Formex structure into a TriSurface # This guarantees that element i of the Formex is element i of the TriSurface S = TriSurface(F) print "The structure has %s nodes, %s edges and %s faces" % (S.ncoords(),S.nedges(),S.nfaces()) nodes = S.coords elems = S.elems # the triangles clear() draw(F) # Shell section and material properties # VALUES SHOULD BE SET CORRECTLY glass_plate = { 'name': 'glass_plate', 'sectiontype': 'shell', 'thickness': 18, 'material': 'glass', } glass = { 'name': 'glass', 'young_modulus': 72000, 'shear_modulus': 26200, 'density': 2.5e-9, # T/mm**3 } print glass_plate print glass glasssection = ElemSection(section=glass_plate,material=glass) PDB = PropertyDB() # All elements have same property: PDB.elemProp(set=arange(len(elems)),section=glasssection,eltype='STRI3') # Calculate the nodal loads # Area of triangles area,normals = S.areaNormals() print "Area:\n%s" % area ### DEFINE LOAD CASE (ask user) ### res = askItems([('Glass',True),('Snow',False)]) if not res: return step = 0 if res['Glass']: step += 1 NODLoad = zeros((S.ncoords(),3)) # add the GLASS weight wgt = 450e-6 # N/mm**2 # Or, calculate weight from density: # wgt = glass_plate['thickness'] * glass['density'] * 9810 # assemble uniform glass load for e,a in zip(S.elems,area): NODLoad[e] += [ 0., 0., - a * wgt / 3 ] # Put the nodal loads in the properties database for i,P in enumerate(NODLoad): PDB.nodeProp(tag=step,set=i,cload=[P[0],P[1],P[2],0.,0.,0.]) if res['Snow']: step += 1 NODLoad = zeros((S.ncoords(),3)) # add NON UNIFORM SNOW fn = 'hesperia-nieve.prop' snowp = fromfile(fn,sep=',') snow_uniform = 320e-6 # N/mm**2 snow_non_uniform = { 1:333e-6, 2:133e-6, 3:133e-6, 4:266e-6, 5:266e-6, 6:667e-6 } # assemble non-uniform snow load for e,a,p in zip(S.elems,area,snowp): NODLoad[e] += [ 0., 0., - a * snow_non_uniform[p] / 3] # Put the nodal loads in the properties database for i,P in enumerate(NODLoad): PDB.nodeProp(tag=step,set=[i],cload=[P[0],P[1],P[2],0.,0.,0.]) # Get support nodes botnodes = where(isClose(nodes[:,2], 0.0))[0] bot = nodes[botnodes].reshape((-1,1,3)) pf.message("There are %s support nodes." % bot.shape[0]) botofs = bot + [ 0.,0.,-0.2] bbot2 = concatenate([bot,botofs],axis=1) print bbot2.shape S = Formex(bbot2) draw(S) ## np_central_loaded = NodeProperty(3, displacement=[[1,radial_displacement]],coords='cylindrical',coordset=[0,0,0,0,0,1]) ## #np_transf = NodeProperty(0,coords='cylindrical',coordset=[0,0,0,0,0,1]) ## # Radial movement only ## np_fixed = NodeProperty(1,bound=[0,1,1,0,0,0],coords='cylindrical',coordset=[0,0,0,0,0,1]) # Since we left out the ring beam, we enforce no movement at the botnodes bc = PDB.nodeProp(set=botnodes,bound=[1,1,1,0,0,0],csys=CoordSystem('C',[0,0,0,0,0,1])) # And we record the name of the bottom nodes set botnodeset = Nset(bc.nr) fe_model = Dict(dict(nodes=nodes,elems=elems,prop=PDB,botnodeset=botnodeset,nsteps=step)) export({'fe_model':fe_model}) smooth() lights(False) ##################################################################### #### Analyze the structure using Abaqus #### def createAbaqusInput(): """Write the Abaqus input file. It is supposed that the Finite Element model has been created and exported under the name 'fe_model'. """ try: FE = named('fe_model') nodes = FE.nodes elems = FE.elems prop = FE.prop nsteps = FE.nsteps except: warning("I could not find the finite element model.\nMaybe you should try to create it first?") return # ask job name from user res = askItems([('JobName','hesperia_shell')]) if not res: return jobname = res['JobName'] if not jobname: print "No Job Name: writing to sys.stdout" jobname = None out = [ Output(type='history'), Output(type='field'), ] res = [ Result(kind='NODE',keys=['U','COORD']), Result(kind='ELEMENT',keys=['S'],pos='AVERAGED AT NODES'), Result(kind='ELEMENT',keys=['SINV'],pos='AVERAGED AT NODES'), Result(kind='ELEMENT',keys=['SF'],pos='AVERAGED AT NODES'), ] step1 = Step(time=[1.,1.,0.01,1.],nlgeom='no',tags=[1]) step2 = Step(time=[1.,1.,0.01,1.],nlgeom='no',tags=[2]) model = Model(nodes,elems) AbqData(model,prop,[step1,step2],out=out,res=res).write(jobname) ############################################################################# #### perform analysis with the calpy module #### def runCalpyAnalysis(): """Create data for Calpy analysis module and run Calpy on the data. While we could write an analysis file in the Calpy format and then run the Calpy program on it (like we did with Abaqus), we can (and do) take another road here: Calpy has a Python/numpy interface, allowing us to directly present the numerical data in arrays to the analysis module. It is supposed that the Finite Element model has been created and exported under the name 'fe_model'. """ ############################ # Load the needed calpy modules # You can prepend your own path here to override the installed calpy # sys.path[0:0] = ['/home/bene/prj/calpy'] from plugins import calpy_itf calpy_itf.check() import calpy print calpy calpy.options.optimize=True from calpy import fe_util,beam3d ############################ try: FE = named('fe_model') ## print FE.keys() ## nodes = FE.nodes ## elems = FE.elems ## prop = FE.prop ## nodloads = FE.loads ## botnodes = FE.botnodes ## nsteps = FE.nsteps except: warning("I could not find the finite element model.\nMaybe you should try to create it first?") return # ask job name from user res = askItems([('JobName','hesperia_frame'),('Verbose Mode',False)]) if not res: return jobname = res['JobName'] if not jobname: print "No Job Name: bailing out" return verbose = res['Verbose Mode'] nnod = FE.nodes.shape[0] nel = FE.elems.shape[0] print "Number of nodes: %s" % nnod print "Number of elements: %s" % nel # Create an extra node for beam orientations # # !!! This is ok for the support beams, but for the structural beams # !!! this should be changed to the center of the sphere !!! extra_node = array([[0.0,0.0,0.0]]) coords = concatenate([FE.nodes,extra_node]) nnod = coords.shape[0] print "Adding a node for orientation: %s" % nnod # We extract the materials/sections from the property database matprops = FE.prop.getProp(kind='e',attr=['section']) # Beam Properties in Calpy consist of 7 values: # E, G, rho, A, Izz, Iyy, J # The beam y-axis lies in the plane of the 3 nodes i,j,k. mats = array([[mat.young_modulus, mat.shear_modulus, mat.density, mat.cross_section, mat.moment_inertia_11, mat.moment_inertia_22, mat.moment_inertia_12, ] for mat in matprops]) if verbose: print "Calpy.materials" print mats # Create element definitions: # In calpy, each beam element is represented by 4 integer numbers: # i j k matnr, # where i,j are the node numbers, # k is an extra node for specifying orientation of beam (around its axis), # matnr refers to the material/section properties (i.e. the row nr in mats) # Also notice that Calpy numbering starts at 1, not at 0 as customary # in pyFormex; therefore we add 1 to elems. # The third node for all beams is the last (extra) node, numbered nnod. # We need to reshape tubeprops to allow concatenation matnr = zeros(nel,dtype=int32) for i,mat in enumerate(matprops): # proces in same order as above! matnr[mat.set] = i+1 elements = concatenate([FE.elems + 1, # the normal node numbers nnod * ones(shape=(nel,1),dtype=int), # extra node matnr.reshape((-1,1))], # mat number axis=1) if verbose: print "Calpy.elements" print elements # Boundary conditions # While we could get the boundary conditions from the node properties # database, we will formulate them directly from the numbers # of the supported nodes (botnodes). # Calpy (currently) only accepts boundary conditions in global # (cartesian) coordinates. However, as we only use fully fixed # (though hinged) support nodes, that presents no problem here. # For each supported node, a list of 6 codes can (should)be given, # corresponding to the six Degrees Of Freedom (DOFs): ux,uy,uz,rx,ry,rz. # The code has value 1 if the DOF is fixed (=0.0) and 0 if it is free. # The easiest way to set the correct boundary conditions array for Calpy # is to put these codes in a text field and have them read with # ReadBoundary. s = "" for n in FE.botnodes + 1: # again, the +1 is to comply with Calpy numbering! s += " %d 1 1 1 1 1 1\n" % n # a fixed hinge # Also clamp the fake extra node s += " %d 1 1 1 1 1 1\n" % nnod if verbose: print "Specified boundary conditions" print s bcon = fe_util.ReadBoundary(nnod,6,s) fe_util.NumberEquations(bcon) if verbose: print "Calpy.DOF numbering" print bcon # all DOFs are numbered from 1 to ndof # The number of free DOFs remaining ndof = bcon.max() print "Number of DOF's: %s" % ndof # Create load vectors # Calpy allows for multiple load cases in a single analysis. # However, our script currently puts all loads together in a single # load case. So the processing hereafter is rather simple, especially # since Calpy provides a function to assemble a single concentrated # load into the load vector. We initialize the load vector to zeros # and then add all the concentrated loads from the properties database. # A single concentrated load consists of 6 components, corresponding # to the 6 DOFs of a node. # # AssembleVector takes 3 arguments: the global vector in which to # assemble a nodal vector (length ndof), the nodal vector values # (length 6), and a list of indices specifying the positions of the # nodal DOFs in the global vector. # Beware: The function does not change the global vector, but merely # returns the value after assembling. # Also notice that the indexing inside the bcon array uses numpy # convention (starting at 0), thus no adding 1 is needed! print "Assembling Concentrated Loads" nlc = 1 loads = zeros((ndof,nlc),float) for p in FE.prop.getProp('n',attr=['cload']): cload = zeros(6) for i,v in p.cload: cload[i] += v print cload print cload.shape loads[:,0] = fe_util.AssembleVector(loads[:,0],cload,bcon[p.set,:]) if verbose: print "Calpy.Loads" print loads # Perform analysis # OK, that is really everything there is to it. Now just run the # analysis, and hope for the best ;) # Enabling the Echo will print out the data. # The result consists of nodal displacements and stress resultants. print "Starting the Calpy analysis module --- this might take some time" pf.app.processEvents() starttime = time.clock() displ,frc = beam3d.static(coords,bcon,mats,elements,loads,Echo=True) print "Calpy analysis has finished --- Runtime was %s seconds." % (time.clock()-starttime) # Export the results, but throw way these for the extra (last) node export({'calpy_results':(displ[:-1],frc)}) def postCalpy(): """Show results from the Calpy analysis.""" from plugins.postproc import niceNumber,frameScale from plugins.postproc_menu import showResults try: FE = named('fe_model') displ,frc = named('calpy_results') except: warning("I could not find the finite element model and/or the calpy results. Maybe you should try to first create them?") raise return # The frc array returns element forces and has shape # (nelems,nforcevalues,nloadcases) # nforcevalues = 8 (Nx,Vy,Vz,Mx,My1,Mz1,My2,Mz2) # Describe the nforcevalues element results in frc. # For each result we give a short and a long description: frc_contents = [('Nx','Normal force'), ('Vy','Shear force in local y-direction'), ('Vz','Shear force in local z-direction'), ('Mx','Torsional moment'), ('My','Bending moment around local y-axis'), ('Mz','Bending moment around local z-axis'), ('None','No results'), ] # split in two lists frc_keys = [ c[0] for c in frc_contents ] frc_desc = [ c[1] for c in frc_contents ] # Ask the user which results he wants res = askItems([('Type of result',None,'select',frc_desc), ('Load case',0), ('Autocalculate deformation scale',True), ('Deformation scale',100.), ('Show undeformed configuration',False), ('Animate results',False), ('Amplitude shape','linear','select',['linear','sine']), ('Animation cycle','updown','select',['up','updown','revert']), ('Number of cycles',5), ('Number of frames',10), ('Animation sleeptime',0.1), ]) if res: frcindex = frc_desc.index(res['Type of result']) loadcase = res['Load case'] autoscale = res['Autocalculate deformation scale'] dscale = res['Deformation scale'] showref = res['Show undeformed configuration'] animate = res['Animate results'] shape = res['Amplitude shape'] cycle = res['Animation cycle'] count = res['Number of cycles'] nframes = res['Number of frames'] sleeptime = res['Animation sleeptime'] dis = displ[:,0:3,loadcase] if autoscale: siz0 = Coords(FE.nodes).sizes() siz1 = Coords(dis).sizes() print siz0 print siz1 dscale = niceNumber(1./(siz1/siz0).max()) if animate: dscale = dscale * frameScale(nframes,cycle=cycle,shape=shape) # Get the scalar element result values from the frc array. val = val1 = txt = None if frcindex <= 5: val = frc[:,frcindex,loadcase] txt = frc_desc[frcindex] if frcindex > 3: # bending moment values at second node val1 = frc[:,frcindex+2,loadcase] showResults(FE.nodes,FE.elems,dis,txt,val,showref,dscale,count,sleeptime) ############################################################################# ######### Create a menu with interactive tasks ############# def create_menu(): """Create the Hesperia menu.""" MenuData = [ ("&How To Use",howto), ("---",None), ("&Create Geometry",createGeometry), ("&Assign Properties",assignProperties), ("&Export Properties",exportProperties), ("&Select Properties",selectProperties), ("&Save Properties",saveProperties), ("&Read Properties",readProperties), ("---",None), ("&Create Frame Model",createFrameModel), ("&Create Shell Model",createShellModel), ("---",None), ("&Write Abaqus input file",createAbaqusInput), ("&Run Calpy Analysis",runCalpyAnalysis), ("&Show Calpy Results",postCalpy), ("---",None), ("&Close Menu",close_menu), ] return menu.Menu('Hesperia',items=MenuData,parent=pf.GUI.menu,before='help') def show_menu(): """Show the menu.""" if not pf.GUI.menu.item('Hesperia'): create_menu() def close_menu(): """Close the menu.""" m = pf.GUI.menu.item('Hesperia') if m : m.remove() def reload_menu(): """Reload the menu.""" close_menu() show_menu() #################################################################### ######### What to do when the script is executed ################### if __name__ == "draw": # The sole intent of running this script is to create a top level # menu 'Hesperia'. The typical action then might be 'show_menu()'. # However, during development, you might want to change the menu's # actions will pyFormex is running, so a 'reload' action seems # more appropriate. reload_menu() # End pyformex-0.8.6/pyformex/examples/Position.py0000644000211500021150000000425611705104656021113 0ustar benebene00000000000000# $Id: Position.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Position level = 'beginner' topics = ['geometry'] techniques = ['position'] Position an object A thus that its three points X are aligned with the three points X of object B. """ def drawObjectWithName(obj,name): """Draw an object and show its name at the center""" drawText3D(obj.center(),name) draw(obj) def drawPointsNumbered(pts,color,prefix): """Draw a set of points with their number""" draw(pts,color=color,ontop=True,nolight=True) drawNumbers(Coords(pts),leader=prefix) clear() smoothwire() # The object to reposition A = Formex('4:0123',1).replic2(6,3) # The object to define the position B = Formex('3:016',2).replic2(4,4,taper=-1).trl(0,7.) drawObjectWithName(A,'Object A') drawObjectWithName(B,'Object B') #define matching points X = A[0,[0,3,1]] drawPointsNumbered(X,red,'X') Y = B[3,[1,2,0]] Y[2] = Y[0].trl([0.,1.,1.]) drawPointsNumbered(Y,green,'Y') zoomAll() pause() # Reposition A so that X are aligned with Y C = A.position(X,Y) draw(C,color=blue) zoomAll() # End pyformex-0.8.6/pyformex/examples/ScallopDome.py0000644000211500021150000000772711705104656021517 0ustar benebene00000000000000# $Id: ScallopDome.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Scallop Dome level = 'normal' topics = ['geometry','domes'] techniques = ['dialog', 'color'] """ #pf.canvas.settings['colormap'][2] = [1.,0.3,0.] # This example is fully annotated with comments in the statusbar # First we define a function to display a Formex and then wait for the user # to click the Step button def show(F,view='front',clearscr=True): if clearscr: clear() draw(F,view=view) pf.canvas.update() # Here we go message("Create a triangular pattern in the first octant") f1 = Formex([[[0,0],[1,0]],[[1,0],[1,1]]]).replic2(8,8,1,1,0,1,1,-1) + Formex([[[1,0],[2,1]]]).replic2(7,7,1,1,0,1,1,-1) show(f1) # message("Remove some of the bars") f1 = f1.remove(Formex([[[2,0],[3,1]]]).replic(3,2,0)) show(f1) # message("Transform the octant into a circular sector") f2 = f1.circulize1() f1.setProp(1) f2.setProp(2) show(f1+f2) clear() #draw(f2) # message("Make circular copies to obtain a full circle") show(f1+f2.rosette(6,60.)) # Create and display a scallop dome using the following parameters: # n = number of repetitions of the base module in circumference (this does not # have to be equal to 6: the base module will be compressed/expanded to # generate a full circle # f = if 1, the dome will have sharp edges where repeated modules meet; # if 2, the dome surface will be smooth over neighbouring modules. # c = height of the dome at the center of the dome. # r = height of the arcs at the circumference of the dome. def scallop(n,f,c,r): func = lambda x,y,z: [x,y,c*(1.-x*x/64.)+r*x*x/64.*4*power((1.-y)*y,f)] a=360./n f3 = f2.toCylindrical([0,1,2]).scale([1.,1./60.,1.]) f4 = f3.map(func).cylindrical([0,1,2],[1.,a,1.]).rosette(n,a) message("Scallop Dome with n=%d, f=%d, c=%f, r=%f" % (n,f,c,r)) return f4 message("Create a dome from the circular layout") f2.setProp(3) pf.canvas.camera.setAngles([0.,-45.]) show(scallop(6,1,2,0),None,False) howmany = ask("How many / Which domes do you want?", ['One','Sequence','Custom','None']) n,f,c,r = [6,1,2.,0.] if howmany == 'One': # The example from the pyformex homepage show(scallop(6,1,4,4),None) elif howmany == 'Sequence': # Present some nice examples for n,f,c,r in [ [6,1,2,0], [6,1,2,2], [6,1,2,5], [6,1,2,-2], [6,1,-4,4], [6,1,0,4], [6,1,4,4], [6,2,2,-4], [6,2,2,4], [6,2,2,8], [12,1,2,-2], [12,1,2,2] ]: show(scallop(n,f,c,r),None) elif howmany == 'Custom': # Customized version while True: res = askItems([['n',n],['f',f],['c',c],['r',r]]) n = int(res['n']) f = int(res['f']) c = float(res['c']) r = float(res['r']) show(scallop(n,f,c,r),None) if not ack("Want to try another one?"): exit() pyformex-0.8.6/pyformex/examples/SplineSurface.py0000644000211500021150000002563011705104656022051 0ustar benebene00000000000000# $Id: SplineSurface.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """SplineSurface level = 'advanced' topics = ['geometry','surface'] techniques = ['spline'] .. Description SplineSurface ------------- This example illustrates some advanced geometrical modeling tools using spline curves and surfaces. The script first creates a set of closed BezierSpline curves. Currently two sets of curves are predefined: - a set of transformations of a unit circle. The circle is scaled non-uniformously, resulting in an ellips, which is then rotated and translated. - a set of curves obtained by cutting a triangulated surface model with a series of parallel planes. The original surface model was obtained from medical imaging processes and represents a human artery with a kink. These curves are read from a geometry file 'splines.pgf' include in the pyFormex distribution. In the first case, the number of curves will be equal to the specified number. In the latter case, the number can not be larger than the number of curves in the file. The set of splines are then used to create a QuadSurface (a surface consisting of quadrilaterals). The number of elements along the splines can be chosen. The number of elements across the splines is currently unused. """ """Definition of surfaces in pyFormex. This module defines classes and functions specialized for handling two-dimensional geometry in pyFormex. """ # I wrote this software in my free time, for my joy, not as a commissioned task. # Any copyright claims made by my employer should therefore be considered void. import numpy as np from geometry import Geometry from plugins.curve import * ############################################################################## def rollCurvePoints(curve,n=1): """Roll the points of a closed curve. Rolls the points of a curve forward over n positions. Thus point 0 becomes point 1, etc. The function does not return a value. The curve is changed inplace. This only works for PolyLine and BezierSpline (and derived) classes. """ if (isinstance(curve,PolyLine) or isinstance(curve,BezierSpline)) and curve.closed: if isinstance(curve,PolyLine): mult = 1 else: mult = curve.degree curve.coords[:-1] = roll(curve.coords[:-1],-mult*n,axis=0) curve.coords[-1] = curve.coords[0] else: raise ValueError,"Expected a closed PolyLine or BezierSpline." def alignCurvePoints(curve,axis=1,max=True): """Roll the points of a closed curved according to some rule. The points of a closed curve are rotated thus that the starting (and ending) point is the point with the maximum or minimum value of the specified coordinate. The function returns nothing: the points are rolled inplace. """ if not curve.closed: raise ValueError,"Expected a closed curve." if max: ind = curve.pointsOn()[:,axis].argmax() else: ind = curve.pointsOn()[:,axis].argmin() rollCurvePoints(curve,ind) class SplineSurface(Geometry): """A surface created by a sequence of splines. The surface consists of a list of curves. The parametric value of the curves is called 'u', while 'v' is used for the parametric value across the splines. Two sets of parametric curves can be drawn: in u and in v direction. """ def __init__(self,curves=None,nu=0,coords=None): self.curves = curves self.grid = None self.ccurves = None closed = [ c.closed for c in self.curves ] self.uclosed = sum(closed) > 0 errors = [ c.closed != self.uclosed for c in self.curves ] if sum(errors) > 0: raise ValueError,"Either ALL or NONE of the curves should be closed." if nu <= 0: nu = len(curves)-1 self.grid = self.createGrid(nu) def bbox(self): return bbox(self.curves) def createGrid(self,nu,nv=None): print "Creating grid %s x %s" % (nu,nv) if nv is None: nv = self.curves[0].nparts CA = [ C.approx(ntot=nu) for C in self.curves ] print "Curves have %s points" % CA[0].coords.shape[0] print "There are %s curves" % len(CA) if not self.uclosed: nu += 1 grid = Coords(stack([CAi.coords[:nu] for CAi in CA])) print "Created grid %s x %s" % grid.shape[:2] return grid def vCurves(self): return [ BezierSpline(self.grid[:,i,:],curl=0.375) for i in range(self.grid.shape[1]) ] def uCurves(self): return [ BezierSpline(self.grid[i,:,:],curl=0.375) for i in range(self.grid.shape[0]) ] def approx(self,nu,nv): CL = self.vCurves() draw(CL,color=red) def actor(self,**kargs): return [ draw(c,**kargs) for c in self.curves ] def gridToMesh(grid,closed=False): """Convert a Grid Surface to a Quad Mesh""" nu = grid.shape[1] nv = grid.shape[0] -1 elems = array([[ 0,1,nu+1,nu ]]) if closed: elems = concatenate([(elems+i) for i in range(nu-1)],axis=0) elems = concatenate([elems,[[ nu-1,0,nu,2*nu-1]]],axis=0) else: #drawNumbers(self.grid.reshape(-1,3)) #print elems elems = concatenate([(elems+i) for i in range(nu-1)],axis=0) #print elems #print nu elems = concatenate([(elems+i*nu) for i in range(nv)],axis=0) x = grid.reshape(-1,3) #print nu,nv #print x.shape #print elems.shape #print elems.min(),elems.max() M = Mesh(grid.reshape(-1,3),elems) #print M.elems #drawNumbers(M.coords) return M def createCircles(n): """Create a set of BezierSpline curves. The curves are transformations of a unit circle. They are non-uniformously scaled to yield ellipses, and then rotated and translated. """ C = circle() t = arange(n+1) /float(n) CL = [ C.scale([1.,a,0.]) for a in 0.5 + arange(n+1) /float(n) ] CL = [ Ci.rot(a,2) for Ci,a in zip(CL,arange(n+1)/float(n)*45.) ] CL = [ Ci.trl(2,a) for Ci,a in zip(CL,arange(n+1)/float(n)*4.) ] return CL def createPowerCurves(nu,nv): """Create a set of BezierSpline power curves. The curves are transformations of a straight line. The line is transformed by two subsequent power law transformations: y = a*x**b and z = c*y**d. """ X = Formex(origin()).replic(nu+1,1.).coords.reshape(-1,3) C = BezierSpline(X) sx = C.dsize() sy = 0.5*sx sz = 0.25*sx powers = 1. * (arange(nv+1) * 2 - nv) / float(nv) print powers powers = exp(powers) print powers CL = [ C.map1(1,lambda x:sy*(x/sx)**e,0).map1(2,lambda x:sz*(x/sx)**e,1) for e in powers ] return CL def readSplines(): """Read spline curves from a geometry file. The geometry file splines.pgf is provided with the pyFormex distribution. """ from geomfile import GeometryFile fn = getcfg('datadir')+'/splines.pgf' f = GeometryFile(fn) obj = f.read() T = obj.values() print len(T) print [len(Si.coords) for Si in T] return T def removeInvalid(CL): """Remove the curves that contain NaN values. NaN values are invalid numerical values. This function removes the curves containing such values from a list of curves. """ nc = len(CL) CL = [ Ci for Ci in CL if not isnan(Ci.coords).any() ] nd = len(CL) if nc > nd: print "Removed %s invalid curves, leaving %s" % (nc-nd,nd) return CL def area(C,nroll=0): """Compute area inside spline The curve is supposed to be in the (x,y) plane. The nroll parameter may be specified to roll the coordinates appropriately. """ print nroll from plugins.section2d import PlaneSection F = C.toFormex().rollAxes(nroll) S = PlaneSection(F) C = S.sectionChar() return C['A'] ############################################################### clear() smoothwire() from gui.widgets import simpleInputItem as I res = askItems([ I('base',itemtype='vradio',choices=[ 'Circles and Ellipses', 'Power Curves', 'Kinked Artery', ]), I('ncurves',value=12,text='Number of spline curves'), I('nu',value=36,text='Number of cells along splines'), I('refine',False), I('nv',value=12,text='Number of cells across splines'), I('align',False), I('aligndir',1), I('alignmax',True), ]) if not res: exit() globals().update(res) if base == 'Circles and Ellipses': CL = createCircles(n=ncurves) nroll = 0 reverse = False elif base == 'Power Curves': CL = createPowerCurves(nu,nv) nroll = 0 reverse = False else: CL = readSplines() nroll = -1 reverse = True ncurves = len(CL) print "Created %s BezierSpline curves" % ncurves CL = removeInvalid(CL) if reverse: areas = [ area(Ci,nroll) for Ci in CL ] print areas for i,a in enumerate(areas): if a < 0.0: print "Reversing curve %s" % i CL[i] = CL[i].reverse() if align: #view('left') for Ci in CL: #clear() alignCurvePoints(Ci,aligndir,alignmax) draw(Ci.pointsOn()[0],color=green) #zoomAll() #pause() #exit() draw(CL) export({'splines':CL}) print "Number of points in the curves:",[ Ci.coords.shape[0] for Ci in CL] PL = [Ci.approx(1) for Ci in CL] createPL = False if createPL: export({'polylines':PL}) draw(PL,color=red) print "Number of points in the PolyLines:",[ Ci.coords.shape[0] for Ci in PL] S = SplineSurface(CL,nu) M = gridToMesh(S.grid,closed = S.uclosed) draw(M,color=yellow,bkcolor='steelblue') export({'quadsurface':M}) if refine: clear() print "Refining to %s" % nv S = SplineSurface(S.vCurves(),nv) N = gridToMesh(S.grid,closed = S.uclosed) draw(N,color=magenta,bkcolor='olive') export({'quadsurface-1':N}) zoomAll() ############################################################################## # End pyformex-0.8.6/pyformex/examples/Cones.py0000644000211500021150000001116511705104656020353 0ustar benebene00000000000000# $Id: Cones.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Cones level = 'normal' topics = ['geometry'] techniques = ['connect','dialog'] """ import simple from gui import widgets def cone(r0,r1,h,t=360.,nr=1,nt=24,diag=None): """Constructs a Formex which is (a sector of) a circle / (truncated) cone / cylinder. r0,r1,h are the lower and upper radius and the height of the truncated cone. All can be positive, negative or zero. Special cases: r0 = r1 : cylinder h = 0 : (flat) circle r0 = 0 or r1 = 0 : untruncated cone Only a sector of the structure, with opening angle t, is modeled. The default results in a full circumference. The cone is modeled by nr elements in height direction and nt elements in circumferential direction. By default, the result is a 4-plex Formex whose elements are quadrilaterals (some of which may collapse into triangles). If diag='up' or diag = 'down', all quads are divided by an up directed diagonal and a plex-3 Formex results. """ B = simple.rectangle(nt,nr,1.,1.,diag=diag) # grid with size 1x1 B = B.map(lambda x,y,z:[x,y,r0-y*(r0-r1)]) # translate and tilt it B = B.scale([t,h,1.]) # scale to fit parameters return B.cylindrical(dir=[2,0,1]) # roll it into a cone def cone1(r0,r1,h,t=360.,nr=1,nt=24,diag=None): """Constructs a Formex which is (a sector of) a circle / (truncated) cone / cylinder. r0,r1,h are the lower and upper radius and the height of the truncated cone. All can be positive, negative or zero. Special cases: r0 = r1 : cylinder h = 0 : (flat) circle r0 = 0 or r1 = 0 : untruncated cone Only a sector of the structure, with opening angle t, is modeled. The default results in a full circumference. The cone is modeled by nr elements in height direction and nt elements in circumferential direction. By default, the result is a 4-plex Formex whose elements are quadrilaterals (some of which may collapse into triangles). If diag='up' or diag = 'down', all quads are divided by an up directed diagonal and a plex-3 Formex results. """ r0,r1,h,t = map(float,(r0,r1,h,t)) p = Formex(simple.regularGrid([r0,0.,0.],[r1,h,0.],[0,nr,0]).reshape(-1,3)) #draw(p,color=red) a = (r1-r0)/h if a != 0.: p = p.shear(0,1,a) #draw(p) q = p.rotate(t/nt,axis=1) #draw(q,color=green) if diag == 'u': F = connect([p,p,q],bias=[0,1,1]) + \ connect([p,q,q],bias=[1,2,1]) elif diag == 'd': F = connect([q,p,q],bias=[0,1,1]) + \ connect([p,p,q],bias=[1,2,1]) else: F = connect([p,p,q,q],bias=[0,1,1,0]) F = Formex.concatenate([F.rotate(i*t/nt,1) for i in range(nt)]) return F from simple import rectangle r0=3. # bottom radius r1=1. # top radius h=5. # height t=360. # degrees (180. = half) nr=2 # number of elements along height nt=12 # number of elements along circumference diag='' items = [ widgets.simpleInputItem(n,globals()[n]) for n in ['r0','r1','h','t', 'nr','nt'] ] + [ widgets.simpleInputItem('diag',diag,itemtype='radio',choices=['','u','d']) ] dialog = widgets.InputDialog(items) while not dialog.result() == widgets.TIMEOUT: res = dialog.getResult() if not res: break globals().update(res) F = cone(r0,r1,h,t,nr,nt,diag) G = cone1(r0,r1,h,t,nr,nt,diag).swapAxes(1,2).trl(0,2*max(r0,r1)) G.setProp(1) H = F+G clear() draw(H) exit() #exit() # End pyformex-0.8.6/pyformex/examples/ColorImage.py0000644000211500021150000000742611705104656021332 0ustar benebene00000000000000# $Id: ColorImage.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """ColorImage level = 'normal' topics = ['image'] techniques = ['color'] """ from gui.widgets import ImageView,simpleInputItem as I from gui.imagecolor import * def selectImage(fn): fn = askImageFile(fn) if fn: viewer.showImage(fn) loadImage(fn) return fn def loadImage(fn): global image, scaled_image image = QImage(fn) if image.isNull(): warning("Could not load image '%s'" % fn) return None w,h = image.width(),image.height() print "size = %sx%s" % (w,h) diag = currentDialog() if diag: diag.updateData({'nx':w,'ny':h}) maxsiz = 40000. if w*h > maxsiz: scale = sqrt(maxsiz/w/h) w = int(w*scale) h = int(h*scale) return w,h if __name__ == 'draw': # allows loading this file as a module flat() lights(False) transparent(False) view('front') # default image file filename = getcfg('datadir')+'/butterfly.png' image = None scaled_image = None w,h = 200,200 # image viewer widget viewer = ImageView(filename) transforms = { 'flat': lambda F: F, 'cylindrical': lambda F: F.cylindrical([2,0,1],[2.,90./float(nx),1.]).rollAxes(-1), 'spherical': lambda F: F.spherical(scale=[1.,90./float(nx),2.]).rollAxes(-1), 'projected_on_cylinder': lambda F: F.projectOnCylinder(2*R,1), } res = askItems([ I('filename',filename,text='Image file',itemtype='button',func=selectImage), I('viewer',viewer,itemtype='widget'), # the image previewing widget I('nx',w,text='width'), I('ny',h,text='height'), I('transform',itemtype='vradio',choices=transforms.keys()), ]) if not res: exit() globals().update(res) if image is None: print "Loading image" loadImage(filename) if image is None: exit() # Create the colors color,colortable = image2glcolor(image.scaled(nx,ny)) print "Converting image to color array" # Create a 2D grid of nx*ny elements print "Creating grid" R = float(nx)/pi L = float(ny) F = Formex('4:0123').replic2(nx,ny).centered() F = F.translate(2,R) # Transform grid and draw def drawTransform(transform): print "Transforming grid" trf = transforms[transform] G = trf(F) clear() print "Drawing Colored grid" draw(G,color=color,colormap=colortable) drawText('Created with pyFormex',20,20,size=24) layout(2) viewport(0) drawTransform('cylindrical') zoomAll() viewport(1) drawTransform('spherical') zoomAll() # End pyformex-0.8.6/pyformex/examples/TrussFrame.py0000644000211500021150000000435211705104656021377 0ustar benebene00000000000000# $Id: TrussFrame.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """TrussFrame level = 'normal' topics = ['geometry'] techniques = ['color'] """ clear() yf = [ 0.0, 0.2, 1.2, 2.2, 3.2, 4.2, 4.5 ] # y of nodes in frame columns a = Formex([[[0.0,y]] for y in yf ]) b = connect([a,a],bias=[0,1]).translate([0.5,0.0,0.0]) b.setProp(3) c = b.reflect(0) d = connect([b,c],bias=[1,1]) d.setProp(2) e = connect([b,c],bias=[1,2]).select([0,2]) + connect([b,c],bias=[2,1]).select([1,3]) e.setProp(1) col = b+c+d+e frame = col.translate([-4.0,0.0,0.0]) + col.translate([+4.0,0.0,0.0]) # Dakligger h0 = 1.2 # hoogte in het midden h1 = 0.5 # hoogte aan het einde xd = [ 0, 0.6 ] + [ 0.6+i*1.2 for i in range(5)] # hor. positie knopen ko = Formex([[[x,0.0]] for x in xd]) ond = connect([ko,ko],bias=[0,1]) bov = ond.translate(1,h0).shear(1,0,(h1-h0)/xd[-1]) tss = connect([ond,bov],bias=[1,1]) ond.setProp(2) bov.setProp(4) tss.setProp(5) dakligger = (ond+bov+tss) dakligger += dakligger.reflect(0) frame += dakligger.translate([0,yf[-1],0]) draw(frame) structure = frame.replic2(2,6,12.,3.,0,2) clear() draw(structure) view('top') view('right') view('iso') pyformex-0.8.6/pyformex/examples/MeshMatch.py0000644000211500021150000000354411705104656021157 0ustar benebene00000000000000# $Id: MeshMatch.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """MeshMatch level = 'normal' topics = ['mesh'] techniques = ['draw','replicate','match'] """ transparent() smoothwire() clear() from mesh import Mesh n=5 nx=4*n ny=2*n M = Formex('4:0123').replic2(nx,ny).cselect(arange(4*nx,int(7.5*nx))).toMesh().setProp(1) draw(M) drawNumbers(M.coords,color=red) M1 = Formex('3:012').replic2(int(0.6*nx),int(0.45*ny),bias=1,taper=-2).toMesh().scale(2).trl(1,1.).setProp(2) draw(M1) zoomAll() drawNumbers(M1.coords,color=yellow,trl=[0.,-0.25,0.]) match = M.matchCoords(M1) m = match>=0 n1=arange(len(match)) print "List of the %s matching nodes" % m.sum() print column_stack([match[m],n1[m]]) draw(M.coords[match[m]],marksize=10,bbox='last') # End pyformex-0.8.6/pyformex/examples/Formex.py0000644000211500021150000000435011705104656020542 0ustar benebene00000000000000# $Id: Formex.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Formex Structure level = 'beginner' topics = ['illustration'] techniques = ['draw'] This script creates an image of how coordinates are structures in a Formex. It was intended mainly for the manual. """ clear() reset() def tmbbox(a): return [[0.0,0.0,0.0],[1.0,1.0,1.0]] marks.TextMark.bbox = tmbbox def drawAxis(len,dir,text): """Draw an axis of given length and direction annotated with text.""" F = Formex('l:1').scale(len).rotate(dir) #T = F[0][1].scale(1.1) draw(F,linewidth=2.0) drawText3D(F[0][1]+(2.,-0.5,0.),text,size=18) return F def drawFrame(P): """Draw a dashed frame at position P.""" d,e = (2,3) # dash length and step h = Formex('l:1').scale(d) v = h.rotate(-90).replic(4,-e,1) h = h.replic(6,e,0) frame = (h + v).trl(P) draw(frame,linewidth=1.0,bbox=None) drawAxis(30,0,'axis 2: coordinates (x,y,z): length = 3') drawAxis(30,-90,'axis 1: points: length = self.nplex()') F = drawAxis(50,30,'axis 0: elements: length = self.nelems()').divide(8) for i in range(1,5,2): drawFrame(F[i][1]) zoomAll() pyformex-0.8.6/pyformex/examples/Cylinder.py0000644000211500021150000000330511705104656021052 0ustar benebene00000000000000# $Id: Cylinder.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Cylinder level = 'beginner' topics = ['geometry', 'surface', 'cylinder'] techniques = ['import'] .. Description Cylinder -------- This example illustrates the use of simple.sector() and simple.cylinder() to create a parametric cylindrical surface. """ import simple from plugins.trisurface import TriSurface n=12 h=5. A = simple.sector(1.,360.,1,n,diag='u') B = simple.cylinder(2.,h,n,4,diag='u').reverse() C = A.reverse()+B+A.trl(2,h) S = TriSurface(C) export({'surface':S}) smoothwire() view('iso') draw(S,color=red) # End pyformex-0.8.6/pyformex/examples/OpticalIllusions.py0000644000211500021150000004224311705104656022602 0ustar benebene00000000000000# $Id: OpticalIllusions.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Optical Illusions level = 'normal' topics = ['illustration','geometry'] techniques = ['dialog', 'draw', 'persistence','random'] acknowledgements = ['Tomas Praet'] """ from simple import * from gui import widgets from gui.widgets import simpleInputItem as I from odict import ODict ################# Illusion definitions ##################### def ParallelLines(): """Parallel Lines This illustration consists only of equally sized squares. Though the thus formed horizontal lines seem to converge, they are strictly parallel. """ resetview([0.8,0.8,0.8]) lines = Formex([[[0,0,0],[14,0,0]]]).replic(13,1,dir=1) draw(lines,color=[0.8,0.8,0.8],linewidth=1.0) F = Formex('4:0123').replic(13,1) F.setProp([0,7]) F += F.translate([0.2,1,0]) + F.translate([0.4,2,0]) + F.translate([0.2,3,0]) F = F.replic(3,4,dir=1) draw(F) def RotatingCircle(): """Rotating Circle When staring at the cross in the middle, the disappearing magenta circles create the illusion of a green rotating circle. If you keep concentrating your gaze on the centre, the green circle wil seem to devour the magenta circle, up to a point where you no longer see the magenta circles. Blinking or changing your focus will immediately undo the effect. """ resetview([0.8,0.8,0.8]) ask = askItems([('Number of circles',12),('Radius of circles',1.2),('Radius of the figure',12),('Number of rotations',16),('color of circles',[1.0,0.40,1.0]),('Sleep time',0.03),('Zoom',14.)]) if not ask: return N = ask['Number of circles'] r = ask['Radius of circles'] R = ask['Radius of the figure'] n = ask['Number of rotations'] col = ask['color of circles'] sl = ask['Sleep time'] sc = ask['Zoom'] box=[[-sc,-sc,-sc],[sc,sc,sc]] draw(shape('plus'),bbox=box) C = circle(a1=20).scale([r,r,0]).points() O = [0,0,0] F = Formex([[C[i,0],C[i+1,0],O] for i in arange(0,36,2)]).translate([R,0,0]).rosette(N-1,360./N) for i in range(n*N): F = F.rotate(-360./N) dr = draw(F,color=col,bbox=box) if i>0: undraw(DR) DR=dr sleep(sl) def SquaresAndCircles(): """Squares And Circles When you look at one of the white circles, the other circles seem to change color. In fact, the color they appear to have is the same as the color of the squares. This color is generated randomly. Press 'show' multiple times to see the effect of the color of the squares. You may need to zoom to get an optimal effect. """ resetview([0.6,0.6,0.6]) B,H = 16,16 F = Formex('4:0123').replic2(B,H,1.2,1.2) R = 0.2/sqrt(2.) C = circle(a1=20).scale(R).points() O = [0,0,0] G = Formex([[C[i,0],C[i+1,0],O] for i in arange(0,36,2)]).translate([1.1,1.1,0]).replic2(B-1,H-1,1.2,1.2) G.setProp(7) draw(F,color=random.rand(3)/2) draw(G) def ShadesOfGrey(): """Shades Of Grey Our perception of brightness is relative. Therefore, the figure on the left looks a little darker than the one right. The effect can be somewhat subtle though. """ resetview([0.8,0.8,0.8]) sc = 2 box = [[-2,0,-2],[2,8,2]] back = Formex('4:0123').scale([8,8,1]) back += back.translate([-8,0,0]) back.setProp([0,7]) C = circle(a1=11.25).rotate(-90,2).points() F = Formex([[C[i,0],C[i+1,0],2*C[i+1,0],2*C[i,0]] for i in range(0,32,2)]).translate([2,4,0]) n = 40 for i in range(n): F = F.translate([-2./n,0,0]) G = F.reflect(0) dr1 = draw(F+G,color=[0.6,0.6,0.6],bbox=box) dr2 = draw(back,bbox=box) if i>0: undraw(DR2) undraw(DR1) else: sleep(2) DR1 = dr1 DR2 = dr2 def RunningInCircles(): """Running In Circles If you don't look directly at the rectangles, both rectangles will appear to 'overtake' each other constantly, although they are moving at equal and constant speed. """ resetview() box= [[-8,-8,-8],[8,8,8]] N = 72 R = 10 C = circle(a1=360./N).points() O =[0,0,0] F = Formex([[C[i,0],C[i+1,0],O] for i in arange(0,2*N,2)]).scale([R,R,0]) F.setProp([0,7]) p = circle(a1=360./N).vertices() centre = Formex([add(p[0:len(p):2],p[-1])]).translate([-1,0,0]) centre.setProp(1) draw(centre,bbox=box) draw(F,bbox=box) b1 = Formex('4:0123').scale([1.5,0.8,0]).translate([0,8.5,0.1]) b1.setProp(3) b2 = Formex('4:0123').scale([1.5,0.8,0]).translate([0,7,0.1]) b2.setProp(6) b = b1+b2 col = [random.rand(3)/3,[1,1,1]-random.rand(3)/8] for i in range(4*N): b = b.rotate(360./N/4) dr = draw(b,bbox=box,color=col) if i>0: undraw(DR) DR = dr def HowManyColors(): """How Many Colors How many colors are there in this image? It looks like there are 4 colors (pink, orange, light green and cyan), but in fact, there are only 3. The blueish and greenish colors are exactly the same. Lots of zooming might convince you that this is the case. """ resetview() magenta,orange,cyan = array([1.,0.,1.]),array([1.,0.6,0.]),array([0.,1.,0.6]) b,h,B,H = 10,0.5,11,99 F = Formex('4:0123').scale([b,h,1]).replic2(B,H,b,h) col = resize(magenta,(H,B,3)) for i in range(H): for j in range(B): if i%2==0: if j%4==1: col[i,j]=cyan else: if j%4==3: col[i,j]=cyan else: col[i,j]=orange draw(F,color=col.reshape(-1,3)) def AlignedLines(): """Aligned Lines This is a classic optical illusion. Straight lines can appear to be shifted when only a tilted part is visible. """ resetview() a = 60. lines = Formex('l:1').scale([20,1,0]).rotate(a).translate([-20.*cos(a*pi/180.),0,0]).replic(32,1) lines = lines.cutWithPlane([-1,0,0],[1,0,0],side='+').cutWithPlane([22,0,0],[1,0,0],side='-') mask = Formex('4:0123').scale([1,20.*sin(a*pi/180.),1]).replic(11,2) mask.setProp(6) savedelay = pf.GUI.drawwait draw(mask,color=random.rand(3)) delay(2) draw(lines,linewidth=2) for i in range(3): wait() renderMode('wireframe') wait() renderMode('flat') delay(savedelay) def ParallelLinesOverWheel(): """Parallel Lines Over Wheel Another commonly seen illusion. The lines in the back tend to give the illusion of curved lined, although they are completely straight. Some zooming can help to optimize the effect. """ resetview() C,O = circle(a1=20).scale(2).points(),[0,0,0] draw(Formex([[C[i,0],C[i+1,0],O] for i in arange(0,36,2)])) line = Formex([[[-20,0,0],[20,0,0]]]) lines = line hor = line.translate([0,-4,0]) + line.translate([0,4,0]) draw(hor,color=red,linewidth=4) for i in range(0,180,5): lines += line.rotate(i) draw(lines,linewidth=1) def MotionInducedBlindness(): """Motion Induced Blindness This is a very nice illusion. Look at the centre of the image. The moving background will give the illusion that the other static points disappear. Blinking or changing your focus will immediately undo the effect. Cool huh? """ resetview('black') res = askItems([('Number of static points',10),('Background',None,'radio',{'choices':['Tiles','Structured points','Random points']}),('Rotations',2),('Rotation angle',2),('Number of random points',300)]) if not res: return nr,a,rot,back,n = res['Number of random points'],res['Rotation angle'],res['Rotations'],res['Background'],res['Number of static points'] draw(shape('star').scale(0.4),color=red,linewidth=2) points = Formex([[0,-10,0]]).rosette(n,360./n) draw(points,color=random.rand(3),marksize=10) col=random.rand(3) if back=='Tiles': F = shape('plus').replic2(11,11,3,3).translate([-15,-15,0]) elif back=='Structured points': F = Formex([[0,0,0]]).replic2(30,30,1).translate([-15,-15,0]) else: F = Formex(random.rand((nr,3))).scale([30,30,0]).translate([-15,-15,0]) for i in range(rot*360/a): F = F.rotate(a) dr = draw(F,color=col,linewidth=2,bbox=[[-10,-10,0],[10,10,0]]) if i>0: undraw(DR) DR = dr def FlickerInducedBlindness(): """Flicker Induced Blindness """ #... STILL UNDER DEVELOPMENT... (pyFormex might lack the possibility to reach the correct frequencies) resetview('black') n,freq,d = 4,2.,0.17 sl = 1/2/freq centre = shape('plus').scale(0.4) centre.setProp(7) draw(centre) points = Formex([[0,-5,0]]).rosette(n,360./n) points.setProp(6) draw(points,color=[0.2,0.5,0.3],marksize=4) F1 = Formex('4:0123').scale([1,2,1]).translate([d,-6,0]) F2 = F1.translate([-1-2*d,0,0]) F1 = F1.rosette(n,360./n) F2 = F2.rosette(n,360./n) for i in range(200): dr1 = draw(F1,color=[0.4,0.,0.]) if i>0: undraw(dr2) sleep(sl) dr2 = draw(F2,color=[0.4,0.,0.]) undraw(dr1) sleep(sl) def SineWave(): """Sine Wave A simple yet powerful illusion: the vertical lines seem larger at places where the sine wave is more horizontal, and smaller where the sine wave is more vertical. Play with the parameters to get a more obvious result. Amplitude 0 shows that all lines are equally large. """ resetview() res = askItems([('Amplitude',3.5),('Periods',3),('Spacing between lines',0.1)]) if not res: return shift,per,amp = res['Spacing between lines'],res['Periods'],res['Amplitude'] n = int(2*pi/shift*per) F = Formex('2').replic(n,shift) for i in F: i[0,1] = amp*sin(i[0,0]) i[1,1] = i[0,1] + 1 draw(F) def CirclesAndLines(): """Circles And Lines Another classic. All lines are completely straight, though the circles in the background give you the illusion that they aren't. The colors are generated randomly; some combination might not work as well as others. """ resetview() n,m,nc = 5,5,8 size = nc*sqrt(2) lines = Formex('l:1234').translate([-0.5,-0.5,0]).rotate(45).scale(size) c = circle(a1=5) C = c for i in range(2,nc+1): C += c.scale(i) C = C.replic2(n,m,2*nc,2*nc) lines = lines.replic2(n,m,2*nc,2*nc) draw(C,linewidth=2,color=random.rand(3)) draw(lines,linewidth=3,color=random.rand(3)) def Crater(): """Crater Though this image is 2D, you might get a 3D illusion. Look carefully and you'll see the whirlabout of a crater shaped object. """ resetview() deg,rot,col = 5,3,random.rand(2,3) r1,r2,r3,r4,r5,r6,r7,r8 = 1,1.9,2.9,4,5.1,6.1,7,7.8 p = circle(a1=5).vertices() C = Formex([p[0:len(p):2]]) C1 = C.scale(r1).translate([0,r1,0]) C2 = C.scale(r2).translate([0,r2,0]) C3 = C.scale(r3).translate([0,r3,0]) C4 = C.scale(r4).translate([0,r4,0]) C5 = C.scale(r5).translate([0,2*r4-r5,0]) C6 = C.scale(r6).translate([0,2*r4-r6,0]) C7 = C.scale(r7).translate([0,2*r4-r7,0]) C8 = C.scale(r8).translate([0,2*r4-r8,0]) fig = C1+C2+C3+C4+C5+C6+C7+C8 for i in range(rot*360/deg): fig = fig.rotate(deg) dr = draw(fig,color=col) #dr = draw(fig,color=[[0.8,0.8,0.8],[0.2,0.2,0.2]]) if i>0: undraw(DR) DR = dr def Cussion(): """Cussion This is a powerful illusion, though again some color combinations might not work as well as others. The smaller squares on this 'chessboard' tend to give a distortion. Again, all horizontal and vertical lines are perfectly parallel and straight! """ resetview() b,h = 17,17 if b%2==0: b+=1 if h%2==0: h+=1 chess = Formex('4:0123').replic2(b,h,1,1).translate([-b/2+0.5,-h/2+0.5,0]) col=[random.rand(3),random.rand(3)] sq1 = Formex('4:0123').scale([0.25,0.25,1]).translate([-0.45,0.2,0]) sq2 = Formex('4:0123').scale([0.25,0.25,1]).translate([0.2,-0.45,0]) F = sq1.translate([1,0,0]).replic(int(b/2)-1,1)+sq2.translate([0,1,0]).replic(int(h/2)-1,1,dir=1) sq = sq1+sq2 for i in range(int(b/2)): for j in range(int(h/2)): if i+j < (int(b/2)+int(b/2))/2-1: F += sq.translate([i+1,j+1,0]) colors=ndarray([0,0]) for i in F: if (int(i[0,0])+int(i[0,1])-1)%2 == 0: colors=append(colors,col[1]) else: colors=append(colors,col[0]) colors= colors.reshape(-1,3) F = F.rosette(4,90) draw(F,color=colors.reshape(-1,3)) draw(chess,color=col) def CrazyCircles(): """Crazy Circles You've undoubtably seen some variation of this illusion before. Looking at different spots of the image will give the illusion of motion. The secret is all in the combination of colors. Zooming might increase the effect. Switching to wireframe removes the effect and proves that this is merely an illusion. """ resetview() n = 5*6 col = [[1.,1.,1.],[0.12,0.556,1.],[0.,0.,1.],[0.,0.,0.],[0.7,0.9,0.2],[1.,1.,0.]] p = circle(a1=10).rotate(5).vertices() f = Formex([p[0:len(p):2]]) F = f.copy() for i in range(1,n): F += f.scale([1+i*0.5,1+i*0.5,1]) F1 = F.replic2(5,5,n+1,n+1) F2 = F.replic2(4,4,n+1,n+1).translate([(n+1)/2,(n+1)/2,0]) draw(F1+F2,color=col) ############ Other actions ################# def resetview(bgcol='white'): clear() reset() layout(nvps=1) bgcolor(bgcol) renderMode('flat') toolbar.setProjection() frontView() ############# Create dialog ################# gdname = '_OpticalIllusions_data_' dialog = None explanation = None illusions = [ ParallelLines, RotatingCircle, SquaresAndCircles, ShadesOfGrey, RunningInCircles, HowManyColors, AlignedLines, ParallelLinesOverWheel, MotionInducedBlindness, ## FlickerInducedBlindness, SineWave, CirclesAndLines, Crater, Cussion, CrazyCircles, ] headers = [ getattr(f,'__doc__').split('\n')[0] for f in illusions ] method = ODict(zip(headers,illusions)) # Dialog Actions def close(): """Close the dialog""" global dialog,explanation if dialog: pf.PF[gdname] = dialog.results dialog.close() dialog = None if explanation: explanation.close() explanation = None # Release scriptlock scriptRelease(__file__) def explain(): """Show the explanation""" global explanation dialog.acceptData() globals().update(dialog.results) text = method[Illusion].__doc__ if Explain: # use a persistent text box if explanation: explanation.updateText(text) explanation.raise_() else: # create the persistent text box explanation = widgets.TextBox(text,actions=[('Close',None)]) explanation.show() else: # show a non-persistent text showText(method[Illusion].__doc__) def show(): """Show the illusion""" if dialog: dialog.acceptData() globals().update(dialog.results) if Explain: explain() dialog.hide() method[Illusion]() dialog.show() def next(): """Show the next illusion""" dialog.acceptData() ill = dialog.results['Illusion'] i = (method._order.index(ill) + 1) % len(method._order) dialog.updateData({'Illusion':method._order[i]}) show() def timeOut(): """What to do when the dialog receives a timeout signal""" show() wait() close() def openDialog(): """Create and display the dialog""" global dialog,explanation data_items = [ I('Illusion',Illusion,choices=method.keys()), I('Explain',Explain,text='Show explanation'), ] dialog = widgets.InputDialog( data_items, caption='Optical illusions', actions = [('Done',close), ('Next',next), ('Explain',explain), ('Show',show)], default='Show' ) dialog.timeout = timeOut dialog.show() if __name__ == "draw": Illusion = None Explain = False try: globals().update(pf.PF[gdname]) except: pass close() openDialog() # Block other scripts scriptLock(__file__) # End pyformex-0.8.6/pyformex/examples/World.py0000644000211500021150000000424411705104656020373 0ustar benebene00000000000000# $Id: World.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """World level = 'normal' topics = ['image'] techniques = ['color','filename'] """ from gui.imagecolor import * clear() smooth() lights(False) view('front') fn = os.path.join(getcfg('datadir'),'world.jpg') fn = askFilename(cur=fn,filter=utils.fileDescription('img'),) if not fn: exit() im = QtGui.QImage(fn) if im.isNull(): warning("Could not load image '%s'" % fn) exit() nx,ny = im.width(),im.height() #nx,ny = 200,200 # Create the colors color,colormap = image2glcolor(im.scaled(nx,ny)) part = ask("How shall I show the image?",["Plane","Half Sphere","Full Sphere"]) # Create a 2D grid of nx*ny elements F = Formex('4:0123').replic2(nx,ny).centered().translate(2,1.) #color = [ 'yellow' ]*(nx-2) + ['orange','red','orange'] if part == "Plane": G = F else: if part == "Half Sphere": sx = 180. else: sx = 360. G = F.spherical(scale=[sx/nx,180./ny,2.*max(nx,ny)]).rollAxes(-1) draw(G,color=color,colormap=colormap) drawText('Created with pyFormex',10,10) # End pyformex-0.8.6/pyformex/examples/Sphere_stl.py0000644000211500021150000000554711705104656021423 0ustar benebene00000000000000# $Id: Sphere_stl.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Sphere_stl level = 'normal' topics = ['geometry','surface'] techniques = ['connect','spherical','dialog', 'persistence', 'color'] """ clear() top = 0. bot = -90. r = 1. n = 8 m = 12 # initial divisions # Create points dy = float(top-bot) / n F = [ Formex(zeros((m+1,1,3))) ] for i in range(n): dx = 360./(m+i) f = Formex([[[j*dx,(i+1)*dy,0]] for j in range(m+i+1)]) F.append(f) draw(F) # Create Lines if ack("Create Line model?"): G = [[],[],[]] for i,f in enumerate(F[1:]): G[0].append(connect([f,f],bias=[0,1])) G[1].append(connect([F[i],f],bias=[0,0])) if i > 0: G[2].append(connect([F[i],f],bias=[0,1])) G = map(Formex.concatenate,G) for i,f in enumerate(G): f.setProp(i) G = Formex.concatenate(G) clear() draw(G) print G.bbox() L = G.translate([0,bot,r]).spherical() clear() draw(L) # Create Triangles if ack("Create Surface model?"): G = [[],[]] for i,f in enumerate(F[1:]): G[0].append(connect([F[i],f,f],bias=[0,1,0])) if i > 0: G[1].append(connect([F[i],F[i],f],bias=[0,1,1])) G = map(Formex.concatenate,G) for i,f in enumerate(G): f.setProp(i+1) G = Formex.concatenate(G) clear() draw(G) smoothwire() #pf.canvas.update() T = G.translate([0,bot,r]).spherical() clear() draw(T) T += T.reflect(dir=2) clear() draw(T) if ack('Export this model in STL format?',default='No'): fn = askNewFilename(getcfg('workdir'),"Stl files (*.stl)") if fn: from plugins import trisurface f = open(fn,'w') surface.write_stla(f,T.coords) f.close() pyformex-0.8.6/pyformex/examples/Interpolate.py0000644000211500021150000000373111705104656021572 0ustar benebene00000000000000# $Id: Interpolate.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Interpolate level = 'beginner' topics = ['geometry'] techniques = ['color'] """ def demo_interpolate(): clear() a = Formex([[[0,0,0],[1,0,0]],[[1,0,0],[2,0,0]]]) b = Formex([[[0,1,0],[1,1,0]],[[1,1,0],[2,1,0]]]) message("Two lines") draw(a+b) n = 10 v = 1./n * arange(n+1) p = arange(n) c = interpolate(a,b,v) c.setProp(p) message("Interpolate between the two") draw(c) drawNumbers(c) sleep(2) d = interpolate(a,b,v,swap=True) d.setProp(p) clear() message("Interpolate again with swapped order") draw(d) drawNumbers(d) exit() sleep(2) f = c.divide(v) f.setProp((1,2)) clear() message("Divide the set of lines") draw(f) if __name__ == "draw": wireframe() demo_interpolate() pyformex-0.8.6/pyformex/examples/Lustrum.py0000644000211500021150000000331411705104656020754 0ustar benebene00000000000000# $Id: Lustrum.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Lustrum level = 'normal' topics = ['curve','drawing','illustration'] techniques = ['color','persistence','lima','import'] """ reset() from pyformex.examples.Lima import * from project import Project linewidth(2) fgcolor(blue) grow('Plant1',ngen=7,clearing=False,text=False) data = readGeomFile(os.path.join(pf.cfg['datadir'],'blippo.pgf')) curve = data['blippo_000'] bb = curve.coords.bbox() ctr = bb.center() siz = bb.sizes() curve.coords = curve.coords.trl(0,-ctr[0]).scale(50./siz[0]) draw(curve,color=pyformex_pink,linewidth=5) # End pyformex-0.8.6/pyformex/examples/Slice.py0000644000211500021150000000502311705104656020337 0ustar benebene00000000000000# $Id: Slice.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Slice level = 'advanced' topics = ['surface'] techniques = ['color','widgets'] """ from plugins.trisurface import TriSurface def askSlices(bb): res = askItems([('Direction',0), ('# slices',25), ('total rot',70.), ],caption = 'Define the slicing planes') if res: axis = res['Direction'] nslices = res['# slices'] totalrot = res['total rot'] xmin,xmax = bb[:,axis] dx = (xmax-xmin) / nslices x = arange(nslices+1) * dx N = unitVector(axis) P = [ bb[0]+N*s for s in x ] return P,N,totalrot else: return None reset() smooth() lights(True) transparent(False) setView('horse',[20,20,0]) S = TriSurface.read(getcfg('datadir')+'/horse.off') bb = S.bbox() t = -0.3 bb[0] = (1.0-t)*bb[0] + t*bb[1] draw(S,bbox=bb,view='front') try: P,n,t = askSlices(S.bbox()) except: exit() a = t/len(P) F = S.toFormex() G = [] old = seterr(all='ignore') setDrawOptions({'bbox':None}) for i,p in enumerate(P): F1,F = F.cutWithPlane(p,-n) if F1.nelems() > 0: F1.setProp(i) G = [ g.rot(a,around=p) for g in G ] G.append(F1) clear() draw([F,G]) seterr(**old) x = pf.canvas.width()/2 y = pf.canvas.height() - 40 drawText("No animals got hurt during the making of this movie!",x,y,size=18,gravity='C') # End pyformex-0.8.6/pyformex/examples/HorseSlice.py0000644000211500021150000000345411705104656021346 0ustar benebene00000000000000# $Id: HorseSlice.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """HorseTorse level = 'advanced' topics = ['geometry','surface','mesh'] techniques = ['intersection'] """ from plugins.trisurface import TriSurface from mesh import Mesh reset() smooth() lights(True) S = TriSurface.read(getcfg('datadir')+'/horse.off') SA = draw(S) res = askItems([ ('direction',[1.,0.,0.]), ('number of sections',20), ('color','red'), ('ontop',False), ]) if not res: exit() d = res['direction'] n = res['number of sections'] c = res['color'] slices = S.slice(dir=d,nplanes=n) linewidth(2) draw(slices,color=c,view=None,bbox='last',nolight=True,ontop=res['ontop']) #undraw(SA) zoomAll() pyformex-0.8.6/pyformex/examples/Curves.py0000644000211500021150000001421611705104656020553 0ustar benebene00000000000000# $Id: Curves.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Curves Examples showing the use of the 'curve' plugin level = 'normal' topics = ['geometry','curve'] techniques = ['widgets','persistence','import','spline'] """ from plugins.curve import * from plugins.nurbs import * from odict import ODict from gui.widgets import InputDialog ctype_color = [ 'red','green','blue','cyan','magenta','yellow','white' ] point_color = [ 'black','white' ] open_or_closed = { True:'A closed', False:'An open' } TA = None curvetypes = [ 'PolyLine', 'Quadratic Bezier Spline', 'Cubic Bezier Spline', 'Natural Spline', 'Nurbs Curve', ] def drawCurve(ctype,dset,closed,endcond,curl,ndiv,ntot,extend,spread,drawtype,cutWP=False,scale=None,directions=False): global S,TA P = dataset[dset] text = "%s %s with %s points" % (open_or_closed[closed],ctype.lower(),len(P)) if TA is not None: undecorate(TA) TA = drawText(text,10,10,font='sans',size=20) draw(P, color='black',nolight=True) drawNumbers(Formex(P)) if ctype == 'PolyLine': S = PolyLine(P,closed=closed) elif ctype == 'Quadratic Bezier Spline': S = BezierSpline(P,degree=2,closed=closed,curl=curl,endzerocurv=(endcond,endcond)) elif ctype == 'Cubic Bezier Spline': S = BezierSpline(P,closed=closed,curl=curl,endzerocurv=(endcond,endcond)) elif ctype == 'Natural Spline': S = NaturalSpline(P,closed=closed,endzerocurv=(endcond,endcond)) directions = False elif ctype == 'Nurbs Curve': S = NurbsCurve(P,closed=closed)#,blended=closed) scale = None directions = False drawtype = 'Curve' if scale: S = S.scale(scale) im = curvetypes.index(ctype) print "%s control points" % S.coords.shape[0] #draw(S.coords,color=red,nolight=True) if drawtype == 'Curve': draw(S,color=ctype_color[im],nolight=True) else: if spread: #print ndiv,ntot PL = S.approx(ndiv=ndiv,ntot=ntot) else: #print ndiv,ntot PL = S.approx(ndiv=ndiv) if cutWP: PC = PL.cutWithPlane([0.,0.42,0.],[0.,1.,0.]) draw(PC[0],color=red) draw(PC[1],color=green) else: draw(PL, color=ctype_color[im]) draw(PL.pointsOn(),color=black) if directions: t = arange(2*S.nparts+1)*0.5 ipts = S.pointsAt(t) draw(ipts) idir = S.directionsAt(t) drawVectors(ipts,0.2*idir) dataset = [ Coords([[1., 0., 0.],[0., 1., 0.],[-1., 0., 0.], [0., -1., 0.]]), Coords([[6., 7., 12.],[9., 5., 6.],[11., -2., 6.], [9., -4., 14.]]), Coords([[-5., -10., -4.], [-3., -5., 2.],[-4., 0., -4.], [-4., 5, 4.], [6., 3., -1.], [6., -9., -1.]]), Coords([[-1., 7., -14.], [-4., 7., -8.],[-7., 5., -14.],[-8., 2., -14.], [-7., 0, -6.], [-5., -3., -11.], [-7., -4., -11.]]), Coords([[-1., 1., -4.], [1., 1., 2.],[2.6, 2., -4.], [2.9, 3.5, 4.], [2., 4., -1.],[1.,3., 1.], [0., 0., 0.], [0., -3., 0.], [2., -1.5, -2.], [1.5, -1.5, 2.], [0., -8., 0.], [-1., -8., -1.], [3., -3., 1.]]), Coords([[0., 1., 0.],[0., 0.1, 0.],[0.1, 0., 0.], [1., 0., 0.]]), Coords([[0., 1., 0.],[0.,0.,0.],[0.,0.,0.],[1., 0., 0.]]), #Coords([[0., 1., 0.],[1., 0., 0.]]), ] data_items = [ _I('DataSet','0',choices=map(str,range(len(dataset)))), _I('CurveType',choices=curvetypes), _I('Closed',False), _I('EndCurvatureZero',False), _I('Curl',1./3.), _I('Ndiv',10), _I('SpreadEvenly',False), _I('Ntot',40), _I('ExtendAtStart',0.0), _I('ExtendAtEnd',0.0), _I('Scale',[1.0,1.0,1.0]), _I('DrawAs',None,'hradio',choices=['Curve','Polyline']), _I('Clear',True), _I('ShowDirections',False), _I('CutWithPlane',False), ] clear() setDrawOptions({'bbox':'auto','view':'front'}) linewidth(2) flat() dialog = None import script def close(): global dialog if dialog: dialog.close() dialog = None # Release scriptlock scriptRelease(__file__) def show(all=False): dialog.acceptData() globals().update(dialog.results) export({'_Curves_data_':dialog.results}) if Clear: clear() if all: Types = curvetypes else: Types = [CurveType] setDrawOptions({'bbox':'auto'}) for Type in Types: drawCurve(Type,int(DataSet),Closed,EndCurvatureZero,Curl,Ndiv,Ntot,[ExtendAtStart,ExtendAtEnd],SpreadEvenly,DrawAs,CutWithPlane,Scale,ShowDirections) setDrawOptions({'bbox':None}) def showAll(): show(all=True) def timeOut(): showAll() wait() close() dialog = widgets.InputDialog( data_items, caption='Curve parameters', actions = [('Close',close),('Clear',clear),('Show All',showAll),('Show',show)], default='Show') if pf.PF.has_key('_Curves_data_'): #print pf.PF['_Curves_data_'] dialog.updateData(pf.PF['_Curves_data_']) dialog.timeout = timeOut dialog.show() # Block other scripts scriptLock(__file__) # End pyformex-0.8.6/pyformex/examples/Clip.py0000644000211500021150000000625011705104656020172 0ustar benebene00000000000000# $Id: Clip.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Clip level = 'beginner' topics = ['geometry'] techniques = ['color'] """ resetAll() setDrawOptions({'clear':True}) n = 16 # These are triangles F = Formex([[[0,0,0],[1,0,0],[0,1,0]],[[1,0,0],[1,1,0],[0,1,0]]],0).replic2(n,n,1,1) # Novation (Spots) m = 4 h = 0.15*n r = n/m s = n/r a = [ [r*i,r*j,h] for j in range(1,s) for i in range(1,s) ] for p in a: F = F.bump(2,p, lambda x:exp(-0.75*x),[0,1]) draw(F) # Define a plane plane_p = [3.2,3.0,0.0] plane_n = [2.0,1.0,0.0] #number of nodes above/below the plane dist = F.distanceFromPlane(plane_p,plane_n) above = sum(dist>0.0,-1) below = sum(dist<0.0,-1) # Define a line by a point and direction line_p = [0.0,0.0,0.0] line_n = [1.,1.,1./3] d = F.distanceFromLine(line_p,line_n) #number of nodes close to line close = sum(d < 2.2,-1) sel = [ F.test(nodes=0,dir=0,min=1.5,max=3.5), F.test(nodes=[0,1],dir=0,min=1.5,max=3.5), F.test(nodes=[0,1,2],dir=0,min=1.5,max=3.5), F.test(nodes='all',dir=1,min=1.5,max=3.5), F.test(nodes='any',dir=1,min=1.5,max=3.5), F.test(nodes='none',dir=1,min=1.5), (above > 0) * (below > 0 ), close == 3, ] txt = [ 'First node has x between 1.5 and 3.5', 'First two nodes have x between 1.5 and 3.5', 'First 3 nodes have x between 1.5 and 3.5', 'All nodes have y between 1.5 and 3.5', 'Any node has y between 1.5 and 3.5', 'No node has y larger than 1.5', 'Touching the plane P = [3.2,3.0,0.0], n = [2.0,1.0,0.0]', '3 nodes close to line through [0.0,0.0,0.0] and [1.0,1.0,1.0]', ] color = getcfg('canvas/colormap')[1:] # omit the black while len(color) < len(sel): color.extend(color) color[0:0] = ['black'] # restore the black prop = zeros(F.nelems()) i = 1 for s,t in zip(sel,txt): prop[s] = i F.setProp(prop) message('%s (%s): %s' % (color[i],sum(s),t)) draw(F) i += 1 message('Clip Formex to last selection') draw(F.clip(s),view=None) message('Clip complement') draw(F.cclip(s)) # End pyformex-0.8.6/pyformex/examples/Pattern.py0000644000211500021150000000340111705104656020713 0ustar benebene00000000000000# $Id: Pattern.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Pattern level = 'beginner' topics = ['geometry'] techniques = ['color','pattern'] """ import simple if __name__ == "draw": reset() setDrawOptions(dict(view='front',linewidth=5,fgcolor='red')) grid = actors.GridActor(nx=(4,4,0),ox=(-2.0,-2.0,0.0),dx=(1.0,1.0,1.0),planes=False,linewidth=1) drawActor(grid) linewidth(3) FA = None setDrawOptions({'bbox':None}) for n,p in simple.Pattern.items(): message("%s = %s" % (n,p)) FB = draw(Formex(p),bbox=None,color='red') if FA: undraw(FA) FA = FB pause() # End pyformex-0.8.6/pyformex/examples/Flare.py0000644000211500021150000000571111705104656020335 0ustar benebene00000000000000# $Id: Flare.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Torus level = 'beginner' topics = ['geometry'] techniques = ['dialog','transform','function'] """ def addFlares(F,dir=[0,2]): """Adds flares at both ends of the structure. The flare parameters are hardcoded, a real-life example would make them adjustable. Returns the flared structure. """ F = F.flare(m/4.,-1.,dir,0,0.5) F = F.flare(m/4.,1.5,dir,1,2.) return F # Some named colors (they should exist in /etc/X11/rgb.txt) color_choice = ['red','blue','orange','indianred','gold','pink','orchid','steelblue','turquoise','aquamarine','aquamarine1','aquamarine2','aquamarine3','aquamarine4','navy blue','royal blue'] # Ask data from the user data = [ {'name':'m','value':36,'text':'number of cells in longest grid direction'}, {'name':'n','value':12,'text':'number of cells in shortes grid direction'}, {'name':'f0','value':True,'text':'add flares on rectangle'}, {'name':'f1','value':False,'text':'add flares on cylinder'}, {'name':'f2','value':False,'text':'add flares on torus'}, {'name':'geom','value':'cylinder','itemtype':'radio','choices':['rectangle','cylinder','torus'],'text':'geometry'}, {'name':'color0','value':'red','choices':color_choice}, {'name':'color1','value':'blue','choices':color_choice}, ] res = askItems(data) if not res: exit() # Add the returned data to the global variables globals().update(res) F = Formex('3:012934',[0,1]).replic2(m,n,1,1) if f0: F = addFlares(F) if geom != 'rectangle': F = F.translate(2,1).cylindrical([2,1,0],[1.,360./n,1.]) if f1: F = addFlares(F,dir=[2,0]) if geom == 'torus': F = F.translate(0,5).cylindrical([0,2,1],[1.,360./m,1.]) if f2: F = addFlares(F) # Draw the structure clear() view('iso') draw(F,colormap=[color0,color1]) # End pyformex-0.8.6/pyformex/examples/WedgeHex.py0000644000211500021150000000344211705104656021003 0ustar benebene00000000000000# $Id: WedgeHex.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """WedgeHex level = 'normal' topics = ['mesh'] techniques = ['revolve','degenerate'] """ import simple clear() smoothwire() # create a 2D xy mesh nx,ny = 6,2 G = simple.rectangle(1,1,1.,1.).replic2(nx,ny) M = G.toMesh() draw(M, color='red') view('iso') # create a 3D axial-symmetric mesh by REVOLVING n,a = 8,45. R = M.revolve(n,angle=a,axis=1,around=[1.,0.,0.]) sleep(2) draw(R,color='yellow') # reduce the degenerate elements to WEDGE6 clear() print R ML = R.fuse().splitDegenerate() print "AFTER SPLITTING: %s MESHES" % len(ML) for m in ML: print m ML = [ Mi.setProp(i) for i,Mi in enumerate(ML) ] draw(ML) # End pyformex-0.8.6/pyformex/examples/Diamatic.py0000644000211500021150000000501711705104656021016 0ustar benebene00000000000000# $Id: Diamatic.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Diamatic dome level = 'beginner' topics = ['structure','domes'] techniques = ['color'] """ wireframe() u = 3. # modular length n = 6 # number of modules in one sector r = 36. # radius of the dome # Topology for 1 sector T = Formex('l:164',3).replic2(n,n,1,1,0,1,0,-1) # 4 sectors m = 4 angle = 360./m # circulize sector D = T.scale(u).circulize(angle) D = D.mapd(2,lambda d:sqrt(r**2-d**2),[0,0,0],[0,1]) dome1=D.rosette(m,angle) clear() draw(dome1) # 6 sectors m = 6 angle = 360./m a = sqrt(3.)/2 D = T.shear(0,1,0.5).scale([1,a,1]) #D = T.replic2(n,n,1,a,0,1,0.5,-1) D = D.scale(u).circulize(angle) D = D.mapd(2,lambda d:sqrt(r**2-d**2),[0,0,0],[0,1]) dome2=D.rosette(m,angle) clear() draw(dome2) # 8 sectors m = 8 angle = 360./m a = sqrt(2.)/2 T = Formex([[[0,0],[1,0]],[[1,0],[a,a]],[[a,a],[0,0]]],3) D = T.replic2(n,n,1,a,0,1,a,-1) # circulize sector D = D.scale(u).circulize(angle) D = D.mapd(2,lambda d:sqrt(r**2-d**2),[0,0,0],[0,1]) dome3=D.rosette(m,angle) clear() draw(dome3) # circulize1 m = 6 angle = 360./m T = Formex('l:127',3) D = T.replic2(n,n,1,1,0,1,1,-1) D = D.scale(u).circulize1() D = D.mapd(2,lambda d:sqrt(r**2-d**2),[0,0,0],[0,1]) dome4=D.rosette(m,angle) clear() draw(dome4) clear() dome4.setProp(1) draw(dome2+dome4) clear() d=1.1*r draw(dome1+dome2.translate([d,0,0])+dome3.translate([0,d,0])+dome4.translate([d,d,0])) pyformex-0.8.6/pyformex/examples/Cone.py0000644000211500021150000000372211705104656020170 0ustar benebene00000000000000# $Id: Cone.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Cone level = 'beginner' topics = ['geometry','surface'] techniques = ['dialog', 'color'] """ import simple reset() smooth() r=3. h=15. n=64 F = simple.sector(r,360.,n,n,h=h,diag=None) #F.coords = F.coords.flare(h/4,r/2,dir=[2,0],end=1,exp=5.) F.setProp(0) draw(F,view='bottom') setDrawOptions({'bbox':None}) zoomAll() zoom(3) print map(str,range(4)) ans = ask('How many balls do you want?',['0','1','2','3']) try: nb = int(ans) except: nb = 3 if nb > 0: B = simple.sphere3(n,n,r=0.9*r,bot=-90,top=90) B1 = B.translate([0.,0.,0.95*h]) B1.setProp(1) draw(B1) #sleep(10) if nb > 1: B2 = B.translate([0.2*r,0.,1.15*h]) B2.setProp(2) draw(B2) if nb > 2: B3 = B.translate([-0.2*r,0.1*r,1.25*h]) B3.setProp(6) draw(B3) zoomAll() zoom(3) focus(B1) pyformex-0.8.6/pyformex/examples/TestDraw.py0000644000211500021150000001063511705104656021042 0ustar benebene00000000000000# $Id: TestDraw.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """TestDraw Example for testing the low level drawing functions level = 'normal' topics = ['geometry','mesh','drawing'] techniques = ['widgets','dialog','random','color'] """ from numpy.random import rand from gui.widgets import simpleInputItem as I setDrawOptions({'clear':True, 'bbox':'auto'}) linewidth(2) # The linewidth option is not working nyet geom_mode = [ 'Formex','Mesh' ] plexitude = [ 1,2,3,4,5,6,8 ] element_type = [ 'auto', 'tet4', 'wedge6', 'hex8' ] color_mode = [ 'none', 'single', 'element', 'vertex' ] # The points used for a single element of plexitude 1..8 Points = { 1: [[0.,0.,0.]], 2: [[0.,0.,0.],[1.,0.,0.]], 3: [[0.,0.,0.],[1.,0.,0.],[0.,1.,0.]], 4: [[0.,0.,0.],[1.,0.,0.],[1.,1.,0.],[0.,1.,0.]], 5: [[0.,0.,0.],[1.,0.,0.],[1.5,0.5,0.],[1.,1.,0.],[0.,1.,0.]], 6: [[0.,0.,0.],[1.,0.,0.],[1.5,0.5,0.],[1.,1.,0.],[0.,1.,0.],[-0.2,0.5,0.]], 'tet4': [[0.,0.,0.],[1.,0.,0.],[0.,1.,0.],[0.,0.,1.]], 'wedge6': [[0.,0.,0.],[1.,0.,0.],[0.,1.,0.],[0.,0.,1.],[1.,0.,1.],[0.,1.,1.]], 'hex8': [[0.,0.,0.],[1.,0.,0.],[1.,1.,0.],[0.,1.,0.],[0.,0.,1.],[1.,0.,1.],[1.,1.,1.],[0.,1.,1.]], } def select_geom(geom,nplex,eltype): """Construct the geometry""" try: nplex = int(eltype[-1]) except: if not nplex in Points.keys(): nplex = max([i for i in plexitude if i in Points.keys()]) eltype = None if eltype is None: x = Points[nplex] else: x = Points[eltype] F = Formex([x],eltype=eltype).replic2(2,2,2.,2.) if geom == 'Formex': return F else: return F.toMesh() def select_color(F,color): """Create a set of colors for object F""" if color == 'single': shape = (1,3) elif color == 'element': shape = (F.nelems(),3) elif color == 'vertex': shape = (F.nelems(),F.nplex(),3) else: return None return rand(*shape) geom = 'Formex' nplex = 3 eltype = 'auto' color = 'element' pos = None items = [ I('geom',geom,'radio',choices=geom_mode,text='Geometry Model'), I('nplex',nplex,'select',choices=plexitude,text='Plexitude'), I('eltype',eltype,'select',choices=element_type,text='Element Type'), I('color',color,'select',choices=color_mode,text='Color Mode'), ] dialog = None def show(): """Accept the data and draw according to them""" dialog.acceptData() res = dialog.results res['nplex'] = int(res['nplex']) globals().update(res) G = select_geom(geom,nplex,eltype) print "GEOM: nelems=%s, nplex=%s" % (G.nelems(),G.nplex()) C = select_color(G,color) if C is not None: print "COLORS: shape=%s" % str(C.shape) draw(G,color=C,clear=True) def close(): global dialog if dialog: dialog.close() dialog = None scriptRelease(__file__) def timeOut(): """What to do on a InputDialog timeout event. As a policy, all pyFormex examples should behave well on a dialog timeout. Most users can simply ignore this. """ show() close() # Create the non-modal dialog widget and show it dialog = widgets.InputDialog(items,caption='Drawing parameters',actions = [('Close',close),('Show',show)],default='Show') dialog.timeout = timeOut dialog.show() scriptLock(__file__) # End pyformex-0.8.6/pyformex/examples/SurfaceProjection.py0000644000211500021150000001532511705104656022733 0ustar benebene00000000000000# $Id: SurfaceProjection.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """SurfaceProjection.py level = 'normal' topics = ['surface'] techniques = ['transform','projection','dialog','image'] original idea: gianluca .. Description SurfaceProjection ----------------- This example illustrates the use of intersectSurfaceWithLines and Coords.projectOnSurface. """ from plugins.trisurface import TriSurface import elements from gui.widgets import ImageView,simpleInputItem as I from gui.imagecolor import * def selectImage(fn): fn = askImageFile(fn) if fn: viewer.showImage(fn) return fn def loadImage(fn): global image, scaled_image image = QImage(fn) if image.isNull(): warning("Could not load image '%s'" % fn) return None return image.width(),image.height() def makeGrid(nx,ny,eltype): """Create a 2D grid of nx*ny elements of type eltype. The grid is scaled to unit size and centered. """ elem = getattr(elements,eltype) return elem.toFormex().replic2(nx,ny).resized(1.).centered() def drawImage(grid,base,patch): """Draw the image on the specified patch grid. grid is a Formex with px*py Quad8 elements. Each element of grid will be filled by a kx*jy patch of colors. """ mT = [ patch.isopar('quad8',x,base) for x in grid.coords ] return [ draw(i,color=c,bbox='last') for i,c in zip (mT,pcolor)] def intersectSurfaceWithSegments2(s1, segm, atol=1.e-5, max1xperline=True): """it takes a TriSurface ts and a set of segments (-1,2,3) and intersect the segments with the TriSurface. It returns the points of intersections and, for each point, the indices of the intersected segment and triangle. If max1xperline is True, only 1 intersection per line is returned (in order to remove multiple intersections due to the tolerance) together with the index of line and triangle (at the moment the selection of one intersection among the others is random: it does not take into account the distances). If some segments do not intersect the surface, their indices are also returned.""" segm = segm.coords p, il, it=trisurface.intersectSurfaceWithLines(s1, segm[:, 0], normalize(segm[:, 1]-segm[:, 0])) win= length(p-segm[:, 0][il])+ length(p-segm[:, 1][il])< length(segm[:, 1][il]-segm[:, 0][il])+atol px, ilx, itx=p[win], il[win], it[win] if max1xperline: ip= inverseIndex(ilx.reshape(-1, 1)) sp=sort(ip, axis=1)[:, -1] w= where(sp>-1)[0] sw=sp[w] return px[sw], w, itx[sw], delete( arange(len(segm)), w) else:return px, ilx, itx clear() smooth() lights(True) transparent(False) view('iso') image = None scaled_image = None # read the teapot surface T = TriSurface.read(getcfg('datadir')+'/teapot.off') xmin,xmax = T.bbox() T= T.trl(-T.center()).scale(4./(xmax[0]-xmin[0])).setProp(2) draw(T) # default image file filename = getcfg('datadir')+'/benedict_6.jpg' viewer = ImageView(filename) px,py = 5,5 #control points for projection of patches kx,ky = 60,50 #number of cells in each patch scale = 0.8 method='projection' res = askItems([ I('filename',filename,text='Image file',itemtype='button',func=selectImage), # viewer, # uncomment this line to add the image previewer I('px',px,text='Number of patches in x-direction'), I('py',py,text='Number of patches in y-direction'), I('kx',kx,text='Width of a patch in pixels'), I('ky',ky,text='Height of a patch in pixels'), I('scale',scale,text='Scale factor'), I('method',method,choices=['projection','intersection']), ]) if not res: exit() globals().update(res) nx,ny = px*kx,py*ky # pixels print 'The image is reconstructed with %d x %d pixels'%(nx, ny) F = Formex('4:0123').replic2(nx,ny).centered() if image is None: print "Loading image" wpic, hpic=loadImage(filename) if image is None: exit() # Create the colors color,colortable = image2glcolor(image.scaled(nx,ny)) # Reorder by patch pcolor = color.reshape((py,ky,px,kx,3)).swapaxes(1,2).reshape(-1,kx*ky,3) #print pcolor.shape mH = makeGrid(px,py,'Quad8') try: hpic, wpic ratioYX = float(hpic)/wpic mH = mH.scale(ratioYX,1) # Keep original aspect ratio except: pass mH0 = mH.scale(scale).translate([-0.5,-0.1,2.]) dg0 = draw(mH0,mode='wireframe') zoomAll() zoom(0.5) pause('Create %s x %s patches > STEP' % (px,py)) # Create the transforms base = makeGrid(1,1,'Quad8').coords[0] patch = makeGrid(kx,ky,'Quad4').toMesh() d0 = drawImage(mH0,base,patch) if method == 'projection': print type(T) pts = mH0.coords.projectOnSurface(T,[0.,0.,1.]) print mH0.shape print pts.shape dg1=d1 = None else: mH1 = mH.rotate(-30.,0).scale(0.5).translate([0.,-.7,-2.]) dg1 = draw(mH1,mode='wireframe') d1 = drawImage(mH1,base,patch) x = connect([mH0.points(), mH1.points()]) dx = draw(x) pause('Creating intersection with surface > STEP') pts, il, it, mil=intersectSurfaceWithSegments2(T, x, max1xperline=True) if len(x) != len(pts): print "Some of the lines do not intersect the surface:" print " %d lines, %d intersections %d missing" % (len(x),len(pts),len(mil)) exit() dp=draw(pts, marksize=6, color='white') print pts.shape mH2 = Formex(pts.reshape(-1,8,3)) if method == 'projection': x = connect([mH0.points(),mH2.points()]) dx = draw(x) pause('Create projection mapping using the grid points > STEP') d2 = drawImage(mH2.trl([0.,0.,0.01]),base,patch) # small translation to make sure the image is above the surface, not cutting it pause('Finally show the finished image > STEP') undraw(dp) undraw(dx) undraw(d0) undraw(d1) undraw(dg0) undraw(dg1) view('front') zoomAll() # End pyformex-0.8.6/pyformex/examples/BezierCurve.py0000644000211500021150000000371611705104656021534 0ustar benebene00000000000000# $Id: BezierCurve.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """BezierCurve level = 'normal' topics = ['geometry', 'curve'] techniques = [] .. Description BezierCurve =========== This example illustrates the use of Bernstein polynomials to evaluate points on a Bezier curve. """ from plugins.curve import * from plugins.nurbs import * predefined = ['514','1234','51414336','custom'] res = askItems([ dict(name='pattern',choices=predefined), dict(name='custom',value=''), ]) if not res: exit() s = res['pattern'] if s == 'custom': s = res['custom'] if not s.startswith('l:'): s = 'l:' + s C = Formex(s).toCurve() clear() linewidth(2) flat() draw(C,bbox='auto',view='front') draw(C.coords) drawNumbers(C.coords) setDrawOptions({'bbox':None}) n = 100 u = arange(n+1)*1.0/n P = pointsOnBezierCurve(C.coords,u) draw(P) # End pyformex-0.8.6/pyformex/examples/Stars.py0000644000211500021150000000521311705104656020375 0ustar benebene00000000000000# $Id: Stars.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Stars level = 'beginner' topics = ['geometry'] techniques = ['color'] """ nstars = 200 # number of stars minpoints = 5 # minimum number of points in the stars maxpoints = 15# maximum number of points in the stars noise = 0.2 # relative amplitude of noise in the shape of the star displ = sqrt(nstars)*3.0 # relative displacement of the stars maxrot = 70. # maximum rotation angle (in degrees) def star(n,noise=0.,prop=0): """Create a regular n-pointed star, possibly with noise and properties. n should be odd and >= 3. With 3 however, the result is a triangle, so at least 5 is recommended. If an even number is given, 1 is added. A noise parameter can be given to vary the regular shape. A prop can be set too. """ if n < 3: n = 3 if n % 2 == 0: n += 1 f = Formex([[[0,1]]]).rosette(n,(n/2)*360./n).view() if noise != 0.: f = f + noise * random.random(f.shape) P = Formex(concatenate([f,f[:1]])) return connect([P,P],bias=[0,1]).setProp(prop) # create random number of points, rotation and translation npts = random.randint(minpoints-1,maxpoints,(nstars,)) rot = random.random((nstars,3)) ang = random.random((nstars,)) * maxrot trl = random.random((nstars,3)) * displ # create the stars Stars = Formex.concatenate([ star(n,noise,i).rotate(a,r).translate(t) for i,n,a,r,t in zip(range(nstars),npts,ang,rot,trl) ]) # draw them with random colors colors = random.random((nstars,3)) clear() draw(Stars,colormap=colors) # End pyformex-0.8.6/pyformex/examples/Connect.py0000644000211500021150000001037311705104656020675 0ustar benebene00000000000000# $Id: Connect.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Connect level = 'normal' topics = ['formex','surface'] techniques = ['connect','color'] """ import simple clear() linewidth(2) flatwire() setDrawOptions({'bbox':'auto'}) # A tapered grid of points F = Formex([0.]).replic2(10,5,taper=-1) draw(F) # Split in parts by testing y-position; note use of implicit loop! G = [ F.clip(F.test(dir=1,min=i-0.5,max=i+0.5)) for i in range(5) ] print [ Gi.nelems() for Gi in G ] def annot(char): [ drawText3D(G[i][0,0]+[-0.5,0.,0.],"%s%s"%(char,i)) for i,Gi in enumerate(G) ] # Apply a general mapping function : x,y,x -> [ newx, newy, newz ] G = [ Gi.map(lambda x,y,z:[x,y+0.01*float(i+1)**1.5*x**2,z]) for i,Gi in enumerate(G) ] clear() annot('G') draw(G) setDrawOptions({'bbox':'last'}) # Connect G0 with G1 H1 = connect([G[0],G[1]]) draw(H1,color=blue) # Connect G1 with G2 with a 2-element bias H2 = connect([G[1],G[2]],bias=[0,2]) draw(H2,color=green) # Connect G3 with G4 with a 1-element bias plus loop H2 = connect([G[3],G[4]],bias=[1,0],loop=True) draw(H2,color=red) # Create a triangular grid of bars clear() annot('G') draw(G) # Connect Gi[j] with Gi[j+1] to create horizontals K1 = [ connect([i,i],bias=[0,1]) for i in G ] draw(K1,color=blue) # Connect Gi[j] with Gi+1[j] to create verticals K2 = [ connect([i,j]) for i,j in zip(G[:-1],G[1:]) ] draw(K2,color=red) # Connect Gi[j+1] with Gi+1[j] to create diagonals K3 = [ connect([i,j],bias=[1,0]) for i,j in zip(G[:-1],G[1:]) ] draw(K3,color=green) # Create triangles clear() annot('G') draw(G) L1 = [ connect([i,i,j],bias=[0,1,0]) for i,j in zip(G[:-1],G[1:]) ] draw(L1,color=red) L2 = [ connect([i,j,j],bias=[1,0,1]) for i,j in zip(G[:-1],G[1:]) ] draw(L2,color=green) # Connecting multiplex Formices using bias clear() annot('K') draw(K1) L1 = [ connect([i,i,j],bias=[0,1,0]) for i,j in zip(K1[:-1],K1[1:]) ] draw(L1,color=red) L2 = [ connect([i,j,j],bias=[1,0,1]) for i,j in zip(K1[:-1],K1[1:]) ] draw(L2,color=green) # Connecting multiplex Formices using nodid clear() annot('K') draw(K1) L1 = [ connect([i,i,j],nodid=[0,1,0]) for i,j in zip(K1[:-1],K1[1:]) ] draw(L1,color=red) L2 = [ connect([i,j,j],nodid=[1,0,1]) for i,j in zip(K1[:-1],K1[1:]) ] draw(L2,color=green) # Add the missing end triangles L3 = [ connect([i,i,j],nodid=[0,1,1],bias=[i.nelems()-1,i.nelems()-1,j.nelems()-1]) for i,j in zip(K1[:-1],K1[1:]) ] draw(L3,color=magenta) # Collect all triangles in a single Formex L = (Formex.concatenate(L1)+Formex.concatenate(L3)).setProp(1) + Formex.concatenate(L2).setProp(2) clear() draw(L) # Convert to a Mesh print "nelems = %s, nplex = %s, coords = %s" % (L.nelems(),L.nplex(),L.coords.shape) M = L.toMesh() print "nelems = %s, nplex = %s, coords = %s" % (M.nelems(),M.nplex(),M.coords.shape) clear() draw(M,color=yellow,mode=flatwire) drawNumbers(M) draw(M.getBorderMesh(),color=black,linewidth=6) # Convert to a surface from plugins.trisurface import TriSurface S = TriSurface(M) print "nelems = %s, nplex = %s, coords = %s" % (S.nelems(),S.nplex(),S.coords.shape) clear() draw(S) print "Total surface area: %s" % S.area() export({'surface-1':S}) setDrawOptions({'bbox':'auto'}) # End pyformex-0.8.6/pyformex/examples/Sphere2.py0000644000211500021150000000322511705104656020612 0ustar benebene00000000000000# $Id: Sphere2.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Sphere2 level = 'normal' topics = ['geometry','surface'] techniques = ['color'] """ from simple import sphere2,sphere3 reset() nx = 4 ny = 4 m = 1.6 ns = 6 smooth() setView('front') for i in range(ns): b = sphere2(nx,ny,bot=-90,top=90).translate(0,-1.0) s = sphere3(nx,ny,bot=-90,top=90) s = s.translate(0,1.0) s.setProp(3) clear() bb = bbox([b,s]) draw(b,bbox=bb,wait=False) draw(s,bbox=bb)#,color='random') nx = int(m*nx) ny = int(m*ny) pyformex-0.8.6/pyformex/examples/RotoTranslation.py0000644000211500021150000001050511705104656022443 0ustar benebene00000000000000# $Id: RotoTranslation.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """RotoTranslation level = 'advanced' topics = ['geometry'] techniques = ['transform'] original idea: gianluca .. Description RotoTranslation --------------- This example illustrates the use of transformCS() to return to an original reference system after a number of affine transformations. """ import simple savewait = pf.GUI.drawwait pf.GUI.drawwait = 2.0 def atExit(): pf.GUI.drawwait = savewait line = 300 line_inc = -40 def createScene(text=None,caged=True,color=None,move=0): """Create a scene of the story. The scene draws the horse (H), with the specified color number (0..7), caged or not, with the local axes (CS), and possibly a text. If move > 0, the horse moves before the scene is drawn. The horse and cage actors are returned. """ global line,H,C,CS if move: H,C,CS = [ i.rotate(30,1).rotate(-10.,2).translate([0.,-move*0.1,0.]) for i in [H,C,CS] ] if caged: cage = draw(C,mode='wireframe',wait=False,) else: cage = None if color is None: color = 1 + random.randint(6) H.setProp(color) horse = draw(H) if text: drawText(text,20,line,size=20) line += line_inc axes = drawAxes(CS) zoomAll() return horse,cage clear() lights(True) view('iso') smooth() transparent(state=False) linewidth(2) setDrawOptions({'bbox':None}) # read the model of the horse F = Formex.read(getcfg('datadir')+'/horse.pgf') # make sure it is centered F = F.centered() # scale it to unity size and head it in the x-direction xmin,xmax = F.bbox() H = F.scale(1./(xmax[0]-xmin[0])).rotate(180, 1) # create the global coordinate system CS0 = CS = CoordinateSystem() # some text # A storage for the scenes script = [] # Scene 0: The story starts idyllic T = 'There once was a white horse running free in the wood.' script += [ createScene(text=T,caged=False,color=7) ] pause() # Scene 1: Things turn out badly T = 'Some wicked pyFormex user caged the horse and transported it around.' # apply same transformations on model and coordinate system H,CS = [ i.translate([0.,3.,6.]) for i in [H,CS] ] C = simple.cuboid(*H.bbox()) script += [ createScene(text=T) ] pause() # Scene 2..n: caged movements T = 'The angry horse randomly changed colour at each step.' script += [ createScene(text=T,move=1) ] m = len(script) n = 16 script += [ createScene(move=i) for i in range(m,n,1) ] pause() # Scene n+1: the escape T = 'Finally the horse managed to escape from the cage.\nIt wanted to go back home and turned black, so it would not be seen in the night.' escape = script[-1] script += [ createScene(text=T,color=0,caged=False) ] undraw(escape) pause() # The problem T = "But alas, it couldn't remember how it got there!!!" drawText(T,20,line,size=20) line += line_inc for s in script[:-2]: #sleep(0.1) undraw(s) pause() # The solution T = "But thanks to pyFormex's orientation,\nit could go back in a single step, straight through the bushes." drawText(T,20,line,size=20) line += line_inc H = H.transformCS(CS0,CS) draw(Formex([[CS[3],CS0[3]]])) pause() T = "And the horse lived happily ever after." script += [ createScene(text=T,color=7,caged=False) ] undraw(script[-2]) # End pyformex-0.8.6/pyformex/examples/DoubleLayer.py0000644000211500021150000000350111705104656021506 0ustar benebene00000000000000# $Id: DoubleLayer.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """DoubleLayer level = 'beginner' topics = ['structure'] techniques = ['color'] """ from plugins import formian clear() n=10; a=2./3.; d=1./n; e1 = Formex([[[0,0,d],[2,0,d]],[[2,0,d],[1,1,d]],[[1,1,d],[0,0,d]]],prop=1) e2 = Formex([[[0,0,d],[1,1-a,0]],[[2,0,d],[1,1-a,0]],[[1,1,d],[1,1-a,0]]],prop=3) # top and bottom layers e4 = e1.replic2(n,n,2,1,bias=1,taper=-1).bb(1./(2*n),1./(2*n)/tand(30)) e5 = e1.replic2(n-1,n-1,2,1,bias=1,taper=-1).translate([1,1-a,-d]).bb(1./(2*n),1./(2*n)/tand(30)) # diagonals e6 = e2.replic2(n,n,2,1,bias=1,taper=-1).bb(1./(2*n),1./(2*n)/tand(30)) e5.setProp(2) # full structure out = (e4+e5+e6).translate(2,-d) draw(out) pyformex-0.8.6/pyformex/examples/ConnectMesh.py0000644000211500021150000000343111705104656021507 0ustar benebene00000000000000# $Id: ConnectMesh.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """ConnectMesh level = 'normal' topics = ['mesh'] techniques = ['connect','color'] """ import simple from mesh import Mesh clear() smoothwire() nx = 4 ny = 3 nz = 7 delay(2) # A rectangular mesh M1 = simple.rectangle(nx,ny).toMesh().setProp(1) # Same mesh, rotated and translated M2 = M1.rotate(45,0).translate([1.,-1.,nz]).setProp(3) draw([M1,M2]) # Leave out the first and the last two elements sel = arange(M1.nelems())[1:-2] m1 = M1.select(sel) m2 = M2.select(sel) clear() draw([m1,m2],view=None) # Connect both meshes to a hexaeder mesh m = m1.connect(m2,nz) clear() draw(m,color=red,view=None) # End pyformex-0.8.6/pyformex/examples/Cube.py0000644000211500021150000000717711705104656020172 0ustar benebene00000000000000# $Id: Cube.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Cube level = 'normal' topics = ['geometry','surface'] techniques = ['color','elements','reverse'] """ from elements import Hex8 from formex import * # This could be obtained from a Mesh conversion def cube_tri(color=None): """Create a cube with triangles.""" back = Formex('3:012934') left = back.rotate(-90,1) bot = back.rotate(90,0) front = back.translate(2,1) right = left.translate(0,1).reverse() top = bot.translate(1,1).reverse() back = back.reverse() faces = front+top+right+back+bot+left if color == 'None': color = 'white' elif color == 'Single': color = 'blue' elif color == 'Face': color = arange(1,7).repeat(2) elif color == 'Full': color = array([[4,5,7],[7,6,4],[7,3,2],[2,6,7],[7,5,1],[1,3,7], [3,1,0],[0,2,3],[0,1,5],[5,4,0],[0,4,6],[6,2,0]]) return faces,color def cube_quad(color=None): """Create a cube with quadrilaterals.""" v = Hex8.vertices f = Hex8.faces faces = Formex(v[f],eltype=f.eltype) if color == 'Single': color = 'red' elif color == 'Face': color = [4,1,5,2,6,3] elif color == 'Full': color = array([7,6,4,5,3,2,0,1])[f] return faces,color def showCube(base,color): #print base,color if base == 'Triangle': cube = cube_tri else: cube = cube_quad cube,color = cube(color) clear() draw(cube,color=color) export({'cube':cube}) # zoomAll() if __name__ == "draw": from gui import widgets clear() reset() smooth() view('iso') baseshape = ['Quad','Triangle'] colormode = ['None','Single','Face','Full'] all = False base = 'Quad' color = 'Full' while True: res = askItems([ _I('All',all), _I('Base',base,choices=baseshape), _I('Color',color,choices=colormode), ],caption="Make a selection or check 'All'") if not res: break; all = res['All'] if all: bases = baseshape colors = colormode else: bases = [ res['Base'] ] colors = [ res['Color'] ] for base in bases: lights(False) for color in colors: showCube(base,color) if all: sleep(1) # Break from endless loop if an input timeout is active ! if widgets.input_timeout >= 0: break # End pyformex-0.8.6/pyformex/examples/Projection.py0000644000211500021150000000302711705104656021416 0ustar benebene00000000000000# $Id: Projection.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Projection level = 'normal' topics = ['geometry','surface'] techniques = ['projection'] """ import simple reset() smoothwire() transparent() lights(True) nx,ny = 20,10 F = simple.rectangle(nx,ny) F = F.trl(-F.center()+[0.,0.,nx/2]) draw(F) G = F.projectOnSphere(ny) draw(G,color=red) H = F.rotate(30).projectOnCylinder(ny) draw(H,color=blue) #End pyformex-0.8.6/pyformex/examples/Baumkuchen.py0000644000211500021150000000367711705104656021377 0ustar benebene00000000000000# $Id: Baumkuchen.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Baumkuchen Vault level = 'beginner' topics = ['structure'] techniques = ['color','bump'] """ clear() m = 12 # number of cells in direction 0 n = 36 # number of cells in direction 1 k = 7 # number of vaults in direction 0 e1 = 30 # elevation of the major arcs e2 = 5 # elevation of the minor arcs # Create a grid of beam elements a1 = Formex('l:2').replic2(m+1,n,1,1,0,1) + \ Formex('l:1').replic2(m,n+1,1,1,0,1) draw(a1,'front') p = array(a1.center()) p[2] = e1 f = lambda x:1-(x/18)**2/2 a2 = a1.bump(2,p,f,1) draw(a2,'bottom',color='red') p[2] = e2 a3 = a2.bump(2,p,lambda x:1-(x/6)**2/2,0) draw(a3,'bottom',color='green') # Replicate the structure in x-direction a4 = a3.replicate(k,dir=0,step=m) draw(a4,'bottom',color='blue') exit() clear() draw(a4,'bottom') # End pyformex-0.8.6/pyformex/examples/Torus.py0000644000211500021150000000366011705104656020421 0ustar benebene00000000000000# $Id: Torus.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Torus level = 'beginner' topics = ['geometry'] techniques = ['color'] """ m = 36 # number of cells along torus big circle n = 36 # number of cells along torus small circle message("Create a triangle with three colored members") F = Formex('l:164',[1,2,3]) clear();draw(F);pause() message("Replicate it into a rectangular pattern") F = F.replic2(m,n,1,1) clear();draw(F);pause() message("Fold the rectangle into a tube") G = F.translate(2,1).cylindrical([2,1,0],[1.,360./n,1.]) clear();draw(G,view='right');pause() message("Bend the tube into a torus with mean radius 5") H = G.translate(0,5).cylindrical([0,2,1],[1.,360./m,1.]) clear();draw(H,view='iso');pause() message("Cut a part from the torus") K = H.cutWithPlane([0.,2.,0.],[1.,1.,1.],side='-') clear() draw(K) pyformex-0.8.6/pyformex/examples/NurbsCurve.py0000644000211500021150000001230711705104656021401 0ustar benebene00000000000000# $Id: NurbsCurve.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """NurbsCurve level = 'advanced' topics = ['geometry', 'curve'] techniques = ['nurbs','connect','border'] .. Description Nurbs ===== """ import simple from plugins.curve import * from plugins.nurbs import * clear() linewidth(2) flat() def drawThePoints(N,n,color=None): umin = N.knots[N.degree] umax = N.knots[-N.degree-1] #print "Umin = %s, Umax = %s" % (umin,umax) u = umin + arange(n+1) * (umax-umin) / float(n) P = N.pointsAt(u) draw(P,color=color,marksize=5) drawNumbers(P,color=color) XD = N.derivatives(u,5)[:4] if XD.shape[-1] == 4: XD = XD.toCoords() x,d1,d2,d3 = XD[:4] e1,e2,e3,k,t = frenet(d1,d2,d3) #print t #k = 1./k #k[isnan(k)] = 0. k /= k[isnan(k) == 0].max() tmax = t[isnan(t) == 0].max() if tmax > 0: t /= tmax #print t s = 0.3 x1 = x+s*e1 x2 = x+s*e2 x3 = x+s*e3 x2k = x+k.reshape(-1,1)*e2 # draw curvature along normal x3t = x+t.reshape(-1,1)*e3 draw(x,marksize=10,color=yellow) draw(connect([Formex(x),Formex(x1)]),color=yellow,linewidth=3) draw(connect([Formex(x),Formex(x2)]),color=cyan,linewidth=2) draw(connect([Formex(x),Formex(x2k)]),color=blue,linewidth=5) draw(connect([Formex(x),Formex(x3)]),color=magenta,linewidth=2) draw(connect([Formex(x),Formex(x3t)]),color=red,linewidth=5) def drawNurbs(points,pointtype,degree,strategy,closed,blended,weighted=False,Clear=False): if Clear: clear() X = pattern(points) F = Formex(X) draw(F,marksize=10,bbox='auto',view='front') drawNumbers(F,leader='P',trl=[0.02,0.02,0.]) if closed: # remove last point if it coincides with first x,e = Coords.concatenate([X[0],X[-1]]).fuse() if x.shape[0] == 1: X = X[:-1] blended=True draw(PolyLine(X,closed=closed)) if not blended: nX = ((len(X)-1) // degree) * degree + 1 X = X[:nX] if weighted: wts = array([1.]*len(X)) wts[1::2] = 0.5 #print wts,wts.shape else: wts=None if pointtype == 'Control': N = NurbsCurve(X,wts=wts,degree=degree,closed=closed,blended=blended) else: N = globalInterpolationCurve(X,degree=degree,strategy=strategy) draw(N,color=red) #drawThePoints(N,11,color=black) clear() setDrawOptions({'bbox':None}) linewidth(2) flat() dialog = None def close(): global dialog if dialog: dialog.close() dialog = None # Release scriptlock scriptRelease(__file__) def show(): dialog.acceptData() res = dialog.results export({'_Nurbs_data_':res}) drawNurbs(**res) def showAll(): dialog.acceptData() res = dialog.results export({'_Nurbs_data_':res}) for points in predefined: print res res['points'] = points drawNurbs(**res) def timeOut(): showAll() wait() close() predefined = [ '51414336', '51i4143I36', '2584', '25984', '184', '514', '1234', '5858585858', '12345678', '121873', '1218973', '8585', '85985', '214121', '214412', '151783', 'ABCDABCD', ] data_items = [ _I('points',text='Point set',choices=predefined), _I('pointtype',text='Point type',itemtype='select',choices=['Control','OnCurve']), _I('degree',2), _I('strategy',0.5), _I('closed',False), _I('blended',True,enabled=False), _I('weighted',False), _I('Clear',True), ] input_enablers = [ ('pointtype','OnCurve','strategy'), ('pointtype','Control','closed'), ('pointtype','Control','blended'), ('pointtype','Control','weighted'), # ('closed',False,'blended'), ] dialog = Dialog( data_items, enablers = input_enablers, caption = 'Nurbs parameters', actions = [('Close',close),('Clear',clear),('Show All',showAll),('Show',show)], default = 'Show', ) if pf.PF.has_key('_Nurbs_data_'): dialog.updateData(pf.PF['_Nurbs_data_']) dialog.timeout = timeOut dialog.show() # Block other scripts scriptLock(__file__) # End pyformex-0.8.6/pyformex/examples/HorseTorse.py0000644000211500021150000000413311705104656021376 0ustar benebene00000000000000# $Id: HorseTorse.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """HorseTorse level = 'advanced' topics = ['geometry','surface'] techniques = ['animation', 'color'] Torsing a horse is like horsing a torse. """ from plugins.trisurface import TriSurface def drawSurf(F,surface=False,**kargs): """Draw a Formex as surface or not.""" if surface: F = TriSurface(F) return draw(F,**kargs) reset() smooth() lights(True) surf=True F = Formex.read(getcfg('datadir')+'/horse.pgf') F = F.translate(-F.center()) xmin,xmax = F.bbox() F = F.scale(1./(xmax[0]-xmin[0])) FA = drawSurf(F,surf) angle = 360. n = 120 da = angle*Deg/n F.setProp(1) for i in range(n+1): a = i*da torse = lambda x,y,z: [x,cos(a*x)*y-sin(a*x)*z,cos(a*x)*z+sin(a*x)*y] G = F.map(torse) GA = drawSurf(G,surf) undraw(FA) FA = GA elong = 2.0 nx = 50 dx = elong/nx for i in range(nx+1): s = 1.0+i*dx H = G.scale([s,1.,1.]) GA = drawSurf(H,surf,bbox=None) undraw(FA) FA = GA pyformex-0.8.6/pyformex/examples/BarrelVault1.py0000644000211500021150000000376711705104656021621 0ustar benebene00000000000000# $Id: BarrelVault1.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Barrel Vault Shell level = 'beginner' topics = ['frame'] techniques = ['dialog'] """ reset() smoothwire() res = askItems([ dict(name='m',value=12,text='number of modules in axial direction'), dict(name='n',value=8,text='number of modules in tangential direction'), dict(name='r',value=10.,text='barrel radius'), dict(name='a',value=180.,text='barrel opening angle'), dict(name='l',value=30.,text='barrel length'), dict(name='eltype',value='quad8',text='element type',itemtype='radio',choices=['tri3','quad4','quad8','quad9']), ]) if not res: exit() globals().update(res) # Grid g = Formex('4:0123').replic2(m,n).toMesh().convert(eltype) # Create barrel barrel = g.rotate(90,1).translate(0,r).scale([1.,a/n,l/m]).cylindrical() draw(barrel,color=red,bkcolor=blue) #drawNumbers(barrel.coords) # End pyformex-0.8.6/pyformex/examples/Props.py0000644000211500021150000000340711705104656020407 0ustar benebene00000000000000# $Id: Props.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Props level = 'beginner' topics = ['geometry'] techniques = ['viewport', 'color', 'symmetry'] A demonstration of propagating property numbers. Also shows the use of multiple viewports. """ def vp(i): viewport(i) smooth() lights(False) clear() if __name__ == "draw": layout(4) F0 = Formex('3:012934',[1,3]) F1 = F0.replic2(2,2) F2 = F1 + F1.reflect(1) F3 = F2 + F2.rotate(180.,1) for i,F in enumerate([F0,F1,F2,F3]): vp(i) draw(F) #drawText("F%s"%i,10,10,size=18) pf.canvas.update() #sleep(2) # End pyformex-0.8.6/pyformex/examples/Schwedler.py0000644000211500021150000000371511705104656021226 0ustar benebene00000000000000# $Id: Schwedler.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Schwedler Dome level = 'normal' topics = ['geometry','domes'] techniques = ['color'] """ clear() wireframe() nx=16 # number of modules in circumferential direction ny=8 # number of modules in meridional direction rd=100 # radius of the sphere cap base=50 # slope of the dome at its base (= half angle of the sphere cap) top=5 # slope of the dome at its top opening (0 = no opening) a = ny*float(top)/(base-top) e1 = Formex('l:54',[1,3]) # diagonals and meridionals e2 = Formex('l:1',0) # horizontals f1 = e1.replic2(nx,ny,1,1) f2 = e2.replic2(nx,ny+1,1,1) g = (f1+f2).translate([0,a,1]).spherical(scale=[360./nx,base/(ny+a),rd],colat=True) draw(e1+e2) draw(f1+f2) clear() draw(g) h = g.withProp([0,3]) # only horizontals and meridionals clear() draw(g+h.translate([2*rd,0,0])) pyformex-0.8.6/pyformex/examples/DataInterpolation.py0000644000211500021150000000611711705104656022726 0ustar benebene00000000000000# $Id: DataInterpolation.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """DataInterpolation level = 'advanced' topics = [ 'mesh','postprocess'] techniques = ['calpy','color'] .. Description DataInterpolation ----------------- This example demonstrates how to use calpy to interpolate data on a Mesh. The data are given in the Gauss integration points (GP) and are computed in the nodes, but the same technique can be used if data are given in other points or need to be calculated in other points. """ # First, we need to import calpy. If you do not have calpy, # download it from ftp://bumps.ugent.be/pub/calpy # and compile/install it # Locate calpy and load interface from plugins import calpy_itf # Now, let's create a grid of 'quad8' elements # size of the grid nx,ny = 4,3 # plexitude nplex = 8 clear() flatwire() M = Formex('4:0123').replic2(nx,ny).toMesh().convert('quad%s'%nplex,fuse=True) #draw(M,color=yellow) # Create the Mesh interpolateor gprule = (5,1) # integration rule: minimum (1,1), maximum (5,5) Q = calpy_itf.QuadInterpolator(M.nelems(),M.nplex(),gprule) # Define some random data at the GP. # We use 3 data per GP, because we will use the data directly as colors ngp = prod(gprule) # number of datapoints per element data = random.rand(M.nelems(),ngp,3) print "Number of data points per element: %s" % ngp print "Original element data: %s" % str(data.shape) # compute the data at the nodes, per element endata = Q.GP2Nodes(data) print "Element nodal data: %s" % str(endata.shape) # compute nodal averages nodata = Q.NodalAvg(M.elems+1,endata,M.nnodes()) print "Average nodal data: %s" % str(nodata.shape) # extract the colors per element colors = nodata[M.elems] print "Color data: %s" % str(colors.shape) layout(2) viewport(0) clear() flatwire() draw(M,color=endata) drawNumbers(M.coords) drawText("Per element interpolation",20,20,font='9x15') viewport(1) clear() flatwire() draw(M,color=colors) drawNumbers(M.coords) drawText("Averaged nodal values",20,20,font='9x15') #End pyformex-0.8.6/pyformex/examples/Edit.py0000644000211500021150000000416111705104656020167 0ustar benebene00000000000000# $Id: Edit.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Edit Global Variables level = 'advanced' topics = ['editing'] techniques = ['persistence','interactive'] """ clear() alfabet = { 'A': 'l:22144/61', 'C': 'l:/13221', } def plotChar(char): try: draw(Formex(alfabet[char])) except: raise ValueError,"Can not plot character %s" % char data = dict( a = 1, b = 1.5, c = [0.,0.,0.], d = 'red', f = Formex('l:121212') ) export(data) globals().update(data) #draw(f) plotChar('A') plotChar('C') right = array([1.,0.,0.]) print right left = -right print left from plugins.curve import * clear() x = Formex('3:065').coords.reshape(-1,3) C = BezierSpline(x) print C.pointsOn() draw(C.pointsOn()) draw(C,color=red) C = BezierSpline(x.trl(0,1.),deriv=[left,right]) print C.pointsOn() draw(C.pointsOn()) draw(C,color=blue) exit() x = Coords([[0,0],[0,2],[0,2],[1,1.5],[0,1],[0,1],[1,0.5],[0,0]]) C = BezierSpline(x) draw(C.pointsOn()) draw(C,color=red) # End pyformex-0.8.6/pyformex/examples/Colored.py0000644000211500021150000000367211705104656020677 0ustar benebene00000000000000# $Id: Colored.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Colored level = 'beginner' topics = ['surface'] techniques = ['color'] """ from gui.actors import * smooth() lights(False) Rendermode = [ 'smooth','flat' ] Lights = [ False, True ] Shapes = [ '3:016', '4:0123', ] color0 = None # no color: current fgcolor color1 = red # single color color2 = array([red,green,blue]) # 3 colors: will be repeated for shape in Shapes: F = Formex(shape).replic2(8,4) color3 = resize(color2,F.shape()) # full color for mode in Rendermode: renderMode(mode) for c in [ color0,color1,color2,color3]: clear() FA = GeomActor(F,color=c) drawActor(FA) print c zoomAll() for light in Lights: lights(light) sleep(1) # End pyformex-0.8.6/pyformex/examples/BezierSpline.py0000644000211500021150000000553011705104656021676 0ustar benebene00000000000000# $Id: BezierSpline.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """BezierSpline level = 'normal' topics = ['geometry', 'curve'] techniques = ['spline','dialog'] .. Description Bezier Spline ============= This example shows a collection of Bezier Spline curves through a number of points. The points used are the corners and midside points of a unit square. The user is asked for a number of points to use. The image shows open (left) and closed (right) BezierSpline curves of degrees 1(red), 2(magenta) and 3(blue). """ from gui.widgets import simpleInputItem as I from plugins.curve import BezierSpline clear() linewidth(2) # Predefined set of points pts = Coords([ [1.,0.,0.], [1.,1.,0.], [0.,1.,0.], [-1.,1.,0.], [-1.,0.,0.], [-1.,-1.,0.], [0.,-1.,0.], [1.,-1.,0.], [1.,0.,0.], ]) # Ask the user how many points he wants to use res = askItems([I('npts',5,text='How many points to use (2..%s)' % len(pts))]) if not res: exit() # Keep only the requested number of points npts = res['npts'] pts = pts[:npts] # Show open and closed Bezier Splines, for degrees 1,2,3 degrees = [1,2,3] colors = [red,green,blue] collection = {} for closed in [False,True]: draw(pts) drawNumbers(pts) for d,c in zip(degrees,colors): print "DEGREE %s, %s" % (d,closed) B = BezierSpline(coords=pts,closed=closed,degree=d) collection["BezierSpline-degree:%s-closed:%s" % (d,closed)] = B draw(B,color=c) t = arange(2*B.nparts)*0.5 ipts = B.pointsAt(t) draw(ipts,color=c,marksize=10) idir = B.directionsAt(t) drawVectors(ipts,0.2*idir) # Translate the points to the right pts = pts.trl(0,2.5)#[:-1] zoomAll() export(collection) # End pyformex-0.8.6/pyformex/examples/Multicut.py0000644000211500021150000000322311705104656021106 0ustar benebene00000000000000# $Id: Multicut.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Multicut level = 'beginner' topics = ['surface'] techniques = ['cut'] .. Description Multicut -------- This example shows how to cut a hole in a surface. It uses the cutWithPlane function with a series of cutting planes. """ clear() from plugins.trisurface import Sphere S = Sphere(4).scale(3.) T = S.cutWithPlane([[2.,0.,0.],[0.,1.,0.],[-2.,0.,0.],[0.,-1.,0.]], [[-1.,0.,0.],[0.,-1.,0.],[1.,0.,0.],[0.,+1.,0.]], side = '-') draw(T) # End pyformex-0.8.6/pyformex/examples/ExtrudeMesh.py0000644000211500021150000000412011705104656021532 0ustar benebene00000000000000# $Id: ExtrudeMesh.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """ExtrudeMesh level = 'beginner' topics = ['mesh'] techniques = ['extrude'] """ clear() nx,ny,nz = 5,3,2 degree = 2 # create quadratic extrusions, change to 1 for linear noise = 0.0 # set nonzero to add some noise to the coordinates smoothwire() view('iso') delay(0) a = Formex([0.,0.,0.]).toMesh() # a point at the origin print a.eltype draw(a,color='black') delay(2) b = a.extrude(nx,1.,0,degree=degree) # point extruded to quadratic line print b.eltype draw(b.coords,wait=False) draw(b,color='red') c = b.extrude(ny,1.,1,degree=degree) # line extruded to quadratic surface print c.eltype draw(c.coords,wait=False) draw(c,color='blue') d = c.extrude(nz,-1.,2,degree=degree) # surface extruded to quadratic volume print d.eltype draw(d.coords,wait=False) draw(d,color='yellow') if noise: e = d.addNoise(noise) draw(e.coords,wait=False,clear=True) draw(e,color=cyan) # End pyformex-0.8.6/pyformex/examples/Light.py0000644000211500021150000000317311705104656020353 0ustar benebene00000000000000# $Id: Light.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Light level = 'beginner' topics = ['geometry'] techniques = ['dialog', 'color', 'persistence'] """ smooth() Shape = { 'triangle':'3:016', 'quad':'4:0123', } color2 = array([red,green,blue]) # 3 base colors F = Formex(Shape['triangle']).replic2(8,4) color3 = resize(color2,F.shape()) draw(F,color=color3) #for a in [ 'ambient', 'specular', 'emission', 'shininess' ]: # v = getattr(pf.canvas,a) # print " %s: %s" % (a,v) # End pyformex-0.8.6/pyformex/examples/Extrude.py0000644000211500021150000000312511705104656020721 0ustar benebene00000000000000# $Id: Extrude.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Extrude level = 'beginner' topics = ['formex'] techniques = ['extrude'] """ clear() smooth() M = Formex('4:0123').replic2(60,30).toMesh()#.setProp(5) draw(M,color=yellow) #drawNumbers(M) exit() smoothwire() view('iso') delay(1) a = Formex([0.,0.,0.]) draw(a,color='black') b = a.extrude(8,1.,1) draw(b,color='red') c = b.extrude(8,1.,0) draw(c,color='blue') d = c.extrude(7,-1.,2) draw(d,color='yellow') # End pyformex-0.8.6/pyformex/examples/Lima.py0000644000211500021150000001626711705104656020176 0ustar benebene00000000000000# $Id: Lima.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Lima examples level = 'normal' topics = ['illustration'] techniques = ['dialog','lima'] """ # We use the lima module from plugins import lima,turtle # allow this example to be used as a module from gui.draw import * # return standard Turtle rules def turtlecmds(rules={}): """Return standard Turtle rules, extended and/or overriden by arg. The specified arg should be a dictionary of rules, which will extend and/or override the default rules. """ d = { 'F' : 'fd();', 'G' : 'fd();', '+' : 'ro(90);', '-' : 'ro(-90);', '*' : 'ro(60);', '/' : 'ro(-60);', 'J':'mv();','K':'mv();', 'X':'', 'Y':'', '[':'push();', ']':'pop();' } d.update(rules) return d # here are some nice lima generations. # Each tuple holds an axiom, grow rules, generations and turtle rules limas = { 'Dragon Curve': [ "F", {"F":"F+G","G":"F-G"},10,turtlecmds() ], 'Koch Line': [ "F", {"F":"F*F//F*F"},6,turtlecmds() ], 'rule2': [ "F+F+F+F", {"F":"FF+FF--FF+F"},4,turtlecmds() ], 'rule3': [ "F+F+F+F", {"F":"FF+F+F-F+FF"},4,turtlecmds() ], 'Koch Snowflake': [ "F//F//F", {"F":"F*F//F*F"},5,turtlecmds() ], 'rule4': [ "F+F+F+F", {"F":"FF+F++F+F"},4,turtlecmds() ], 'rule5': [ "F+F+F+F", {"F":"FF+F+F+F+F+F-F"},4,turtlecmds() ], 'Hilbert Curve': [ "X", {"X":"-YF+XFX+FY-", "Y":"+XF-YFY-FX+"},5,turtlecmds() ], 'Greek Cross Curve': [ "F+XF+F+XF", {"X":"XF-F+F-XF+F+XF-F+F-X"},4,turtlecmds() ], 'Peano Curve': [ "X", {"X":"XFYFX+F+YFXFY-F-XFYFX", "Y":"YFXFY-F-XFYFX+F+YFXFY"},4,turtlecmds() ], 'Gosper Curve': [ "XF", {"X":"X*YF**YF/FX//FXFX/YF*", "Y":"/FX*YFYF**YF*FX//FX/Y"},4,turtlecmds() ], 'Sierpinski Triangle': [ "F**F**F", {"F":"F*J++F**F", "J":"JJ"},6,turtlecmds() ], 'Sierpinski Triangle1': [ "F", {"F":"*G/F/G*", "G":"/F*G*F/"},8,turtlecmds() ], 'Sierpinski Carpet': [ "F+F+F+F", {"F":"JF+F+F+F+JF+F+F+F+J", "J":"JJJ"},3,turtlecmds() ], 'Gosper Island': [ "F*F*F*F*F*F", {"F":"+F/F*F-"},5,turtlecmds({'+':'ro(20);','-':'ro(-20);'}) ], 'Gosper Island Tiling': [ "F*F*F*F*F*F/F/F/F/F/F*F*F*F*F*F", {"F":"+F/F*F-"},4,turtlecmds({'+':'ro(20);','-':'ro(-20);'}) ], 'Plant0': [ "+F", {"F":"F[*F]F[/F]F"},5,turtlecmds({'*':'ro(25);','/':'ro(-25);'}) ], 'Plant1': [ "+Y", {"Y":"YFX[*Y][/Y]", "X":"[/F][*F]FX"},7,turtlecmds({'*':'ro(25);','/':'ro(-25);'}) ], 'Breezy Bush': [ "+F", {"F":"FF[//F*F*F][*F/F/F]"},4,turtlecmds({'*':'ro(22.55);','/':'ro(-22.5);'}) ], 'Islands and Lakes': [ "F-F-F-F", {"F":"F-J+FF-F-FF-FJ-FF+J-FF+F+FF+FJ+FFF", "J":"JJJJJJ"},2,turtlecmds() ], 'Hexagones': [ "F*F*F*F*F*F", {"F":"[//J*G*F*G]J", "G":"[//K*G*F*G]J"},5,turtlecmds() ], 'Lace': [ "F+F", {"F":"F*FF**F**FF*F"},4,turtlecmds() ], 'rule19': [ "F++F", {"F":"*F//F*"}, 10, turtlecmds({'*':'ro(30);','/':'ro(-30);'}) ], 'rule20': [ "F+F+F+F", {"F":"*F//G*","G":"/F**G/"}, 8, turtlecmds({'*':'ro(30);','/':'ro(-30);'}) ], 'rule21': [ "G+G+G+G", {"F":"*F//G*","G":"/F**G/"}, 8, turtlecmds({'*':'ro(30);','/':'ro(-30);'}) ], 'Grass': [ "***X", { "F":"FF", "X":"F*[[X]/X]/F[/FX]*X" }, 6, turtlecmds({'*':'ro(25);','/':'ro(-25);'}) ], #22: [ "+F", {"F":"GH", "G":"GG", "H":"G[*F][/F]"}, 12, turtlecmds({'*':'ro(22.5);','/':'ro(-22.5);'}) ], #23: [ "F", {"F":"*F-F*"}, 12, turtlecmds({'*':'ro(45);'}) ], #24: [ "JF", {"F":"*F-FF+F*","J":"/J"}, 8, turtlecmds({'*':'ro(45);','/':'ro(-45);'}) ], #25: [ "F", {"F":"F-F++F-F"}, 4, turtlecmds() ], } def show(i,L,turtle_cmds,clear=True,text=True,colors=True): """Show the current production of the Lima L.""" global FA,TA turtle_script = L.translate(turtle_cmds) coords = turtle.play("reset();" + turtle_script) if len(coords) > 0: if colors: prop = i else: prop = 0 FB = draw(Formex(coords,prop)) if clear: undraw(FA) FA = FB if text: TB = drawText("Generation %d"%i,40,40,size=24) undecorate(TA) TA = TB def grow(rule='',clearing=True,text=True,ngen=-1,colors=True,viewports=False): """Show subsequent Lima productions.""" global FA,TA FA = None TA = None viewport(0) clear() if not rule in limas.keys(): return if text: drawText(rule,40,60,size=24) a,r,g,t = limas[rule] if ngen >= 0: # respect the requested number of generations g = ngen L = lima.Lima(a,r) # show the axiom show(0,L,t,clearing,text) # show g generations for i in range(g): if viewports: viewport(i+1) L.grow() show(i+1,L,t,clearing,text) def setDefaultGenerations(rule): rule = str(rule) if limas.has_key(rule): ngen = limas[rule][2] d = currentDialog() if d: d.updateData({'ngen':ngen}) if __name__ == "draw": layout(1) viewport(0) clear() wireframe() linewidth(2) keys = limas.keys() keys.sort() choices = ['__all__','__custom__'] + keys defaults = { 'rule':None, 'ngen':-1, 'colors':True, 'clearing':True, 'viewports':False } defaults = pf.PF.get('__Lima__data',defaults) res = askItems([ dict(name='rule',value=defaults['rule'],text='Production rule',choices=choices,onselect=setDefaultGenerations), dict(name='ngen',value=defaults['ngen'],text='Number of generations (-1 = default)'), dict(name='colors',value=defaults['colors'],text='Use different colors per generation'), dict(name='clearing',value=defaults['clearing'],text='Clear screen between generations'), dict(name='viewports',value=defaults['viewports'],text='Use a separate viewport for each generation'), ]) if res: globals().update(res) pf.PF['__Lima__data'] = res if viewports: layout(ngen+1,ncols=(ngen+2)//2) if rule == '__custom__': pass elif rule == '__all__': for rule in keys: res['rule'] = rule grow(**res) else: grow(**res) # End pyformex-0.8.6/pyformex/examples/FeEx.py0000644000211500021150000010426011705104656020132 0ustar benebene00000000000000# $Id: FeEx.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """FeEx level = 'advanced' topics = ['FEA'] techniques = ['menu', 'dialog', 'persistence', 'color'] """ from pyformex import GUI,PF from gui import menu from gui.widgets import simpleInputItem as I from simple import rectangle from plugins.fe import * from plugins.properties import * from plugins.fe_abq import * from plugins.fe_post import FeResult from plugins import postproc_menu from plugins import geometry_menu from plugins import isopar from odict import ODict import utils # global data parts = None model = None PDB = None # check for existing results feresult_base = 'FeResult' def numericEnd(s): i = utils.splitEndDigits(s) if len(i[1]) > 0: return int(i[1]) else: return -1 feresults = [ k for k in pf.PF.keys() if k.startswith(feresult_base)] if feresults: feresults.sort(lambda a,b:a-b, numericEnd) name = feresults[-1] else: name = feresult_base feresult_name = utils.NameSequence(name) def resetData(): global parts,model,PDB parts = PF.get('FeEx-parts',[]) model = PF.get('FeEx-model',None) PDB = PF.get('FeEx-propdb',None) geometry_menu.selection.set([]) def saveData(): export({'FeEx-parts':parts,'FeEx-model':model,'FeEx-propdb':PDB}) def reset(): clear() smoothwire() transparent() #lights(False) geometry_menu.selection.draw() def deleteAll(): global parts,model,PDB parts = [] model = None PDB = None geometry_menu.selection.set([]) reset() ######################## parts #################### x0,y0 = 0.,0. x1,y1 = 1.,0. x2,y2 = 1.,1. x3,y3 = 0.,1. nx,ny = 4,4 eltype = 'quad' def createRectPart(res=None): """Create a rectangular domain from user input""" global x0,y0,x2,y2,nx,ny,eltype if model is not None: if ask('You have already merged the parts! I can not add new parts anymore.\nYou should first delete everything and recreate the parts.',['Delete','Cancel']) == 'Delete': deleteAll() else: return if res is None: res = askItems([ I('x0',x0,tooltip='The x-value of one of the corners'), I('y0',y0), I('x2',x2),I('y2',y2), I('nx',nx),I('ny',ny), I('eltype',eltype,itemtype='radio',choices=['quad','tri-u','tri-d']), ]) if res: globals().update(res) if x0 > x2: x0,x2 = x2,x0 if y0 > y2: y0,y2 = y2,y0 diag = {'quad':'', 'tri-u':'u', 'tri-d':'d'}[eltype] M = rectangle(nx,ny,x2-x0,y2-y0,diag=diag).toMesh().trl([x0,y0,0]) addPart(M) def createQuadPart(res=None): """Create a quadrilateral domain from user input""" global x0,y0,x1,y1,x2,y2,x3,y3,nx,ny,eltype if model is not None: if ask('You have already merged the parts! I can not add new parts anymore.\nYou should first delete everything and recreate the parts.',['Delete','Cancel']) == 'Delete': deleteAll() else: return if res is None: res = askItems([ I('Vertex 0',(x0,y0)), I('Vertex 1',(x1,y1)), I('Vertex 2',(x2,y2)), I('Vertex 3',(x3,y3)), I('nx',nx), I('ny',ny), I('eltype',eltype,itemtype='radio',choices=['quad','tri-u','tri-d']), ]) if res: x0,y0 = res['Vertex 0'] x1,y1 = res['Vertex 1'] x2,y2 = res['Vertex 2'] x3,y3 = res['Vertex 3'] nx = res['nx'] ny = res['ny'] eltype = res['eltype'] diag = {'quad':'', 'tri-u':'u', 'tri-d':'d'}[eltype] xold = rectangle(1,1).coords xnew = Coords([[x0,y0],[x1,y1],[x2,y2],[x3,y3]]) M = rectangle(nx,ny,1.,1.,diag=diag).toMesh().isopar('quad4',xnew,xold) addPart(M) def addPart(M): """Add a Mesh to the parts list.""" global parts n = len(parts) part = M.setProp(n) parts.append(part) partname = 'part-%s'%n export({partname:part}) geometry_menu.selection.names.append(partname) geometry_menu.selection.draw() def convertQuadratic(qtype='quad8'): """Convert the parts to quadratic""" global parts parts = [ p.convert(qtype) for p in parts ] geometry_menu.selection.changeValues(parts) drawParts() def convertQuadratic9(): """Convert the parts to quadratic9""" convertQuadratic(qtype='quad9') def drawParts(): """Draw all parts""" clear() ## draw(parts) ## [ drawNumbers(p,color=blue) for p in parts ] ## [ drawNumbers(p.coords,color=red) for p in parts ] ## zoomAll() geometry_menu.selection.draw() ######################## the model #################### def createModel(): """Merge all the parts into a Finite Element model.""" global model,PDB model = Model(*mergeMeshes(parts)) PDB = PropertyDB() export({'FeEx-parts':parts,'FeEx-model':model,'FeEx-propdb':PDB}) drawModel() def drawModel(offset=0): """Draw the merged parts""" if model is None: warning("You should first merge the parts!") return flatwire() transparent(True) clear() meshes = [ Mesh(model.coords,e,eltype='quad4') for e in model.elems ] draw(meshes,color='yellow') #drawNumbers(Formex(model.coords),color=red,offset=offset) #[ drawNumbers(m,leader='%s-'%i) for i,m in enumerate(meshes) ] zoomAll() def drawCalpy(): """This should draw node numbers +1""" drawModel(offset=1) def pickNodes(): """Let user pick nodes and return node numbers. This relies on the model being merged and drawn, resulting in a single actor having point geometry. Thus we do not bother about the key. """ K = pickPoints() for k,v in K.items(): if len(v) > 0: return v return None def getPickedElems(K,p): """Get the list of picked elems from part p.""" if p in K.keys(): return K[p] return [] def printModel(): print "model:",model ################# Add properties ###################### def warn(): warning("You should first merge the parts!") section = { 'name':'thin plate', 'sectiontype': 'solid', 'young_modulus': 207000, 'poisson_ratio': 0.3, 'thickness': 1.0, } def setMaterial(): """Set the material""" global section if model is None: warn() return removeHighlights() keys = ['name','sectiontype','young_modulus','poisson_ratio','thickness'] items = [ (k,section[k]) for k in keys ] res = askItems(items) if res: section.update(res) K = pickElements() if K: for k in range(len(parts)): e = getPickedElems(K,k) + model.celems[k] print k,e if len(e) > 0: PDB.elemProp(set=e,eltype='CPS4',section=ElemSection(section=section)) def deleteAllMats(): PDB.delProp(kind='e',attr=['eltype']) # Boundary conditions xcon = True ycon = True def setBoundary(): """Pick the points with boundary condition.""" global PDB,xcon,ycon if model is None: warn() return removeHighlights() res = askItems([('x-constraint',xcon),('y-constraint',ycon)]) if res: xcon = res['x-constraint'] ycon = res['y-constraint'] nodeset = pickNodes() if len(nodeset) > 0: print nodeset bcon = [int(xcon),int(ycon),0,0,0,0] print "SETTING BCON %s" % bcon PDB.nodeProp(set=nodeset,bound=[xcon,ycon,0,0,0,0]) def deleteAllBcons(): PDB.delProp(kind='n',attr=['bound']) # Concentrated loads xload = 0.0 yload = 0.0 nloads = 1 lc = 0 def setCLoad(): """Pick the points with load condition.""" global xload,yload,nloads if model is None: warn() return removeHighlights() res = askItems([ #('load-case',lc), ('x-load',xload),('y-load',yload)]) if res: #lc = res['load-case'] xload = res['x-load'] yload = res['y-load'] nodeset = pickNodes() if len(nodeset) > 0: print nodeset print "SETTING CLOAD %s" % [xload,yload,0.,0.,0.,0.] PDB.nodeProp(set=nodeset,tag="Load-%s"%lc,cload=[xload,yload,0.,0.,0.,0.]) nloads = max(nloads,lc+1) def deleteAllCLoads(): PDB.delProp(kind='n',attr=['cload']) # Edge loads edge_load = {'x':0., 'y':0.} def setELoad(): """Pick the edges with load condition.""" global edge_load if model is None: warn() return removeHighlights() edge_load = askItems([ ('x',edge_load['x'],{'text':'x-load'}), ('y',edge_load['y'],{'text':'y-load'}), ]) if edge_load: K = pickEdges() for k in K.keys(): v = K[k] elems,edges = v // 4, v % 4 print k,elems,edges for el,edg in zip(elems,edges): for label in 'xy': if edge_load[label] != 0.: PDB.elemProp(set=el,group=k,eload=EdgeLoad(edge=edg,label=label,value=edge_load[label])) def deleteAllELoads(): PDB.delProp(kind='e',attr=['eload']) def printDB(): print "\n*** Node properties:" for p in PDB.nprop: print p print "\n*** Element properties:" for p in PDB.eprop: print p ############################# Abaqus ############################## def createAbaqusInput(): """Write the Abaqus input file.""" # ask job name from user res = askItems([('JobName',feresult_name.next())]) if not res: return jobname = res['JobName'] if not jobname: print "No Job Name: writing to sys.stdout" jobname = None out = [ Output(type='history'), Output(type='field'), ] res = [ Result(kind='NODE',keys=['U','COORD']), Result(kind='ELEMENT',keys=['S'],pos='AVERAGED AT NODES'), Result(kind='ELEMENT',keys=['SINV'],pos='AVERAGED AT NODES'), Result(kind='ELEMENT',keys=['SF'],pos='AVERAGED AT NODES'), ] step1 = Step(time=[1.,1.,0.01,1.],nlgeom='no',tags=[1]) step2 = Step(time=[1.,1.,0.01,1.],nlgeom='no',tags=[2]) AbqData(model,PDB,[step1,step2],out=out,res=res).write(jobname) ############################# Calix ############################## def createCalixInput(): """Write the Calix input file.""" checkWorkdir() if model is None: warn() return # ask job name from user res = askItems([ I('jobname',feresult_name.next(),text='Job Name'), I('header','A Calix example',text='Header Text'), I('zem','3',text='ZEM control',itemtype='radio',choices=['0','3','6'],), ]) if not res: return jobname = res['jobname'] header = res['header'] nzem = int(res['zem']) if not jobname: print "No Job Name: writing to sys.stdout" jobname = None filnam = jobname+'.dta' print("Writing calix data file %s in %s" % (filnam,os.getcwd())) fil = open(filnam,'w') nnodes = model.coords.shape[0] nelems = model.celems[-1] nplex = [ e.shape[1] for e in model.elems ] if min(nplex) != max(nplex): print [ e.shape for e in model.elems ] warning("All parts should have same element type") return nodel = nplex[0] # Get materials matprops = PDB.getProp(kind='e',attr=['section']) # E, nu, thickness, rho mats = array([[mat.young_modulus, mat.poisson_ratio, mat.thickness, 0.0, # rho was not defined in material ] for mat in matprops]) matnr = zeros(nelems,dtype=int32) for i,mat in enumerate(matprops): # proces in same order as above! matnr[mat.set] = i+1 print matnr nmats = mats.shape[0] nloads = 0 # Header fil.write("""; calix data file generated by %s ; jobname=%s start: %s ;use cmdlog 'calix.log' ;use for messages cmdlog file open 'femodel.tmp' write da 1 yes cmdlog ;yes debug use for cmdlog output femodel elast stress 2 use for cmdlog cmdlog ;----------------------------------------- ; Aantal knopen: %s ; Aantal elementen: %s ; Aantal materialen: %s ; Aantal belastingsgevallen: %s """% (pf.Version,jobname,header,nnodes,nelems,nmats,nloads)) # Nodal coordinates fil.write(""";----------------------------------------- ; Knopen ;-------- nodes coord %s 1 """ % nnodes) fil.write('\n'.join(["%5i%10.2f%10.2f"%(i,x[0],x[1]) for i,x in zip(arange(nnodes)+1,model.coords)])) fil.write('\n\n') # Boundary conditions fil.write(""";----------------------------------------- ; Verhinderde verplaatsingen ;------------------------- bound bcon plane """) for p in PDB.getProp(kind='n',attr=['bound']): bnd = "%5i" + "%5i"*2 % (p.bound[0],p.bound[1]) if p.set is None: nod = arange(model.nnodes) else: nod = array(p.set) fil.write('\n'.join([ bnd % i for i in nod+1 ])) fil.write('\n') fil.write('\n') fil.write("""print bcon 3 $$$$$$$$$$$$$$$$$$$$$$$ $$ D O F S $$ $$$$$$$$$$$$$$$$$$$$$$$ """) # Materials fil.write(""";----------------------------------------- ; Materialen ;----------- array mat %s 4 """ % len(mats)) fil.write('\n'.join([ "%.4e "*4 % tuple(m) for m in mats])) fil.write('\n\n') fil.write("""print mat 3 $$$$$$$$$$$$$$$$$$$$$$$ $$ M A T E R I A L S $$ $$$$$$$$$$$$$$$$$$$$$$$ """) # Elements for igrp,grp in enumerate(model.elems): nelems,nplex = grp.shape fil.write(""";----------------------------------------- ; Elementen ;---------- elements elems-%s matnr-%s %s %s 1 """ % (igrp,igrp,nplex,nelems)) fil.write('\n'.join(["%5i"*(nplex+2) % tuple([i,1]+e.tolist()) for i,e in zip(arange(nelems)+1,grp+1)])) fil.write('\n\n') fil.write("""plane plane-%s coord bcon elems-%s matnr-%s 2 2 """ % (igrp,igrp,igrp)) ######################### # Nodal Loads cloads = [ p for p in PDB.getProp('n',attr=['cload']) ] fil.write("""text 3 1 $$$$$$$$$$$$$$$$$$$$ $$ NODAL LOADS $$ $$$$$$$$$$$$$$$$$$$$ loads f bcon 1 """) if len(cloads) > 0: loadcase=1 for p in cloads: if p.set is None: nodeset = range(calpyModel.nnodes) else: nodeset = p.set F = [0.0,0.0] for i,v in p.cload: if i in [0,1]: F[i] = v fil.write(''.join(["%5i%5i%10.2f%10.2f\n" % (n+1,loadcase,F[0],F[1]) for n in nodeset])) fil.write('\n') ######################### # Distributed loads eloads = [ p for p in PDB.getProp('e',attr=['eload']) ] if len(eloads) > 0: fil.write("""text 4 1 $$$$$$$$$$$$$$$$$$$$$$$$$$ $$ BOUNDARY ELEMENTS $$ $$$$$$$$$$$$$$$$$$$$$$$$$$ elem loadnr localnodes """) # get the data from database, group by group loadcase=1 for igrp in range(len(model.elems)): geloads = [ p for p in eloads if p.group==igrp ] neloads = len(geloads) loaddata = [] fil.write("array randen integer %s 4 0 1\n" % neloads) i = 1 for p in geloads: xload = yload = 0. if p.label == 'x': xload = p.value elif p.label == 'y': yload = p.value # Save the load data for later loaddata.append((i,loadcase,xload,yload)) # Because of the way we constructed the database, the set will # contain only one element, but let's loop over it anyway in case # one day we make the storage more effective for e in p.set: fil.write(("%5s"*4+"\n")%(e+1,i,p.edge+1,(p.edge+1)%4+1)) i += 1 fil.write("""print randen tran randen tranden boundary rand-%s coord bcon elems-%s matnr-%s tranden 1 """ % ((igrp,)*3)) fil.write("""text 3 1 $$$$$$$$$$$$$$$$$$$$$$$ $$ BOUNDARY LOADS $$ $$$$$$$$$$$$$$$$$$$$$$$ loadvec boundary rand-%s f 1 """ % igrp) for eload in loaddata: fil.write("%5s%5s%10s%10s\n" % eload) fil.write('\n') ######################### # Print total load vector fil.write(""" print f 3 $$$$$$$$$$$$$$$$$$$$ $$ LOAD VECTOR $$ $$$$$$$$$$$$$$$$$$$$ ; """) # Assemble for igrp in range(len(model.elems)): fil.write("assemble plane-%s mat s 0 0 0 %s\n" % (igrp,nzem)) # Solve and output fil.write(""";------------------------------------------------solve+output flavia mesh '%s.flavia.msh' %s flavia nodes coord """ % (jobname,nplex)) for igrp in range(len(model.elems)): fil.write("flavia elems elems-%s matnr-%s %s\n" % (igrp,igrp,nplex)) fil.write("flavia results '%s.flavia.res'\n" % jobname) fil.write(""" solbnd s f delete s text named 1 "Displacement" "Elastic Analysis" text typed 1 Vector OnNodes text names 1 "Stress" "Elastic Analysis" text types 1 Matrix OnNodes intvar set 1 1 loop 1 displ f bcon displ $1 1 tran displ disp flavia result named typed disp $1 """) for igrp in range(len(model.elems)): fil.write(""" stress plane-%s mat f stresg $1 1 gp2nod plane-%s stresg strese 0 1 nodavg plane-%s elems-%s strese stren nval 1 tran stren stre flavia result names types stre $1 """ % ((igrp,)*4)) fil.write(""" intvar add 1 1 next stop """) # Done: Close data file fil.close() showFile(filnam,mono=True) if ack("Shall I run the Calix analysis?"): # Run the analysis outfile = utils.changeExt(filnam,'res') cmd = "calix %s %s" % (filnam,outfile) utils.runCommand(cmd) showFile(outfile,mono=True) if ack("Shall I read the results for postprocessing?"): from plugins import flavia meshfile = utils.changeExt(filnam,'flavia.msh') resfile = utils.changeExt(filnam,'flavia.res') DB = flavia.readFlavia(meshfile,resfile) postproc_menu.setDB(DB) export({name:DB}) if showInfo("The results have been exported as %s\nYou can now use the postproc menu to display results" % name,actions=['Cancel','OK']) == 'OK': postproc_menu.selection.set(name) postproc_menu.selectDB(DB) postproc_menu.open_dialog() ############################################################################## def runCalpyAnalysis(jobname=None,verbose=False,flavia=False): """Create data for Calpy analysis module and run Calpy on the data. While we could write an analysis file in the Calpy format and then run the Calpy program on it (like we did with Abaqus), we can (and do) take another road here: Calpy has a Python/numpy interface, allowing us to directly present the numerical data in arrays to the analysis module. It is supposed that the Finite Element model has been created and exported under the name 'fe_model'. """ ############################ # Load the needed calpy modules from plugins import calpy_itf calpy_itf.check() ## # Load development version #import sys #sys.path.insert(0,'/home/bene/prj/calpy') #print sys.path import calpy reload(calpy) print calpy.__path__ calpy.options.optimize=True from calpy import femodel,fe_util,plane from calpy.arrayprint import aprint ############################ checkWorkdir() if model is None: warn() return if jobname is None: # ask job name from user res = askItems([('JobName',feresult_name.peek()),('Verbose Mode',False)]) if res: jobname = res['JobName'] verbose = res['Verbose Mode'] if not jobname: print "No Job Name: bailing out" return # OK, start calpy print "Starting the Calpy analysis module --- this might take some time" pf.app.processEvents() starttime = time.clock() calpyModel = femodel.FeModel(2,"elast","Plane_Stress") calpyModel.nnodes = model.coords.shape[0] calpyModel.nelems = model.celems[-1] print [ e.shape for e in model.elems ] nplex = [ e.shape[1] for e in model.elems ] if min(nplex) != max(nplex): warning("All parts should have same element type") return calpyModel.nnodel = nplex[0] # 2D model in calpy needs 2D coordinates coords = model.coords[:,:2] if verbose: fe_util.PrintNodes(coords) # Boundary conditions bcon = zeros((calpyModel.nnodes,2),dtype=int32) bcon[:,2:6] = 1 # leave only ux and uy for p in PDB.getProp(kind='n',attr=['bound']): bnd = where(p.bound)[0] if p.set is None: nod = arange(calpyModel.nnodes) else: nod = array(p.set) for i in bnd: bcon[p.set,i] = 1 fe_util.NumberEquations(bcon) if verbose: fe_util.PrintDofs(bcon,header=['ux','uy']) # The number of free DOFs remaining calpyModel.ndof = bcon.max() print "Number of DOF's: %s" % calpyModel.ndof # We extract the materials/sections from the property database matprops = PDB.getProp(kind='e',attr=['section']) # E, nu, thickness, rho mats = array([[mat.young_modulus, mat.poisson_ratio, mat.thickness, 0.0, # rho was not defined in material ] for mat in matprops]) calpyModel.nmats = mats.shape[0] if verbose: fe_util.PrintMats(mats,header=['E','nu','thick','rho']) ########### Find number of load cases ############ calpyModel.nloads = nloads calpyModel.PrintModelData() ngp = 2 nzem = 3 calpyModel.banded = True # Create element definitions: # In calpy, each element is represented by nnod + 1 integer numbers: # matnr, node1, node2, .... # Also notice that Calpy numbering starts at 1, not at 0 as customary # in pyFormex; therefore we add 1 to elems. matnr = zeros(calpyModel.nelems,dtype=int32) for i,mat in enumerate(matprops): # proces in same order as above! matnr[mat.set] = i+1 NodesGrp = [] MatnrGrp = [] PlaneGrp = [] for i,e in enumerate(model.elems): j,k = model.celems[i:i+2] Plane = plane.Quad("part-%s" % i,[ngp,ngp],calpyModel) Plane.debug = 0 PlaneGrp.append(Plane) NodesGrp.append(e+1) MatnrGrp.append(matnr[j:k]) if verbose: fe_util.PrintElements(NodesGrp[-1],MatnrGrp[-1]) #Plane.AddElements(e+1,matnr[j:k],mats,coords,bcon) for Plane,nodenrs,matnrs in zip(PlaneGrp,NodesGrp,MatnrGrp): Plane.AddElements(nodenrs,matnrs,mats,coords,bcon) # Create load vectors # Calpy allows for multiple load cases in a single analysis. # However, our script currently puts all loads together in a single # load case. So the processing hereafter is rather simple, especially # since Calpy provides a function to assemble a single concentrated # load into the load vector. We initialize the load vector to zeros # and then add all the concentrated loads from the properties database. # A single concentrated load consists of 6 components, corresponding # to the 6 DOFs of a node. # # AssembleVector takes 3 arguments: the global vector in which to # assemble a nodal vector (length ndof), the nodal vector values # (length 6), and a list of indices specifying the positions of the # nodal DOFs in the global vector. # Beware: The function does not change the global vector, but merely # returns the value after assembling. # Also notice that the indexing inside the bcon array uses numpy # convention (starting at 0), thus no adding 1 is needed! print "Assembling Concentrated Loads" f = zeros((calpyModel.ndof,calpyModel.nloads),float) for p in PDB.getProp('n',attr=['cload']): lc = 0 #lc = int(p.tag) if p.set is None: nodeset = range(calpyModel.nnodes) else: nodeset = p.set F = [0.0,0.0] for i,v in p.cload: if i in [0,1]: F[i] = v for n in nodeset: f[:,lc] = fe_util.AssembleVector(f[:,lc],F,bcon[n]) print "Assembling distributed loads" # This is a bit more complex. See Calpy for details # We first generate the input data in a string, then read them with the # calpy femodel.ReadBoundaryLoads function and finally assemble them with # plane.addBoundaryLoads. We have to do this operation per element group. # The group number is stored in the property record. ngroups = model.ngroups() s = [ "" ] * ngroups nb = [ 0 ] * ngroups lc = 1 for p in PDB.getProp('e',attr=['eload']): xload = yload = 0. if p.label == 'x': xload = p.value elif p.label == 'y': yload = p.value # Because of the way we constructed the database, the set will # contain only one element, but let's loop over it anyway in case # one day we make the storage more effective # Also, remember calpy numbers are +1 ! g = p.group print "Group %s" % g for e in p.set: s[g] += "%s %s %s %s %s\n" % (e+1,p.edge+1,lc,xload,yload) nb[g] += 1 #print s,nb for nbi,si,nodes,matnr,Plane in zip(nb,s,NodesGrp,MatnrGrp,PlaneGrp): if nbi > 0: idloads,dloads = fe_util.ReadBoundaryLoads(nbi,calpyModel.ndim,si) #print idloads,dloads Plane.AddBoundaryLoads(f,calpyModel,idloads,dloads,nodes,matnr,coords,bcon,mats) if verbose: print "Calpy.Loads" print f ############ Create global stiffness matrix ########## s = calpyModel.ZeroStiffnessMatrix(0) for elgrp in PlaneGrp: s = elgrp.Assemble(s,mats,calpyModel) # print "The complete stiffness matrix" # print s ############ Solve the system of equations ########## v = calpyModel.SolveSystem(s,f) print "Calpy analysis has finished --- Runtime was %s seconds." % (time.clock()-starttime) displ = fe_util.selectDisplacements (v,bcon) if verbose: print "Displacements",displ if flavia: flavia.WriteMeshFile(jobname,"Quadrilateral",model.nnodel,coord,nodes,matnr) res=flavia.ResultsFile(jobname) # compute stresses for lc in range(calpyModel.nloads): print "Results for load case %d" %(lc+1) print "Displacements" aprint(displ[:,:,lc],header=['x','y'],numbering=True) if flavia: flavia.WriteResultsHeader(res,'"Displacement" "Elastic Analysis"',lc+1,'Vector OnNodes') flavia.WriteResults(res,displ[:,:,lc]) stresn = count = None i = 0 for e,P in zip(model.elems,PlaneGrp): i += 1 #P.debug = 1 stresg = P.StressGP (v[:,lc],mats) if verbose: print "elem group %d" % i print "GP Stress\n", stresg strese = P.GP2Nodes(stresg) if verbose: print "Nodal Element Stress\n", strese #print "Nodes",e+1 stresn,count = P.NodalAcc(e+1,strese,nnod=calpyModel.nnodes,nodata=stresn,nodn=count) #print stresn,count #print stresn.shape #print count.shape #print "TOTAL",stresn,count stresn /= count.reshape(-1,1) #print "AVG",stresn if verbose: print "Averaged Nodal Stress\n" aprint(stresn,header=['sxx','syy','sxy'],numbering=True) if flavia: flavia.WriteResultsHeader(res,'"Stress" "Elastic Analysis"',lc+1,'Matrix OnNodes') flavia.WriteResults(res,stresn) DB = FeResult() DB.nodes = model.coords DB.nnodes = model.coords.shape[0] DB.nodid = arange(DB.nnodes) DB.elems = dict(enumerate(model.elems)) DB.nelems = model.celems[-1] DB.Finalize() DB.datasize['S'] = 3 #print DB.elems for lc in range(calpyModel.nloads): DB.Increment(lc,0) d = zeros((calpyModel.nnodes,3)) d[:,:2] = displ[:,:,lc] DB.R['U'] = d DB.R['S'] = stresn postproc_menu.setDB(DB) name = feresult_name.next() export({name:DB}) if showInfo("The results have been exported as %s\nYou can now use the postproc menu to display results" % name,actions=['Cancel','OK']) == 'OK': postproc_menu.selection.set(name) postproc_menu.selectDB(DB) postproc_menu.open_dialog() def autoRun(quadratic=False): clear() if quadratic: nx,ny = 2,2 else: nx,ny = 4,4 createRectPart(dict(x0=0.,x2=1.,y0=0.,y2=1.,nx=nx,ny=ny,eltype='quad')) createRectPart(dict(x0=0.,x2=-1.,y0=0.,y2=1.,nx=nx,ny=ny,eltype='quad')) if quadratic: convertQuadratic() createModel() nodenrs = arange(model.coords.shape[0]) PDB.elemProp(eltype='CPS4',section=ElemSection(section=section)) if quadratic: ny *= 2 PDB.nodeProp(set=nodenrs[:ny+1],bound=[1,1,0,0,0,0]) PDB.nodeProp(set=nodenrs[-(ny+1):],cload=[10.,0.,0.,0.,0.,0.]) runCalpyAnalysis('FeEx',verbose=True) def autoRun2(): clear() nx,ny = 1,1 createRectPart(dict(x0=0.,x2=1.,y0=0.,y2=1.,nx=nx,ny=ny,eltype='quad')) convertQuadratic() createModel() nodenrs = arange(model.coords.shape[0]) xmin,xmax = model.coords.bbox()[:,0] xtol = (xmax-xmin) / 1000. left = model.coords.test(dir=0,min=xmin-xtol,max=xmin+xtol) right = model.coords.test(dir=0,min=xmax-xtol,max=xmax+xtol) leftnrs = where(left)[0] rightnrs = where(right)[0] print leftnrs print rightnrs PDB.elemProp(eltype='CPS4',section=ElemSection(section=section)) ny *= 2 PDB.nodeProp(set=leftnrs,bound=[1,1,0,0,0,0]) PDB.nodeProp(set=rightnrs,cload=[10.,0.,0.,0.,0.,0.]) print "This example is incomplete." print PDB #runCalpyAnalysis('FeEx',verbose=True) def autoConv(): clear() res = askItems([('nx',1),('ny',1)]) nx = res['nx'] ny = res['ny'] createRectPart(dict(x0=0.,x1=10.,y0=0.,y1=1.,nx=nx,ny=ny,eltype='quad')) createModel() nodenrs = arange(model.coords.shape[0]) PDB.elemProp(eltype='CPS4',section=ElemSection(section=section)) PDB.nodeProp(set=nodenrs[:ny+1],bound=[1,1,0,0,0,0]) PDB.nodeProp(set=nodenrs[-(ny+1):],cload=[0.,1./(ny+1),0.,0.,0.,0.]) runCalpyAnalysis('FeEx',verbose=True) def importAll(): globals().update(pf.PF) def exportAll(): pf.PF.update(globals()) ############################################################################# ######### Create a menu with interactive tasks ############# def create_menu(): """Create the FeEx menu.""" MenuData = [ ("&Delete All",deleteAll), ("&Create Rectangular Part",createRectPart), ("&Create QuadrilateralPart",createQuadPart), ("&Convert to Quadratic-8",convertQuadratic), ("&Convert to Quadratic-9",convertQuadratic9), ("&Show All",drawParts), ("---",None), ("&Merge Parts into Model",createModel), ("&Show Merged Model",drawModel), ("&Show Calpy Numbers",drawCalpy), ("&Print model",printModel), ("---",None), ("&Add material properties",setMaterial), ("&Add boundary conditions",setBoundary), ("&Add concentrated loads",setCLoad), ("&Add edge loads",setELoad), ("&Delete all material properties",deleteAllMats), ("&Delete all boundary conditions",deleteAllBcons), ("&Delete all concentrated loads",deleteAllCLoads), ("&Delete all edge loads",deleteAllELoads), ("&Print property database",printDB), ("---",None), ("&Create Abaqus input file",createAbaqusInput), ("&Create Calix input file",createCalixInput), ("&Run Calpy analysis",runCalpyAnalysis), ("---",None), ("&Import all",importAll), ("&Export all",exportAll), ("&Autorun example",autoRun), ("&Autorun quadratic example",autoRun2), ("&Autoconv example",autoConv), ("---",None), ("&Close Menu",close_menu), ] return menu.Menu('FeEx',items=MenuData,parent=pf.GUI.menu,before='help') def show_menu(): """Show the menu.""" if not pf.GUI.menu.item('FeEx'): create_menu() def close_menu(): """Close the menu.""" m = pf.GUI.menu.item('FeEx') if m : m.remove() def reload_menu(): """Reload the menu.""" close_menu() show_menu() #################################################################### ######### What to do when the script is executed ################### if __name__ == "draw": geometry_menu.show_menu() resetData() reset() reload_menu() # End pyformex-0.8.6/pyformex/examples/Hypar.py0000644000211500021150000000276511705103030020356 0ustar benebene00000000000000# $Id: Hyparcap.py 1715 2010-12-05 17:03:55Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """Hypar level = 'beginner' topics = ['geometry'] techniques = ['color'] """ clear() smoothwire() layout(2) viewport(0) view('front') F = Formex([[[1.0,0.0,0.0],[0.0,-1.0,1.0],[-1.0,0.0,0.0],[0.0,1.0,1.0]]]) draw(F,color=red,bkcolor=blue) viewport(1) view('front') F.eltype = 'quad4' clear() draw(F,color=red,bkcolor=blue) pyformex-0.8.6/pyformex/examples/Elements.py0000644000211500021150000000724211705104656021061 0ustar benebene00000000000000# $Id: Elements.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Elements level = 'normal' topics = ['geometry','mesh'] techniques = ['dialog','elements'] """ from elements import * from mesh import Mesh from gui.widgets import simpleInputItem as I import utils import olist colors = [black,blue,yellow,red] def showElement(eltype,options): clear() drawText("Element type: %s" %eltype,100,200,font='times',size=18,color=black) el = elementType(eltype) #print el.report() M = el.toMesh() ndim = el.ndim if ndim == 3: view('iso') smooth() else: view('front') if options['Force dimensionality']: flatwire() else: smoothwire() #print options['Deformed'] if options['Deformed']: M.coords = M.coords.addNoise(rsize=0.1) if options['Force dimensionality']: if ndim < 3: M.coords[...,2] = 0.0 if ndim < 2: M.coords[...,1] = 0.0 i = 'xyz'.find(options['Mirrored']) if i>=0: M = M.trl(i,0.2) M = M + M.reflect(i) M.setProp([5,6]) if options['Draw as'] == 'Formex': M = M.toFormex() elif options['Draw as'] == 'Border': M = M.getBorderMesh() draw(M.coords,color=None,wait=False) drawNumbers(M.coords,color=None) if options['Color setting'] == 'prop': draw(M) else: draw(M,color=red,bkcolor=blue) if __name__ == "draw": ElemList = [] for ndim in [0,1,2,3]: ElemList += elementTypes(ndim) res = { 'Deformed':True, 'Mirrored':'No', 'Draw as':'Mesh', 'Color setting':'direct', 'Force dimensionality':False, } res.update(pf.PF.get('Elements_data',{})) #print res res = askItems( store=res, items=[ I('Element Type',choices=['All',]+ElemList), I('Deformed',itemtype='bool'), I('Mirrored',itemtype='radio',choices=['No','x','y','z']), I('Draw as',itemtype='radio',choices=['Mesh','Formex','Border']), I('Color setting',itemtype='radio',choices=['direct','prop']), I('Force dimensionality',itemtype='bool'), ]) if not res: exit() #print "RESULT",res pf.PF['Elements_data'] = res eltype = res['Element Type'] if eltype == 'All': ellist = ElemList else: ellist = [eltype] clear() delay(1) for el in ellist: showElement(el,res) # End pyformex-0.8.6/pyformex/examples/SpaceTrussRoof_calpy.py0000644000211500021150000002274011705104656023417 0ustar benebene00000000000000# $Id: SpaceTrussRoof_calpy.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Double Layer Flat Space Truss Roof level = 'advanced' topics = ['FEA'] techniques = ['dialog', 'animation', 'persistence', 'color'] """ from mesh import Mesh from plugins.properties import * ############################ # Load the needed calpy modules from plugins import calpy_itf calpy_itf.check() import calpy calpy.options.optimize = False from calpy.fe_util import * from calpy.truss3d import * ############################ if not checkWorkdir(): exit() import time #### #Data ################### dx = 1800 # Modular size [mm] ht = 1500 # Deck height [mm] nx = 8 # number of bottom deck modules in x direction ny = 6 # number of bottom deck modules in y direction q = -0.005 #distributed load [N/mm^2] ############# #Creating the model ################### top = (Formex("1").replic2(nx-1,ny,1,1) + Formex("2").replic2(nx,ny-1,1,1)).scale(dx) top.setProp(3) bottom = (Formex("1").replic2(nx,ny+1,1,1) + Formex("2").replic2(nx+1,ny,1,1)).scale(dx).translate([-dx/2,-dx/2,-ht]) bottom.setProp(0) T0 = Formex(4*[[[0,0,0]]]) # 4 times the corner of the top deck T4 = bottom.select([0,1,nx,nx+1]) # 4 nodes of corner module of bottom deck dia = connect([T0,T4]).replic2(nx,ny,dx,dx) dia.setProp(1) F = (top+bottom+dia) # Show upright createView('myview1',(0.,-90.,0.)) clear();linewidth(1);draw(F,view='myview1') ############ #Creating FE-model ################### mesh = F.toMesh() ############### #Creating elemsets ################### # Remember: elements in mesh are in the same order as elements in F topbar = where(F.prop==3)[0] bottombar = where(F.prop==0)[0] diabar = where(F.prop==1)[0] ############### #Creating nodesets ################### nnod = mesh.nnodes() nlist = arange(nnod) count = zeros(nnod) for n in mesh.elems.flat: count[n] += 1 field = nlist[count==8] topedge = nlist[count==7] topcorner = nlist[count==6] bottomedge = nlist[count==5] bottomcorner = nlist[count==3] edge = concatenate([topedge,topcorner]) support = concatenate([bottomedge,bottomcorner]) ######################## #Defining and assigning the properties ############################# Q = 0.5*q*dx*dx P = PropertyDB() P.nodeProp(field,cload = [0,0,Q,0,0,0]) P.nodeProp(edge,cload = [0,0,Q/2,0,0,0]) P.nodeProp(support,bound = [1,1,1,0,0,0]) circ20 = ElemSection(section={'name':'circ20','sectiontype':'Circ','radius':10, 'cross_section':314.159}, material={'name':'S500', 'young_modulus':210000, 'shear_modulus':81000, 'poisson_ratio':0.3, 'yield_stress' : 500,'density':0.000007850}) props = [ \ P.elemProp(topbar,section=circ20,eltype='T3D2'), \ P.elemProp(bottombar,section=circ20,eltype='T3D2'), \ P.elemProp(diabar,section=circ20,eltype='T3D2'), \ ] # Since all elems have same characteristics, we could just have used: # P.elemProp(section=circ20,elemtype='T3D2') # But putting the elems in three sets allows for separate postprocessing ######### #calpy analysis ################### # boundary conditions bcon = zeros([nnod,3],dtype=int) bcon[support] = [ 1,1,1 ] NumberEquations(bcon) #materials mats = array([ [p.young_modulus,p.density,p.cross_section] for p in props]) matnr = zeros_like(F.prop) for i,p in enumerate(props): matnr[p.set] = i+1 matnod = concatenate([matnr.reshape((-1,1)),mesh.elems+1],axis=-1) ndof = bcon.max() # loads nlc=1 loads=zeros((ndof,nlc),Float) for n in field: loads[:,0] = AssembleVector(loads[:,0],[ 0.0, 0.0, Q ],bcon[n,:]) for n in edge: loads[:,0] = AssembleVector(loads[:,0],[ 0.0, 0.0, Q/2 ],bcon[n,:]) message("Performing analysis: this may take some time") # Find a candidate for the output file fullname = os.path.splitext(__file__)[0] + '.out' basename = os.path.basename(fullname) dirname = os.path.dirname(fullname) outfilename = None for candidate in [dirname,pf.cfg['workdir'],'/var/tmp']: if isWritable(candidate): fullname = os.path.join(candidate,basename) if not os.path.exists(fullname) or isWritable(fullname): outfilename = fullname break if outfilename is None: error("No writeable path: I can not execute the simulation.\nCopy the script to a writeable path and try running from there.") exit() outfile = open(outfilename,'w') message("Output is written to file '%s'" % os.path.realpath(outfilename)) stdout_saved = sys.stdout sys.stdout = outfile print "# File created by pyFormex on %s" % time.ctime() print "# Script name: %s" % pf.scriptName displ,frc = static(mesh.coords,bcon,mats,matnod,loads,Echo=True) print "# Analysis finished on %s" % time.ctime() sys.stdout = stdout_saved outfile.close() ################################ #Using pyFormex as postprocessor ################################ if pf.options.gui: from plugins.postproc import niceNumber,frameScale from gui.colorscale import * import gui.decors def showOutput(): #showText(file(outfilename).read()) showFile(outfilename) def showForces(): # Give the mesh some meaningful colors. # The frc array returns element forces and has shape # (nelems,nforcevalues,nloadcases) # In this case there is only one resultant force per element (the # normal force), and only load case; we still need to select the # scalar element result values from the array into a onedimensional # vector val. val = frc[:,0,0] # create a colorscale CS = ColorScale([blue,yellow,red],val.min(),val.max(),0.,2.,2.) cval = array(map(CS.color,val)) #aprint(cval,header=['Red','Green','Blue']) clear() linewidth(3) draw(mesh,color=cval) drawText('Normal force in the truss members',300,50,size=24) CL = ColorLegend(CS,100) CLA = decors.ColorLegend(CL,10,10,30,200,size=24) decorate(CLA) # Show a deformed plot def deformed_plot(dscale=100.): """Shows a deformed plot with deformations scaled with a factor scale.""" # deformed structure dnodes = mesh.coords + dscale * displ[:,:,0] deformed = Mesh(dnodes,mesh.elems,mesh.prop) FA = draw(deformed,bbox='last',view=None,wait=False) TA = drawText('Deformed geometry (scale %.2f)' % dscale,300,50,size=24) return FA,TA def animate_deformed_plot(amplitude,sleeptime=1,count=1): """Shows an animation of the deformation plot using nframes.""" FA = TA = None clear() while count > 0: count -= 1 for s in amplitude: F,T = deformed_plot(s) if FA: pf.canvas.removeActor(FA) if TA: pf.canvas.removeDecoration(TA) TA,FA = T,F sleep(sleeptime) def getOptimscale(): """Determine an optimal scale for displaying the deformation""" siz0 = F.sizes() dF = Formex(displ[:,:,0][mesh.elems]) #clear(); draw(dF,color=black) siz1 = dF.sizes() return niceNumber(1./(siz1/siz0).max()) def showDeformation(): clear() linewidth(1) draw(F,color=black) linewidth(3) deformed_plot(optimscale) view('last',True) def showAnimatedDeformation(): # Show animated deformation scale = optimscale nframes = 10 form = 'revert' duration = 5./nframes ncycles = 2 items = [ ['scale',scale], ['nframes',nframes], ['form',form], ['duration',duration], ['ncycles',ncycles] ] res = askItems(items,'Animation Parameters') if res: scale = float(res['scale']) nframes = int(res['nframes']) duration = float(res['duration']) ncycles = int(res['ncycles']) form = res['form'] if form in [ 'up', 'updown', 'revert' ]: amp = scale * frameScale(nframes,form) animate_deformed_plot(amp,duration,ncycles) optimscale = getOptimscale() options = ['None','Output File','Member forces','Deformation','Animated deformation'] functions = [None,showOutput,showForces,showDeformation,showAnimatedDeformation] while True: ans = ask("Which results do you want to see?",options) ind = options.index(ans) if ind <= 0: break functions[ind]() if widgets.input_timeout > 0: #timeout break # End pyformex-0.8.6/pyformex/examples/SweepBeam.py0000644000211500021150000000746711705104656021166 0ustar benebene00000000000000# $Id: SweepBeam.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """H-beam level = 'normal' topics = ['geometry','surface'] techniques = ['color'] """ from plugins import curve #from mesh import Mesh import simple # GEOMETRICAL PARAMETERS FOR HE200B wide flange beam h = 200. #beam height b = 200. #flange width tf = 15. #flange thickness tw = 9. #body thickness l = 400. #beam length r = 18. #filling radius # MESH PARAMETERS el = 20 #number of elements along the length etb = 2 #number of elements over half of the thickness of the body ehb = 5 #number of elements over half of the height of the body etf = 5 #number of elements over the thickness of the flange ewf = 8 #number of elements over half of the width of the flange er = 6 #number of elements in the circular segment Body = simple.rectangle(etb,ehb,tw/2.,h/2.-tf-r) Flange1 = simple.rectangle(er/2,etf-etb,tw/2.+r,tf-tw/2.).translate([0.,h/2.-(tf-tw/2.),0.]) Flange2 = simple.rectangle(ewf,etf-etb,b/2.-r-tw/2.,tf-tw/2.).translate([tw/2.+r,h/2.-(tf-tw/2.),0.]) Flange3 = simple.rectangle(ewf,etb,b/2.-r-tw/2.,tw/2.).translate([tw/2.+r,h/2.-tf,0.]) c1a = simple.line([0,h/2-tf-r,0],[0,h/2-tf+tw/2,0],er/2) c1b = simple.line([0,h/2-tf+tw/2,0],[tw/2+r,h/2-tf+tw/2,0],er/2) c1 = c1a + c1b c2 = simple.circle(90./er,0.,90.).reflect(0).scale(r).translate([tw/2+r,h/2-tf-r,0]) Filled = simple.connectCurves(c2,c1,etb) Quarter = Body + Filled + Flange1 + Flange2 + Flange3 Half = Quarter + Quarter.reflect(1).reverse() Full = Half + Half.reflect(0).reverse() Section = Full.toMesh() clear() draw(Section,color=red) #exit() #pause() method = ask("Choose extrude method:",['Cancel','Sweep','Connect','Extrude','ExtrudeQuadratic','Revolve','RevolveLoop']) import timer t = timer.Timer() if method == 'Sweep': L = simple.line([0,0,0],[0,0,l],el) x = concatenate([L.coords[:,0],L.coords[-1:,1]]) path = curve.PolyLine(x) Beam = Section.sweep(path,normal=[0.,0.,1.],upvector=[0.,1.,0.]) elif method == 'Connect': Section1 = Section.trl([0,0,l]) Beam = Section.connect(Section1,el) elif method == 'Extrude': Beam = Section.extrude(el,step=l/el,dir=2) elif method == 'ExtrudeQuadratic': Section = Section.convert('quad9') Beam = Section.extrude(el,step=l/el,dir=2,degree=2) elif method == 'Revolve': Beam = Section.revolve(el,axis=1,angle=60.,around=[-l,0.,0.]) elif method == 'RevolveLoop': Beam = Section.revolve(el,axis=1,angle=240.,around=[-l,0.,0.],loop=True) else: exit() print "Computing: %s seconds" % t.seconds() #print Beam.prop #print Beam.elems.shape t.reset() clear() #draw(Beam,color='red',linewidth=2) draw(Beam.getBorderMesh(),color='red',linewidth=2) print "Drawing: %s seconds" % t.seconds() export({'Beam':Beam}) # End pyformex-0.8.6/pyformex/examples/CircumCircle.py0000644000211500021150000000435611705104656021654 0ustar benebene00000000000000# $Id: CircumCircle.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """CircumCircle level = 'beginner' topics = ['geometry'] techniques = ['function','import','dialog','viewport'] """ import simple from examples.Cube import cube_tri from geomtools import * def draw_circles(circles,color=red): for r,c,n in circles: C = simple.circle(r=r,n=n,c=c) draw(C,color=color) def drawCircles(F,func,color=red): r,c,n = func(F.coords) draw(c,color=color) draw_circles(zip(r,c,n),color=color) layout(2) wireframe() # draw in viewport 0 viewport(0) view('front') clear() rtri = Formex('3:016932').scale([1.5,1,0]) F = rtri + rtri.shear(0,1,-0.5).trl(0,-4.0) + rtri.shear(0,1,0.75).trl(0,3.0) draw(F) drawCircles(F,triangleCircumCircle,color=red) zoomAll() drawCircles(F,triangleInCircle,color=blue) drawCircles(F,triangleBoundingCircle,color=black) zoomAll() # draw in viewport 1 viewport(1) view('iso') clear() F,c = cube_tri() draw(F) drawCircles(F,triangleInCircle) zoomAll() if not ack("Keep both viewports ?"): print "Removing a viewport" # remove last viewport removeViewport() # End pyformex-0.8.6/pyformex/examples/Helix.py0000644000211500021150000000346611705104656020362 0ustar benebene00000000000000# $Id: Helix.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Helix level = 'beginner' topics = ['geometry'] techniques = ['color'] This is basically the same example as Torus.py, but it shows all steps. """ m = 36 # number of cells along helix n = 10 # number of cells along circular cross section reset() setDrawOptions({'clear':True}) bgcolor(white) F = Formex('l:164',[1,2,3]); draw(F) F = F.replic(m,1.,0); draw(F) F = F.replic(n,1.,1); draw(F) F = F.translate(2,1.); draw(F,view='iso') F = F.cylindrical([2,1,0],[1.,360./n,1.]); draw(F) F = F.replic(5,m*1.,2); draw(F) F = F.rotate(-10.,0); draw(F) F = F.translate(0,5.); draw(F) F = F.cylindrical([0,2,1],[1.,360./m,1.]); draw(F) draw(F,view='right') pyformex-0.8.6/pyformex/examples/InputDialog.py0000644000211500021150000001545311705104656021527 0ustar benebene00000000000000# $Id: InputDialog.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """InputDialog Example showing the use of the InputDialog level = 'normal' topics = [] techniques = ['dialog'] """ # BV # not working correctly: # - itemtype = 'label' # - bounds on 'float' # - fslider has no slider # - tooltip on group/tab # - push does not work correctly input_text = [ dict(name='label',value=None,text="A constant info field",itemtype='info',tooltip="itemtype='info' with a value=None can be used to show a label only. Note that this does not work with the _I() function."), _I('info','A constant info field',text="itemtype 'info'",itemtype='info',tooltip="This is an informational field that can not be changed"), _I('string','A string input field',text="itemtype str",itemtype='string',tooltip="This is a single line string input field"), _I('text','A multiline text input field',text="itemtype 'text'",itemtype='text',tooltip="This is a multiline text input field"), ] input_select = [ _I('bool',False,text="itemtype bool",stretch='ba',tooltip="This is a boolean field that can only get the values True or False, by checking or unchecking the box"), _I('list',['First','Third'],text="itemtype 'list'",itemtype='list',choices=['First','Second','Third','Fourth'],tooltip="This is a an input field allowing you to select one or more of a set of predefined values"), _I('slist',['First','Third'],text="itemtype 'list' with single=True",itemtype='list',choices=['First','Second','Third','Fourth'],single=True,tooltip="This is like 'list', bu allowing only a single value to be selected"), _I('clist',['First','Third'],text="itemtype 'list' with check=True",itemtype='list',choices=['First','Second','Third','Fourth'],check=True,tooltip="This is a an input field allowing you to select one of a set of predefined values"), _I('select','Third',text="itemtype 'select'",choices=['First','Second','Third','Fourth'],tooltip="This is a an input field allowing you to select one of a set of predefined values"), _I('radio','Third',text="itemtype (h)radio",itemtype='radio',choices=['First','Second','Third','Fourth'],tooltip="Like 'select', this allows selecting one of a set of predefined values"), _I('vradio','Third',text="itemtype vradio",itemtype='vradio',choices=['First','Second','Third','Fourth'],tooltip="Like 'radio', but items are placed vertically"), # _I('push','Third',text="itemtype (h)push",itemtype='push',choices=['First','Second','Third','Fourth'],tooltip="Yet another method to select one of a set of predefined values"), # _I('vpush','Third',text="itemtype vpush",itemtype='vpush',choices=['First','Second','Third','Fourth'],tooltip="Like 'push', but items are placed vertically"), ] input_numerical = [ _I('integer',37,text="any int",tooltip="An integer input field"), _I('bounded',3,text="a bounded integer (0..10)",min=0,max=10,tooltip="A bounded integer input field. This value is e.g. bounded to the interval [0,10]"), _I('float',37.,text="any float",tooltip="A float input field"), _I('boundedf',23.7,text="a bounded float",min=23.5,max=23.9,tooltip="A bounded float input field. This value is e.g. bounded to the interval [23.5,23.9]"), _I('slider',3,text="a integer slider",min=0,max=10,itemtype='slider',tooltip="An integer input field accompanied by a slider to set the value."), _I('fslider',23.7,text="a float slider",min=23.5,max=23.9,itemtype='fslider',tooltip="A float input field accompanied by a slider to set the value."), _I('ivector',[640,400],text="an integer vector",tooltip="An integer vector input field"), ] input_special = [ _I('color',colors.pyformex_pink,itemtype='color',text="Color",tooltip="An inputfield allowing to select a color. The current color is pyFormex pink."), _I('font','',itemtype='font'), _I('point',[0.,0.,0.],itemtype='point'), ] input_tabgroup = [ _I('enable1',False,text="Enable group 1"), _G('group1',input_text,text="Text input group"), _I('enable2',False,text="Enable group 2"), _G('group2',input_select,text="Select input group"), _G('group3',input_special,text="Special input group",checkable=True,checked=True), ] input_data = [ _T('Text',input_text), _T('Selection',input_select), _T('Numerical',input_numerical), _T('Special',input_special), _T('tabgroup',input_tabgroup,text="Tabs and Groups"), ] input_enablers = [ ('tabgroup/enable1',True,'tabgroup/group1','tabgroup/group2/radio'), ('tabgroup/enable2',True,'tabgroup/group2','tabgroup/group3','tabgroup/group1/info'), ] def show(): """Accept the data and show the results""" from utils import formatDict dialog.acceptData() res = dialog.results print formatDict(res) def close(): global dialog pf.PF['ColorScale_data'] = dialog.results if dialog: dialog.close() dialog = None # Release scriptlock scriptRelease(__file__) def timeOut(): """What to do on a InputDialog timeout event. As a policy, all pyFormex examples should behave well on a dialog timeout. Most users can simply ignore this. """ show() wait() close() if __name__ == 'draw': # Update the data items from saved values try: saved_data = named('InputDialog_data') widgets.updateDialogItems(input_data,save_data) except: pass # Create the modeless dialog widget dialog = widgets.InputDialog(input_data,enablers=input_enablers,autoprefix=True,caption='InputDialog',actions = [('Close',close),('Show',show)],default='Show') # Examples style requires a timeout action dialog.timeout = timeOut # Show the dialog and let the user have fun dialog.show() # Block other scripts scriptLock(__file__) # End pyformex-0.8.6/pyformex/examples/scripts.cat0000644000211500021150000002206611700367640021113 0ustar benebene00000000000000kat = ['level', 'topics', 'techniques', 'all'] cat = {'topics': ['', 'FEA', 'curve', 'cylinder', 'domes', 'drawing', 'editing', 'formex', 'frame', 'game', 'geometry', 'illustration', 'image', 'mesh', 'postprocess', 'section2d', 'structure', 'surface'], 'techniques': ['Hex8', 'animation', 'axes', 'border', 'bump', 'calpy', 'color', 'connect', 'cut', 'degenerate', 'dialog', 'draw', 'elements', 'external', 'extrude', 'filename', 'function', 'globals', 'image', 'import', 'interactive', 'interpolate', 'intersection', 'isopar', 'lima', 'match', 'menu', 'nurbs', 'pattern', 'pause', 'persistence', 'position', 'programming', 'projection', 'random', 'replicate', 'reverse', 'revolve', 'solve', 'spherical', 'spline', 'sweep', 'symmetry', 'text', 'transform', 'viewport', 'widgets'], 'all': [], 'level': ['beginner', 'normal', 'advanced']} col = {'techniques/reverse': ['Cube'], 'all': ['BarrelVault', 'BarrelVault1', 'Baumkuchen', 'BeamFreq', 'Bezier', 'BezierCurve', 'BezierSpline', 'Carpetry', 'Casteljou', 'Circle', 'CircumCircle', 'Clip', 'Clock', 'ColorImage', 'ColorScale', 'Colored', 'ColoredText', 'Cone', 'Cones', 'Connect', 'ConnectMesh', 'Cube', 'Curves', 'Cylinder', 'DataInterpolation', 'Diamatic', 'DoubleLayer', 'Edit', 'Elements', 'Extrude', 'ExtrudeMesh', 'FeAbq', 'FeEx', 'Flare', 'FontForge', 'Formex', 'Geodesic', 'Grid', 'Helix', 'Hesperia', 'HorseSlice', 'HorseTorse', 'Hypar', 'Hyparcap', 'Inertia', 'InputDialog', 'Interpolate', 'Isopar', 'KochLine', 'Lamella', 'Light', 'Lima', 'Lustrum', 'Mesh', 'MeshMatch', 'MeshSmoothing', 'Mobius', 'Multicut', 'Novation', 'NurbsCircle', 'NurbsCurve', 'NurbsSurface', 'OpticalIllusions', 'ParabolicTower', 'Pattern', 'Position', 'Projection', 'Props', 'Random', 'RotoTranslation', 'Rubik', 'ScallopDome', 'Schwedler', 'Section2D', 'Shapes', 'Slice', 'SpaceTrussRoof', 'SpaceTrussRoof_abq', 'SpaceTrussRoof_calpy', 'Sphere', 'Sphere2', 'Sphere_stl', 'Spirals', 'SplineSurface', 'Stars', 'SuperShape', 'SurfaceProjection', 'Sweep', 'SweepBeam', 'TestDraw', 'TextGravity', 'Tori', 'Torus', 'TrussBridge', 'TrussFrame', 'Viewports', 'WedgeHex', 'Widgets', 'WireStent', 'WireStent_calpy', 'World', 'X_truss', 'X_truss_calpy', 'architect'], 'topics/surface': ['Carpetry', 'Colored', 'Cone', 'Connect', 'Cube', 'Cylinder', 'Hesperia', 'HorseSlice', 'HorseTorse', 'Mobius', 'Multicut', 'Novation', 'NurbsSurface', 'Projection', 'Random', 'Slice', 'Sphere', 'Sphere2', 'Sphere_stl', 'SplineSurface', 'SurfaceProjection', 'SweepBeam', 'Viewports'], 'techniques/isopar': ['Isopar'], 'topics/': ['architect'], 'techniques/color': ['Baumkuchen', 'Bezier', 'Carpetry', 'Clip', 'ColorImage', 'ColorScale', 'Colored', 'ColoredText', 'Cone', 'Connect', 'ConnectMesh', 'Cube', 'DataInterpolation', 'Diamatic', 'DoubleLayer', 'FeAbq', 'FeEx', 'Geodesic', 'Helix', 'Hesperia', 'HorseTorse', 'Hypar', 'Hyparcap', 'Inertia', 'Interpolate', 'Isopar', 'KochLine', 'Lamella', 'Light', 'Lustrum', 'Mobius', 'Novation', 'ParabolicTower', 'Pattern', 'Props', 'Random', 'Rubik', 'ScallopDome', 'Schwedler', 'Slice', 'SpaceTrussRoof', 'SpaceTrussRoof_abq', 'SpaceTrussRoof_calpy', 'Sphere', 'Sphere2', 'Sphere_stl', 'Stars', 'SweepBeam', 'TestDraw', 'Torus', 'TrussBridge', 'TrussFrame', 'Viewports', 'WireStent', 'WireStent_calpy', 'World', 'X_truss', 'X_truss_calpy'], 'techniques/dialog': ['BarrelVault', 'BarrelVault1', 'BezierSpline', 'Circle', 'CircumCircle', 'ColorScale', 'Cone', 'Cones', 'Elements', 'FeAbq', 'FeEx', 'Flare', 'Geodesic', 'Grid', 'Hesperia', 'InputDialog', 'Isopar', 'Light', 'Lima', 'Mobius', 'Novation', 'OpticalIllusions', 'Rubik', 'ScallopDome', 'SpaceTrussRoof', 'SpaceTrussRoof_calpy', 'Sphere', 'Sphere_stl', 'SuperShape', 'SurfaceProjection', 'TestDraw', 'WireStent'], 'techniques/persistence': ['Curves', 'Edit', 'FeAbq', 'FeEx', 'Hesperia', 'Light', 'Lustrum', 'Novation', 'OpticalIllusions', 'Rubik', 'SpaceTrussRoof_calpy', 'Sphere_stl', 'SuperShape', 'WireStent', 'X_truss_calpy'], 'techniques/viewport': ['BeamFreq', 'CircumCircle', 'Props', 'Viewports'], 'techniques/replicate': ['MeshMatch'], 'techniques/draw': ['Formex', 'MeshMatch', 'OpticalIllusions', 'Rubik'], 'topics/frame': ['BarrelVault', 'BarrelVault1'], 'topics/FEA': ['BeamFreq', 'ColorScale', 'FeAbq', 'FeEx', 'Hesperia', 'SpaceTrussRoof_abq', 'SpaceTrussRoof_calpy', 'WireStent_calpy', 'X_truss_calpy'], 'techniques/widgets': ['Curves', 'Slice', 'TestDraw', 'Tori'], 'topics/formex': ['Connect', 'Extrude'], 'level/normal': ['BeamFreq', 'BezierCurve', 'BezierSpline', 'Carpetry', 'Casteljou', 'Circle', 'ColorImage', 'ColorScale', 'Cones', 'Connect', 'ConnectMesh', 'Cube', 'Curves', 'Elements', 'InputDialog', 'Isopar', 'Lima', 'Lustrum', 'MeshMatch', 'MeshSmoothing', 'Novation', 'OpticalIllusions', 'Projection', 'Rubik', 'ScallopDome', 'Schwedler', 'Section2D', 'SpaceTrussRoof', 'Sphere', 'Sphere2', 'Sphere_stl', 'Spirals', 'SurfaceProjection', 'Sweep', 'SweepBeam', 'TestDraw', 'Tori', 'TrussBridge', 'TrussFrame', 'WedgeHex', 'WireStent', 'World', 'X_truss', 'architect'], 'topics/image': ['ColorImage', 'World'], 'topics/section2d': ['Section2D'], 'techniques/interpolate': ['SpaceTrussRoof'], 'techniques/spherical': ['Sphere_stl'], 'techniques/pause': ['Mesh'], 'topics/editing': ['Edit'], 'techniques/projection': ['Projection', 'SurfaceProjection'], 'techniques/random': ['Carpetry', 'OpticalIllusions', 'Rubik', 'TestDraw'], 'techniques/position': ['Position'], 'techniques/intersection': ['HorseSlice'], 'techniques/sweep': ['Sweep'], 'techniques/text': ['ColoredText', 'TextGravity'], 'topics/mesh': ['Carpetry', 'ConnectMesh', 'DataInterpolation', 'Elements', 'ExtrudeMesh', 'HorseSlice', 'Mesh', 'MeshMatch', 'MeshSmoothing', 'Sweep', 'TestDraw', 'WedgeHex'], 'techniques/animation': ['Circle', 'HorseTorse', 'Mobius', 'SpaceTrussRoof', 'SpaceTrussRoof_calpy'], 'techniques/globals': ['Tori'], 'techniques/degenerate': ['WedgeHex'], 'techniques/calpy': ['DataInterpolation'], 'level/beginner': ['BarrelVault', 'BarrelVault1', 'Baumkuchen', 'Bezier', 'CircumCircle', 'Clip', 'Colored', 'ColoredText', 'Cone', 'Cylinder', 'Diamatic', 'DoubleLayer', 'Extrude', 'ExtrudeMesh', 'Flare', 'Formex', 'Geodesic', 'Grid', 'Helix', 'Hypar', 'Hyparcap', 'Inertia', 'Interpolate', 'KochLine', 'Lamella', 'Light', 'Mesh', 'Multicut', 'ParabolicTower', 'Pattern', 'Position', 'Props', 'Random', 'Stars', 'TextGravity', 'Torus'], 'techniques/transform': ['Flare', 'RotoTranslation', 'Spirals', 'SurfaceProjection'], 'techniques/lima': ['Lima', 'Lustrum'], 'topics/cylinder': ['Cylinder'], 'techniques/programming': ['Tori'], 'techniques/elements': ['Cube', 'Elements'], 'techniques/pattern': ['Pattern'], 'techniques/function': ['CircumCircle', 'Flare'], 'topics/curve': ['BeamFreq', 'Bezier', 'BezierCurve', 'BezierSpline', 'Casteljou', 'Curves', 'Lustrum', 'NurbsCircle', 'NurbsCurve', 'Spirals', 'Sweep'], 'topics/drawing': ['BeamFreq', 'Lustrum', 'TestDraw'], 'techniques/menu': ['FeEx', 'Hesperia'], 'techniques/bump': ['Baumkuchen'], 'techniques/extrude': ['Extrude', 'ExtrudeMesh', 'Mesh'], 'techniques/cut': ['Multicut'], 'topics/domes': ['Diamatic', 'Geodesic', 'Hesperia', 'Lamella', 'ScallopDome', 'Schwedler'], 'techniques/filename': ['World'], 'techniques/match': ['MeshMatch'], 'techniques/axes': ['Inertia'], 'techniques/external': ['BeamFreq'], 'topics/geometry': ['Bezier', 'BezierCurve', 'BezierSpline', 'Casteljou', 'Circle', 'CircumCircle', 'Clip', 'Cone', 'Cones', 'Cube', 'Curves', 'Cylinder', 'Elements', 'Flare', 'Geodesic', 'Grid', 'Helix', 'Hesperia', 'HorseSlice', 'HorseTorse', 'Hypar', 'Hyparcap', 'Inertia', 'Interpolate', 'Isopar', 'KochLine', 'Lamella', 'Light', 'Mesh', 'MeshSmoothing', 'Mobius', 'Novation', 'NurbsCircle', 'NurbsCurve', 'NurbsSurface', 'OpticalIllusions', 'ParabolicTower', 'Pattern', 'Position', 'Projection', 'Props', 'RotoTranslation', 'ScallopDome', 'Schwedler', 'Section2D', 'SpaceTrussRoof', 'Sphere', 'Sphere2', 'Sphere_stl', 'Spirals', 'SplineSurface', 'Stars', 'Sweep', 'SweepBeam', 'TestDraw', 'Tori', 'Torus', 'TrussBridge', 'TrussFrame', 'WireStent', 'X_truss'], 'topics/game': ['Rubik'], 'topics/structure': ['Baumkuchen', 'Diamatic', 'DoubleLayer'], 'level/advanced': ['Clock', 'DataInterpolation', 'Edit', 'FeAbq', 'FeEx', 'Hesperia', 'HorseSlice', 'HorseTorse', 'Mobius', 'NurbsCircle', 'NurbsCurve', 'NurbsSurface', 'RotoTranslation', 'Slice', 'SpaceTrussRoof_abq', 'SpaceTrussRoof_calpy', 'SplineSurface', 'SuperShape', 'Viewports', 'WireStent_calpy', 'X_truss_calpy'], 'techniques/Hex8': ['architect'], 'techniques/symmetry': ['Props'], 'topics/postprocess': ['DataInterpolation'], 'techniques/image': ['SurfaceProjection'], 'techniques/connect': ['Bezier', 'Circle', 'Cones', 'Connect', 'ConnectMesh', 'NurbsCurve', 'SpaceTrussRoof', 'Sphere_stl'], 'techniques/solve': ['Bezier'], 'techniques/interactive': ['Edit'], 'techniques/nurbs': ['Casteljou', 'NurbsCircle', 'NurbsCurve', 'NurbsSurface'], 'techniques/import': ['CircumCircle', 'Curves', 'Cylinder', 'Lustrum', 'SpaceTrussRoof'], 'techniques/spline': ['BezierSpline', 'Curves', 'SplineSurface'], 'techniques/border': ['Mesh', 'NurbsCircle', 'NurbsCurve'], 'topics/illustration': ['Carpetry', 'Formex', 'Lima', 'Lustrum', 'MeshSmoothing', 'OpticalIllusions', 'Rubik'], 'techniques/revolve': ['WedgeHex']} pyformex-0.8.6/pyformex/examples/Spirals.py0000644000211500021150000000455611705104656020727 0ustar benebene00000000000000# $Id: Spirals.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Spirals level = 'normal' topics = ['geometry','curve'] techniques = ['transform'] See also the Sweep example for a more sophisticated Spirals application """ from plugins import curve m = 100 # number of cells along spiral a = 1. # number of 360 degree turns linewidth(2) clear() F = Formex(origin()) # base pattern, here a point F = F.replic(m,1.,0) s = a*2*pi/m F = F.scale(s) draw(F) def spiral(X,dir=[0,1,2],rfunc=lambda x:1,zfunc=lambda x:1): """Perform a spiral transformation on a coordinate array""" theta = X[...,dir[0]] r = rfunc(theta) + X[...,dir[1]] x = r * cos(theta) y = r * sin(theta) z = zfunc(theta) + X[...,dir[2]] X = hstack([x,y,z]).reshape(X.shape) return Coords(X) nwires=6 phi = 30. alpha2 = 70. c = 1. a = c*tand(phi) b = tand(phi) / tand(alpha2) zf = lambda x : c * exp(b*x) rf = lambda x : a * exp(b*x) S = spiral(F.coords,[0,1,2],rf)#.rosette(nwires,360./nwires) PL = curve.PolyLine(S[:,0,:]) clear() draw(PL,color=red) draw(PL.coords,color=red) if ack("Spread point evenly?"): at = PL.atLength(PL.nparts) X = PL.pointsAt(at) PL = curve.PolyLine(X) clear() draw(PL,color=blue) draw(PL.coords,color=blue) # End pyformex-0.8.6/pyformex/examples/Mesh.py0000644000211500021150000000727311705104656020205 0ustar benebene00000000000000# $Id: Mesh.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Mesh level = 'beginner' topics = ['geometry', 'mesh'] techniques = ['extrude','border','pause'] .. Description Mesh ---- This example illustrates some of the powerful methods provided by the **Mesh** class. The example constructs a 2D or 3D (selectable by the user) mesh by extruding a point into a line, the line into a surface, and in case of a 3D structure, the surface into a volume. In a sequence of steps, the example then draws the following geometries with element numbers included: - the extruded geometry, - all the edges of all elments, - the unique edges, - all the faces of all elements, - the unique faces, - the border (in the 3D case, this is a set of faces, in the 2D case it is a set of lines. At each step the number of elements is printed. Remark ------ The script pauses at each step. The user should click the **STEP** button to move the the next step, or the **CONTINUE** button to move to the end of the script. Script ------ Notice how the same script with the same methods is working for both the 2D and the 3D cases. Exercises --------- 1. Make this script also work for the 1D case. """ def atExit(): #print "THIS IS THE EXIT FUNC" pf.GUI.setBusy(False) pf.GUI.setBusy() #draw.logfile = open('pyformex.log','w') clear() smoothwire() transparent() n = 3,2,5 a = Formex(origin()) res = ask("Choose the model:",["None","2D","3D","Help"]) if res == "None": exit() if res == "Help": showDescription() exit() ndim = int(res[0]) if ndim == 2: view('front') else: view('iso') for i in range(ndim): a = a.extrude(n[i],1.,i) draw(a) drawNumbers(a) clear() m = a.toMesh().setProp(1) export({'mesh':m}) draw(m) drawNumbers(m) pause("%s elements" % m.nelems()) flat() e = Mesh(m.coords,m.getLowerEntities(1)).setProp(2) clear() draw(e) drawNumbers(e) pause("%s edges" % e.nelems()) e = Mesh(m.coords,m.getEdges()).setProp(2) clear() draw(e) drawNumbers(e) pause("%s unique edges" % e.nelems()) smoothwire() e = Mesh(m.coords,m.getLowerEntities(2)).setProp(3) clear() draw(e) drawNumbers(e) pause("%s faces" % e.nelems()) e = Mesh(m.coords,m.getFaces()).setProp(3) clear() draw(e) drawNumbers(e) pause("%s unique faces" % e.nelems()) e = Mesh(m.coords,m.getBorder()).setProp(4) export({'border':e}) clear() draw(e) drawNumbers(e) pause("%s border elements" % e.nelems()) clear() m = m.setProp(arange(m.nelems())) draw(m) drawNumbers(m) pause("mesh with properties") e = m.getBorderMesh() clear() draw(e) drawNumbers(e) message("border elements inherit the properties") # End pyformex-0.8.6/pyformex/examples/Novation.py0000644000211500021150000000574711705104656021112 0ustar benebene00000000000000# $Id: Novation.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Novation level = 'normal' topics = ['geometry','surface'] techniques = ['dialog', 'persistence', 'color'] """ reset() basechoices = ['Triangles','Quadrilaterals'] renderchoices = pf.canvas.rendermodes[:5] res = askItems([ _I('baseGeom',itemtype='radio',choices=basechoices,text='Type of surface element'), _I('nbumps',3,text='Number of bumps'), _I('rendermode',choices=renderchoices,text='Render mode'), _I('transparent',False,text='Transparent'), _I('bottom',False,text='Add a bottom plate'), _I('shrink',False,text='Shrink elements'), _I('export',False,text='Export to .stl'), ]) if not res: exit() globals().update(res) n = 10*nbumps if baseGeom == 'Triangles': # The base consists of two triangles e = Formex([[[0,0,0],[1,0,0],[0,1,0]],[[1,0,0],[1,1,0],[0,1,0]]],1).replic2(n,n,1,1) else: # The base is one quadrilateral e = Formex([[[0,0,0],[1,0,0],[1,1,0],[0,1,0]]],1).replic2(n,n,1,1) # These are lines forming quadrilaterals #e = Formex([[[0,0,0],[1,0,0]]]).rosad(.5,.5).rinid(n,n,1,1) # Novation (Spots) s = nbumps+1 r = n/s h = 12 a = [ [r*i,r*j,h] for j in range(1,s) for i in range(1,s) ] if bottom: # create a bottom b = e.reverse() #b.setProp(2) # create the bumps for p in a: e = e.bump(2,p, lambda x:exp(-0.5*x),[0,1]) if shrink: e = e.shrink(0.8) renderMode(rendermode) if transparent: pf.canvas.alphablend = True if bottom: draw(b,color=yellow,alpha=1.0) draw(e,alpha=0.8) if export and checkWorkdir(): from plugins import trisurface f = open('novation.stl','w') F = e # + b # Create triangles G = F.selectNodes([0,1,2]) # If polygones, add more triangles for i in range(3,F.nplex()): G += F.selectNodes([0,i-1,i]) clear() draw(G) surface.write_stla(f,G.coords) f.close() # End pyformex-0.8.6/pyformex/examples/Clock.py0000644000211500021150000001044711705104656020341 0ustar benebene00000000000000# $Id: Clock.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Clock level = 'advanced' topics = [] techniques = [] """ from formex import * # Needed if we want to use this example as a module from gui.draw import * # Needed if we want to use this example as a module import simple from datetime import datetime from PyQt4 import QtCore class AnalogClock(object): """An analog clock built from Formices""" def __init__(self,lw=2,mm=0.75,hm=0.85,mh=0.7,hh=0.6, sh=0.9): """Create an analog clock.""" self.linewidth = lw self.circle = simple.circle(a1=2.,a2=2.) radius = Formex('l:2') self.mainmark = radius.divide([mm,1.0]) self.hourmark = radius.divide([hm,1.0]) self.mainhand = radius.divide([0.0,mh]) self.hourhand = radius.divide([0.0,hh]) if sh > 0.0: self.secshand = radius.divide([0.0,sh]) else: self.secshand = None self.hands = [] self.timer = None def draw(self): """Draw the clock (without hands)""" draw(self.circle,color='black',linewidth=self.linewidth) draw(self.mainmark.rosette(4,90),color='black',linewidth=self.linewidth) draw(self.hourmark.rot(30).rosette(2,30).rosette(4,90), color='black',linewidth=0.5*self.linewidth) def drawTime(self,hrs,min,sec=None): """Draw the clock's hands showing the specified time. If no seconds are specified, no seconds hand is drawn. """ hrot = - hrs*30. - min*0.5 mrot = - min*6. pf.canvas.removeActors(self.hands) MH = draw(self.mainhand.rot(mrot),bbox=None,color='red',linewidth=self.linewidth) HH = draw(self.hourhand.rot(hrot),bbox=None,color='red',linewidth=self.linewidth) self.hands = [MH,HH] if self.secshand and sec: srot = - sec*6. SH = draw(self.secshand.rot(srot),bbox=None,color='orange',linewidth=0.5*self.linewidth) self.hands.append(SH) def drawNow(self): """Draw the hands showing the current time.""" now = datetime.now() self.drawTime(now.hour,now.minute,now.second) breakpt("The clock has been stopped!") def run(self,granularity=1,runtime=100): """Run the clock for runtime seconds, updating every granularity.""" if granularity > 0.0: self.timer = QtCore.QTimer() self.timer.connect(self.timer,QtCore.SIGNAL("timeout()"),self.drawNow) self.timer.start(1000*granularity) if runtime > 0.0: self.timeout = QtCore.QTimer() self.timeout.connect(self.timeout,QtCore.SIGNAL("timeout()"),self.stop) self.timeout.setSingleShot(True) self.timeout.start(1000*runtime) def stop(self): """Stop a running clock.""" print "STOP" if self.timer: self.timer.stop() if __name__ == "draw": reset() C = AnalogClock() C.draw() setDrawOptions({'bbox':None}) res = askItems([('runtime',15,{'text':'Run time (seconds)'})]) if res and res['runtime'] > 0: C.drawNow() C.run() sleep(res['runtime']) C.stop() pyformex-0.8.6/pyformex/examples/Geodesic.py0000644000211500021150000000346011705104656021025 0ustar benebene00000000000000# $Id: Geodesic.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Geodesic Dome level = 'beginner' topics = ['geometry','domes'] techniques = ['dialog', 'color'] """ clear() view('front') m=n=6 f=0.8 res = askItems([('m',m),('n',n),('f',f)]) if not res: exit() m = res['m'] n = res['n'] f = res['f'] v=0.5*sqrt(3.) a = Formex([[[0,0],[1,0],[0.5,v]]],1) aa = Formex([[[1,0],[1.5,v],[0.5,v]]],2) draw(a+aa) #pause() d = a.replic2(m,min(m,n),1.,v,bias=0.5,taper=-1) dd = aa.replic2(m-1,min(m-1,n),1.,v,bias=0.5,taper=-1) clear() draw(d+dd) #pause() e = (d+dd).rosette(6,60,point=[m*0.5,m*v,0]) draw(e) #pause() g = e.mapd(2,lambda d:f*sqrt((m+1)**2-d**2),e.center(),[0,1]) clear() draw(g) # End pyformex-0.8.6/pyformex/examples/Inertia.py0000644000211500021150000000473111705104656020700 0ustar benebene00000000000000# $Id: Inertia.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Inertia level = 'beginner' topics = ['geometry'] techniques = ['color','axes'] """ from plugins import inertia reset() wireframe() view('front') def unitAxes(): """Create a set of three axes.""" Hx = Formex('l:1',5).translate([-0.5,0.0,0.0]) Hy = Hx.rotate(90) Hz = Hx.rotate(-90,1) Hx.setProp(4) Hy.setProp(5) Hz.setProp(6) return Formex.concatenate([Hx,Hy,Hz]) Axes = unitAxes() def showPrincipal1(F): """Show the principal axes.""" clear() C,I = inertia.inertia(F.coords) pf.message("Center: %s" % C) pf.message("Inertia tensor: %s" % I) Iprin,Iaxes = inertia.principal(I) pf.debug("Principal Values: %s" % Iprin) pf.debug("Principal Directions:\n%s" % Iaxes) siz = F.dsize() H = Axes.scale(siz).affine(Iaxes.transpose(),C) Ax,Ay,Az = Iaxes[:,0],Iaxes[:,1],Iaxes[:,2] G = Formex([[C,C+Ax],[C,C+Ay],[C,C+Az]],3) draw([F,G,H]) sleep(2) return C,I,Iprin,Iaxes #F = Formex('l:1').replic(2,2,1).replic(2,2,2).scale(2) nx,ny,nz = 2,3,4 dx,dy,dz = 4,3,2 F = Formex([[[0,0,0]]]).replic(nx,dx,0).replic(ny,dy,1).replic(nz,dz,2) Fr = F C,I,Ip,Ia = showPrincipal1(Fr) Fr = F.rotate(30,0).rotate(45,1).rotate(60,2) C,I,Ip,Ia = showPrincipal1(Fr) sleep(2) Fo = Formex([[C]]) Fc = connect([Fo,Fr],loop=True) draw(Fc) pyformex-0.8.6/pyformex/examples/TrussBridge.py0000644000211500021150000000621611705104656021542 0ustar benebene00000000000000# $Id: TrussBridge.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """TrussBridge level = 'normal' topics = ['geometry'] techniques = ['color'] """ L = 12000 # Nominal length of the bridge N = 12 # Number of modules (should be even) Lo = 300 # Overshoot at the end of the bridge B = 2000 # Nominal width of the bridge Bo = 500 # Sideways overshoot for bracing Bi = 200 # Offset of wind bracing system from girder H = 1100 # Nominal height of the bridge Hb = 600 # Height of the bracing # First we half of one of the trusses. The origin is at the center of the # bridge Lm = L/N # modular length n = N/2 # number of modules for half bridge b = B/2 clear() # We start with the bottom girder, and copy it to the top nodes = Formex([[[0,0,b]]]).replic(n+1,Lm) draw(nodes,view='iso') bot_gird = connect([nodes,nodes],bias=[0,1]) top_gird = bot_gird.translate([0,H,0]) # Add the verticals and diagonals verticals = connect([bot_gird,top_gird],nodid=[1,1]) diagonals = connect([bot_gird,top_gird],nodid=[0,1]) verticals.setProp(3) diagonals.setProp(3) # We missed the central vertical : we construct it by hand central_vertical = Formex([[bot_gird[0,0],top_gird[0,0]]],3) # Bridge deck and braces nodes_out = nodes.translate([0,0,Bo]) nodes_up = nodes.translate([0,Hb,0]) deck_girders = connect([nodes_out,nodes_out.reflect(2)]) deck_girders.setProp(1) braces = connect([nodes_out,nodes_up]) braces.setProp(2) # Wind bracing nodes1 = nodes.select([2*i for i in range(n/2+1)]).translate([0,0,-Bi]) nodes2 = nodes.select([2*i+1 for i in range(n/2)]).translate([0,0,-Bi]).reflect(2) draw(nodes1+nodes2) wind_bracing = connect([nodes1,nodes2]) + connect([nodes2,nodes1],bias=[0,1]) wind_bracing.setProp(5) # Assemble half bridge central = central_vertical + central_vertical.reflect(2) half_truss = bot_gird + top_gird + verticals + diagonals quarter = half_truss + braces half_bridge = quarter + quarter.reflect(2) + deck_girders + wind_bracing draw(half_bridge+central) # And a full bridge bridge = half_bridge + half_bridge.reflect(0) + central clear() draw(bridge) pyformex-0.8.6/pyformex/examples/ColoredText.py0000644000211500021150000000357211705104656021543 0ustar benebene00000000000000# $Id: ColoredText.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """ColoredText level = 'beginner' topics = [] techniques = ['color','text'] """ n = 40 T = ['Python','NumPy','OpenGL','QT4','pyFormex'] font = 'times' ftmin,ftmax = 12,36 r = random.random((n,7)) w,h = pf.canvas.width(), pf.canvas.height() a = r[:,:2] * array([w,h]).astype(int) size = (ftmin + r[:,2] * (ftmax-ftmin)).astype(int) colors = r[:,3:6] t = (r[:,6] * len(T)).astype(int) clear() bgcolor(white) lights(False) TA = None for i in range(n): # fgcolor(red) TB = drawText(T[t[i]],a[i][0],a[i][1],font=font,size=size[i],color=list(colors[i])) sleep(0.2) breakpt() if i < n/2: undecorate(TA) TA = TB #drawTextQt(T[t[i]],a[i][0],a[i][1]) #pf.canvas.update() # End pyformex-0.8.6/pyformex/examples/X_truss_calpy.py0000644000211500021150000001277211705104656022150 0ustar benebene00000000000000# $Id: X_truss_calpy.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """X-shaped truss analysis level = 'advanced' topics = ['FEA'] techniques = ['color','persistence'] """ ############################ # Load the needed calpy modules from plugins import calpy_itf #calpy_itf.check() from calpy.fe_util import * from calpy.truss3d import * ############################ if not checkWorkdir(): exit() import time ########################### reset() clear() from examples.X_truss import X_truss bgcolor(lightgrey) # create a truss (Vandepitte, Chapter 1, p.16) n = 5 l = 800. h = 800. truss = X_truss(n,l,h) # draw it clear() draw(truss.allNodes(),wait=False) draw(truss.allBars()) # assign property numbers truss.bot.setProp(0) truss.top.setProp(0) truss.vert.setProp(2) truss.dia1.setProp(1) truss.dia2.setProp(1) for p in [ truss.bot.prop, truss.top.prop ]: p[0] = p[n-1] = 3 # define member properties materials={ 'steel' : { 'E' : 207000, 'nu' : 0.3 } } sections={ 'hor' : 50, 'end' : 40, 'dia' : 40, 'vert': 30 } properties = { '0' : [ 'steel', 'hor' ], '3' : [ 'steel', 'end' ], '2' : [ 'steel', 'vert' ], '1' : [ 'steel', 'dia' ] } def getmat(key): """Return the 'truss material' with key (str or int).""" p = properties.get(str(key),[None,None]) m = materials.get(p[0],{}) E = m.get('E',0.) rho = m.get('rho',0.) A = sections.get(p[1],0.) return [ E, rho, A ] # create model for structural analysis model = truss.allBars() coords,elems = model.fuse() props = model.prop propset = model.propSet() clear() draw(Formex(reshape(coords,(coords.shape[0],1,coords.shape[1]))),wait=False) draw(model) ############################################ nnod = coords.shape[0] nelems = elems.shape[0] # boundary conditions # we use the knowledge that the elements are in the order # bot,top,vert,mid1,mid2 # remember to add 1 to number starting from 1, as needed by calpy nr_fixed_support = elems[0][0] nr_moving_support = elems[n-1][1] nr_loaded = elems[2][1] # right node of the 3-d element bcon = ReadBoundary(nnod,3,""" all 0 0 1 %d 1 1 1 %d 0 1 1 """ % (nr_fixed_support + 1,nr_moving_support + 1)) NumberEquations(bcon) mats=array([ getmat(i) for i in range(max(propset)+1) ]) matnod = concatenate([reshape(props+1,(nelems,1)),elems+1],1) ndof=bcon.max() nlc=1 loads=zeros((ndof,nlc),Float) loads[:,0]=AssembleVector(loads[:,0],[ 0.0, -50.0, 0.0 ],bcon[nr_loaded,:]) message("Performing analysis: this may take some time") outfilename = os.path.splitext(os.path.basename(pf.scriptName))[0] + '.out' outfile = open(outfilename,'w') message("Output is written to file '%s' in %s" % (outfilename,os.getcwd())) stdout_saved = sys.stdout sys.stdout = outfile print "# File created by pyFormex on %s" % time.ctime() print "# Script name: %s" % pf.scriptName displ,frc = static(coords,bcon,mats,matnod,loads,Echo=True) print "# Analysis finished on %s" % time.ctime() sys.stdout = stdout_saved outfile.close() ################################ #Using pyFormex as postprocessor ################################ from gui.colorscale import * import gui.decors # Creating a formex for displaying results is fairly easy results = Formex(coords[elems],range(nelems)) # Now try to give the formex some meaningful colors. # The frc array returns element forces and has shape # (nelems,nforcevalues,nloadcases) # In this case there is only one resultant force per element (the # normal force), and only load case; we still need to select the # scalar element result values from the array into a onedimensional # vector val. val = frc[:,0,0] # create a colorscale CS = ColorScale([blue,yellow,red],val.min(),val.max(),0.,2.,2.) cval = array(map(CS.color,val)) #aprint(cval,header=['Red','Green','Blue']) clear() draw(results,color=cval) bgcolor('lightgreen') linewidth(3) drawText('Normal force in the truss members',400,100,size=12) CL = ColorLegend(CS,256) CLA = decors.ColorLegend(CL,10,10,30,200) decorate(CLA) # and a deformed plot dscale = 10000. dcoords = coords + dscale * displ[:,:,0] # first load case deformed = Formex(dcoords[elems],range(nelems)) clear() pf.canvas.addDecoration(CLA) linewidth(1) draw(results,color='darkgreen') linewidth(3) draw(deformed,color=cval) drawText('Normal force in the truss members',400,100,size=14) drawText('Deformed geometry (scale %.2f)' % dscale,400,130,size=12) if ack("Show the output file?"): showFile(outfilename) # End pyformex-0.8.6/pyformex/examples/ColorScale.py0000644000211500021150000001241011705104656021324 0ustar benebene00000000000000# $Id: ColorScale.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """ColorScale Examples showing the use of the 'colorscale' plugin level = 'normal' topics = ['FEA'] techniques = ['dialog', 'color'] """ from gui.colorscale import * from gui.gluttext import GLUTFONTS input_data = [ _I('valrange',text='Value range type',itemtype='select',choices=['Minimum-Medium-Maximum','Minimum-Maximum']), _I('maxval',12.0,text='Maximum value'), _I('medval',0.0,text='Medium value'), _I('minval',-6.0,text='Minimum value'), _I('palet',text='Predefined color palette',choices=Palette.keys()), _G('custom',text='Custom Color palette',items=[ _I('maxcol',[1.,0.,0.],text='Maximum color',itemtype='color'), _I('medcol',[1.,1.,0.],text='Medium color',itemtype='color'), _I('mincol',[1.,1.,1.],text='Minimum color',itemtype='color'), ],checked=False), _I('maxexp',1.0,text='High exponent'), _I('minexp',1.0,text='Low exponent'), _I('ncolors',200,text='Number of colors'), _T('Grid/Label',[ _I('ngrid',-1,text='Number of grid intervals'), _I('linewidth',1.5,text='Line width'), _I('nlabel',-1, text='Number of label intervals'), _I('dec',2,text='Decimals'), _I('scale',0,text='Scaling exponent'), _I('lefttext',True,text='Text left of colorscale'), _I('font','hv18',text='Font',choices=GLUTFONTS.keys()), _I('header','Currently not displayed',text='Header',enabled=False), _I('gravity','Notused',text='Gravity',enabled=False), ]), _T('Position/Size',[ _I('autosize',True,text='Autosize'), _I('size',(100,600),text='Size'), _I('autopos',True,text='Autoposition'), _I('position',[400,50],text='Position'), ]), ] input_enablers = [ ('valrange','Minimum-Medium-Maximum','medval','medcol'), ('custom',False,'palet'), ('autosize',False,'size'), ('autopos',False,'position'), ] def show(): """Accept the data and draw according to them""" global medval,medcol,palet,minexp,grid,nlabels clear() lights(False) dialog.acceptData() res = dialog.results print res globals().update(res) if valrange == 'Minimum-Maximum': medval = None minexp = None if custom: palet = map(GLColor,[mincol,medcol,maxcol]) mw,mh = pf.canvas.Size() x,y = position if autopos: x = mw / 2 if autosize: h = int(0.9*(mh-y)) w = min(0.1*mw,100) else: w,h = size # ok, now draw it drawColorScale(palet,minval,maxval,medval,maxexp,minexp,ncolors,dec,scale,ngrid,linewidth,nlabel,lefttext,font,x,y,w,h) def drawColorScale(palet,minval,maxval,medval,maxexp,minexp,ncolors,dec,scale,ngrid,linewidth,nlabel,lefttext,font,x,y,w,h): """Draw a color scale with the specified parameters""" CS = ColorScale(palet,minval,maxval,midval=medval,exp=maxexp,exp2=minexp) CL = ColorLegend(CS,ncolors) CLA = decors.ColorLegend(CL,x,y,w,h,ngrid=ngrid,linewidth=linewidth,nlabel=nlabel,font=font,dec=dec,scale=scale,lefttext=lefttext) decorate(CLA) def close(): global dialog pf.PF['ColorScale_data'] = dialog.results if dialog: dialog.close() dialog = None # Release scriptlock scriptRelease(__file__) def timeOut(): """What to do on a InputDialog timeout event. As a policy, all pyFormex examples should behave well on a dialog timeout. Most users can simply ignore this. """ show() wait() close() if __name__ == 'draw': # Update the data items from saved values try: saved_data = named('ColorScale_data') widgets.updateDialogItems(input_data,saved_data) except: pass # Create the modeless dialog widget dialog = widgets.InputDialog(input_data,enablers=input_enablers,caption='ColorScale Dialog',actions = [('Close',close),('Show',show)],default='Show') # Examples style requires a timeout action dialog.timeout = timeOut # Show the dialog and let the user have fun #dialog = widgets.ScrollDialog(dialog) dialog.show() # Block other scripts scriptLock(__file__) # End pyformex-0.8.6/pyformex/examples/TextGravity.py0000644000211500021150000000350011705104656021570 0ustar benebene00000000000000# $Id: TextGravity.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """TextGravity Show the use of the text gravity parameter. level = 'beginner' topics = [] techniques = ['text'] """ clear() lights(False) x = pf.canvas.width()//2 y = pf.canvas.height()//2 x,y = 600,300 print x,y from gui.decors import Grid d = 50 G = Grid(x-50,y-50,x+50,y+50,2,2) decorate(G) delay(2) for g in [ 'NW','N','NE','W','C','E','SW','S','SE']: T = drawText("XXX %s XXX"%g,x,y,gravity=g) wait() undecorate(T) delay(1) from gui.gluttext import GLUTFONTS for f in GLUTFONTS.keys(): S = drawText(f,20,20,font='hv18') T = drawText('X',x,y,font=f,gravity='C') wait() undecorate(S) undecorate(T) # End pyformex-0.8.6/pyformex/examples/Circle.py0000644000211500021150000000634411705104656020510 0ustar benebene00000000000000# $Id: Circle.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Circle level = 'normal' topics = ['geometry'] techniques = ['connect','dialog', 'animation'] """ from simple import circle from geomtools import rotationAngle # Test linewidth(1) for i in [3,4,5,6,8,12,20,60,180]: #print "%s points" % i clear() draw(circle(360./i,360./i),bbox=None) clear() draw(circle(360./i,2*360./i),bbox=None) clear() draw(circle(360./i,360./i,180.),bbox=None) # Example of the use clear() n = 40 h = 0.5 line = Formex('l:'+'1'*n).scale(2./n).translate([-1.,0.,0.]) curve = line.bump(1,[0.,h,0.],lambda x: 1.-x**2) curve.setProp(1) draw(line) draw(curve) # Create circles in the middle of each segment, with normal along the segment # begin and end points of segment A = curve.coords[:,0,:] B = curve.coords[:,1,:] # midpoint and segment director C = 0.5*(B+A) D = B-A # vector initially normal to circle defined above nuc = array([0.,0.,1.]) # rotation angles and vectors ang,rot = rotationAngle(nuc,D) # diameters varying linearly with the |x| coordinate diam = 0.1*h*(2.-abs(C[:,0])) # finally, here are the circles: circles = [ circle().scale(d).rotate(a,r).translate(c) for d,r,a,c in zip(diam,rot,ang,C) ] F = Formex.concatenate(circles).setProp(3) draw(F) # And now something more fancy: connect 1 out of 15 points of the circles res = askItems([ ('Connect circles',True), ('Create Triangles',True), ('Fly Through',True), ]) if res: if res['Connect circles'] or res['Create Triangles']: conn = range(0,180,15) if res['Connect circles']: G = Formex.concatenate([ connect([c1.select(conn),c2.select(conn)]) for c1,c2 in zip(circles[:-1],circles[1:]) ]) draw(G) if res['Create Triangles']: conn1 = concatenate([conn[1:],conn[:1]]) G = Formex.concatenate([ connect([c1.select(conn),c2.select(conn),c2.select(conn1)]) for c1,c2 in zip(circles[:-1],circles[1:]) ]) smooth() print G.eltype draw(G) if res['Fly Through']: flyAlong(curve,sleeptime=0.1) clear() draw(line) draw(curve) draw(F) pyformex-0.8.6/pyformex/examples/Mobius.py0000644000211500021150000000447011705104656020543 0ustar benebene00000000000000# $Id: Mobius.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Mobius Ring level = 'advanced' topics = ['geometry','surface'] techniques = ['dialog', 'animation', 'color'] """ reset() smoothwire() res = askItems([ ('w',2,{'text':'width'}), ('l',30,{'text':'length'}), ('n',1,{'text':'number of turns'}), ]) if not res: exit() globals().update(res) C = Formex('l:1234') cell = connect([C,C,C,C],bias=[0,1,2,3]) strip = cell.replic2(l,w,1.,1.).translate(1,-0.5*w) TA = draw(strip,color='orange',bkcolor='red') sleep(1) nsteps = 40 step = n*180./nsteps/l for i in arange(nsteps+1): a = i*step torded = strip.map(lambda x,y,z: [x,y*cosd(x*a),y*sind(x*a)]) TB = draw(torded,color='orange',bkcolor='red') undraw(TA) TA = TB sleep(1) #TA = None nsteps = 60 step = 360./nsteps for i in arange(1,nsteps+1): ring = torded.trl(2,l*nsteps/pi/i).scale([i*step/l,1.,1.]).trl(0,-90).cylindrical(dir=[2,0,1]) TB = draw(ring,color='orange',bkcolor='red') undraw(TA) TA = TB sleep(1) nsteps = 80 step = 720./nsteps for i in arange(1,nsteps+1): mobius = ring.rotate(i*step,1) TB = draw(mobius,color='orange',bkcolor='red',bbox='last') undraw(TA) TA = TB # End pyformex-0.8.6/pyformex/examples/SpaceTrussRoof.py0000644000211500021150000000656411705104656022235 0ustar benebene00000000000000# $Id: SpaceTrussRoof.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Double Layer Flat Space Truss Roof level = 'normal' topics = ['geometry'] techniques = ['dialog', 'animation', 'color', 'import', 'connect', 'interpolate'] """ import simple from gui.widgets import simpleInputItem as I dx = 180 # Modular size (cm) ht = 150 # Deck height nx = 14 # number of bottom deck modules in x direction (should be even) ny = 14 # number of bottom deck modules in y direction (should be even) colht = 560 # Column height m = 2 # Column multiplicity: should be an integer divisor of nx and ny coldx = m * dx # column distance (should be a multiple of dx) ncx = nx/m + 1 # number of columns in x-direction ncy = ny/m + 1 # and in y-direction bot = (Formex("1").replic2(nx,ny+1,1,1) + Formex("2").replic2(nx+1,ny,1,1)).scale(dx) bot.setProp(3) top = (Formex("1").replic2(nx+1,ny+2,1,1) + Formex("2").replic2(nx+2,ny+1,1,1)).scale(dx).translate([-dx/2,-dx/2,ht]) top.setProp(0) T0 = Formex(4*[[[0,0,0]]]) # 4 times the corner of the bottom deck T4 = top.select([0,1,nx+1,nx+2]) # 4 nodes of corner module of top deck dia = connect([T0,T4]).replic2(nx+1,ny+1,dx,dx) dia.setProp(1) col = (Formex([[[0,0,-colht],[0,0,0]]]).replic2(ncx,2,m,ny) + Formex([[[0,m,-colht],[0,m,0]]]).replic2(2,ncy-2,nx,m)).scale([dx,dx,1]) col.setProp(2) F = top+bot+dia+col clear() linewidth(1) draw(F) F = F.rotate(-90,0) # put the structure upright clear() draw(F) createView('myview1',(30.,0.,0.)) view('myview1',True) for i in range(19): createView('myview2',(i*10.,20.,0.)) view('myview2',True) # fly tru if ack("Do you want to fly through the structure?"): totaltime = 10 nsteps = 50 # make sure bottom iz at y=0 and structure is centered in (x,z) F = F.centered() F = F.translate(1,-F.bbox()[0,1]) clear() linewidth(1) draw(F) bb = F.bbox() # create a bottom plate B = simple.rectangle(1,1).swapAxes(1,2).centered().scale(F.sizes()[0]*1.5) smooth() draw(B) # Fly at reasonable height bb[0,1] = bb[1,1] = 170. ends = interpolate(Formex([[bb[0]]]),Formex([[bb[1]]]),[-0.5,0.6]) path = connect([ends,ends],bias=[0,1]).divide(nsteps) linewidth(2) draw(path) steptime = float(totaltime)/nsteps flyAlong(path,sleeptime=steptime) pyformex-0.8.6/pyformex/examples/Sphere.py0000644000211500021150000000463511705104656020536 0ustar benebene00000000000000# $Id: Sphere.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Sphere level = 'normal' topics = ['geometry','surface'] techniques = ['dialog', 'color'] """ clear() wireframe() nx=32 # number of modules in circumferential direction ny=32 # number of modules in meridional direction rd=100 # radius of the sphere cap top=70 # latitude angle at the top (90 = closed) bot=-70 # latitude angle of the bottom (-90 = closed) a = ny*float(top)/(bot-top) # First, a line based model base = Formex('l:543',[1,2,3]) # single cell draw(base) d = base.select([0]).replic2(nx,ny,1,1) # all diagonals m = base.select([1]).replic2(nx,ny,1,1) # all meridionals h = base.select([2]).replic2(nx,ny+1,1,1) # all horizontals f = m+d+h draw(f) g = f.translate([0,a,1]).spherical(scale=[360./nx,bot/(ny+a),rd]) clear() draw(g) # Second, a surface model clear() base = Formex( [[[0,0,0],[1,0,0],[1,1,0]], [[1,1,0],[0,1,0],[0,0,0]]], [1,3] ) draw(base) f = base.replic2(nx,ny,1,1) draw(f) h = f.translate([0,a,1]).spherical(scale=[360./nx,bot/(ny+a),rd]) clear() draw(h) # Both g = g.translate([-rd,0,0]) h = h.translate([rd,0,0]) clear() bb = bbox([g,h]) draw(g,bbox=bb) draw(h,bbox=bb) ##if ack('Do you want to see the spheres with smooth rendering?'): ## smooth() ## pf.canvas.update() pyformex-0.8.6/pyformex/examples/SuperShape.py0000644000211500021150000001451211705104656021362 0ustar benebene00000000000000# $Id: SuperShape.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Super Shape level = 'advanced' topic = ['geometry'] techniques = ['dialog','persistence'] """ from simple import rectangle from utils import NameSequence from gui.draw import * from gui.imagecolor import * dialog = None savefile = None tol = 1.e-4 gname = NameSequence('Grid-0') sname = NameSequence('Shape-0') def createGrid(): """Create the grid from global parameters""" global B nx,ny = grid_size b,h = x_range[1]-x_range[0], y_range[1]-y_range[0] if grid_base.startswith('tri'): diag = grid_base[-1] else: diag = '' B = rectangle(nx,ny,b,h,diag=diag,bias=grid_bias).translate([x_range[0],y_range[0],1.]) if grid_skewness != 0.0: B = B.shear(0,1,grid_skewness*b*ny/(h*nx)) if x_clip: B = B.clip(B.test('any',dir=0,min=x_clip[0]+tol*b,max=x_clip[1]-tol*b)) if y_clip: B = B.clip(B.test('any',dir=1,min=y_clip[0]+tol*h,max=y_clip[1]-tol*h)) export({grid_name:B}) def createSuperShape(): """Create a super shape from global parameters""" global F B = pf.PF[grid_name] F = B.superSpherical(n=north_south,e=east_west,k=eggness) if scale == [1.0,1.0,1.0]: pass else: F = F.scale(scale) if post: print "Post transformation" F = eval(post) export({name:F}) def drawGrid(): """Show the last created grid""" clear() wireframe() view('front') draw(B,color=grid_color) def drawSuperShape(): """Show the last created super shape""" global color clear() smoothwire() if type(color) == str and color.startswith('file:'): print "trying to convert color" im = QtGui.QImage('Elevation-800.jpg') print im print im.isNull() nx,ny = grid_size color=image2glcolor(im.scaled(nx,ny)) print color.shape draw(F,color=color) def acceptData(): dialog.acceptData() pf.PF['_SuperShape_data_'] = dialog.results globals().update(dialog.results) ########################## # Button Functions def showGrid(): """Accept data, create and show grid.""" acceptData() createGrid() drawGrid() def replayShape(): """Create and show grid from current data.""" createGrid() createSuperShape() drawSuperShape() def show(): """Accept data, create and show shape.""" acceptData() replayShape() def close(): global dialog,savefile if dialog: dialog.close() dialog = None if savefile: savefile.close() savefile = None scriptRelease(__file__) def save(): global savefile show_shape() if savefile is None: filename = askNewFilename(filter="Text files (*.txt)") if filename: savefile = open(filename,'a') if savefile: print "Saving to file" savefile.write('%s\n' % str(dialog.results)) savefile.flush() globals().update({'grid_name':gname.next(),'name':sname.next(),}) if dialog: dialog['grid_name'].setValue(grid_name) dialog['name'].setValue(name) def replay(): global savefile if savefile: filename = savefile.name savefile.close() else: filename = os.path.join(getcfg('datadir'),'supershape.txt') filename = askFilename(cur=filename,filter="Text files (*.txt)") if filename: savefile = open(filename,'r') for line in savefile: print line globals().update(eval(line)) replayShape() savefile = open(filename,'a') ################# Dialog dialog_items = [ _G('Grid data',[ _I('grid_size',[24,12],), _I('x_range',(-180.,180.),), _I('y_range',(-90.,90.),), _I('grid_base','quad',itemtype='radio',choices=['quad','tri-u','tri-d','tri-x']), _I('grid_bias',0.0,), _I('grid_skewness',0.0,), _I('x_clip',(-360.,360.),), _I('y_clip',(-90.,90.),), _I('grid_name',gname.peek(),), _I('grid_color','blue',), ]), _G('Shape data',[ _I('north_south',1.0,), _I('east_west',1.0,), _I('eggness',0.0,), _I('scale',[1.,1.,1.],), _I('post','',), _I('name',sname.peek(),), _I('color','red',), ]), ] dialog_actions = [ ('Close',close), ('Reset',reset), ('Replay',replay), ('Save',save), ('Show Grid',showGrid), ('Show',show) ] dialog_default = 'Show' def timeOut(): play() close() def createDialog(): global dialog # Create the dialog dialog = Dialog( caption = 'SuperShape parameters', items = dialog_items, actions = dialog_actions, default = dialog_default ) # Update its data from stored values if pf.PF.has_key('_SuperShape_data_'): dialog.updateData(pf.PF['_SuperShape_data_']) # Always install a timeout in official examples! dialog.timeout = timeOut if __name__ == "draw": # when executed as a script, show the dialog clear() smoothwire() lights(True) transparent(False) createDialog() setView('eggview',(0.,-30.,0.)) view('eggview') dialog.show() scriptLock(__file__) # End pyformex-0.8.6/pyformex/examples/Tori.py0000644000211500021150000000446111705104656020222 0ustar benebene00000000000000# $Id: Tori.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """Torus variants level = 'normal' topics = ['geometry'] techniques = ['programming','widgets','globals'] """ def torus(m,n,surface=True): """Create a torus with m cells along big circle and n cells along small.""" if surface: C = Formex([[[0,0,0],[1,0,0],[0,1,0]],[[1,0,0],[1,1,0],[0,1,0]]],[1,3]) else: C = Formex('l:164',[1,2,3]) F = C.replic2(m,n,1,1) G = F.translate(2,1).cylindrical([2,1,0],[1.,360./n,1.]) H = G.translate(0,5).cylindrical([0,2,1],[1.,360./m,1.]) return H def series(): view='iso' for n in [3,4,6,8,12]: for m in [3,4,6,12,36]: clear() draw(torus(m,n),view) view=None def drawTorus(m,n): clear() draw(torus(m,n),None) def nice(): drawTorus(72,36) m = 20 n = 10 while not dialogTimedOut(): res = askItems([('m',m,'slider',{'text':'Number of elements along large circle','min':3,'max':72}), ('n',n,'slider',{'text':'Number of elements along small circle','min':3,'max':36}) ]) if not res: break globals().update(res) drawTorus(m,n) # End pyformex-0.8.6/pyformex/examples/Sweep.py0000644000211500021150000001467611705104656020401 0ustar benebene00000000000000# $Id: Sweep.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Spirals level = 'normal' topics = ['geometry','curve','mesh'] techniques = ['sweep',] """ from plugins import curve from odict import ODict import simple import re linewidth(2) clear() rfuncs = [ 'linear (Archimedes)', 'quadratic', 'exponential (equi-angular)', 'constant', # 'custom', ] # Define a dictionary of planar cross sections cross_sections = ODict() # select the planar patterns from the simple module for cs in simple.Pattern: if re.search('[a-zA-Z]',simple.Pattern[cs][2:]) is None: cross_sections[cs] = simple.Pattern[cs] # add some more patterns cross_sections.update({ 'channel' : 'l:1223', 'H-beam' : 'l:11/322/311', 'sigma' : 'l:16253', 'Z-beam': 'l:353', 'octagon':'l:15263748', 'swastika':'l:12+23+34+41', 'solid_square': '4:0123', 'solid_triangle': '3:012', 'swastika3': '3:012023034041', }) dialog_items = [ _I('nmod',100,text='Number of cells along spiral'), _I('turns',2.5,text='Number of 360 degree turns'), _I('rfunc',None,text='Spiral function',choices=rfuncs), _I('coeffs',(1.,0.5,0.2),text='Coefficients in the spiral function'), _I('spiral3d',0.0,text='Out of plane factor'), _I('spread',False,text='Spread points evenly along spiral'), _I('nwires',1,text='Number of spirals'), _G('sweep',text='Sweep Data',checked=True,items= [ _I('cross_section','cross','select',text='Shape of cross section',choices=cross_sections.keys()), _I('cross_rotate',0.,text='Cross section rotation angle before sweeping'), _I('cross_upvector','2',text='Cross section vector that keeps its orientation'), _I('cross_scale',0.,text='Cross section scaling factor'), ]), _I('flyalong',False,text='Fly along the spiral'), ] def spiral(X,dir=[0,1,2],rfunc=lambda x:1,zfunc=lambda x:0): """Perform a spiral transformation on a coordinate array""" theta = X[...,dir[0]] r = rfunc(theta) + X[...,dir[1]] x = r * cos(theta) y = r * sin(theta) z = zfunc(theta) + X[...,dir[2]] X = hstack([x,y,z]).reshape(X.shape) return Coords(X) def drawSpiralCurves(PL,nwires,color1,color2=None): if color2 is None: color2 = color1 # Convert to Formex, because that has a rosette() method PL = PL.toFormex() if nwires > 1: PL = PL.rosette(nwires,360./nwires) draw(PL,color=color1) draw(PL.points(),color=color2) def createCrossSection(): CS = Formex(cross_sections[cross_section]) if cross_rotate : CS = CS.rotate(cross_rotate) if cross_scale: CS = CS.scale(cross_scale) # Convert to Mesh, because that has a sweep() method CS = CS.swapAxes(0,2).toMesh() return CS def createSpiralCurve(turns,nmod): F = Formex(origin()).replic(nmod,1.,0).scale(turns*2*pi/nmod) a,b,c = coeffs rfunc_defs = { 'constant': lambda x: a, 'linear (Archimedes)': lambda x: a + b*x, 'quadratic' : lambda x: a + b*x + c*x*x, 'exponential (equi-angular)' : lambda x: a + b * exp(c*x), # 'custom' : lambda x: a + b * sqrt(c*x), } rf = rfunc_defs[rfunc] if spiral3d: zf = lambda x : spiral3d * rf(x) else: zf = lambda x : 0.0 S = spiral(F.coords,[0,1,2],rf,zf) PL = curve.PolyLine(S[:,0,:]) return PL def show(): """Accept the data and draw according to them""" clear() dialog.acceptData() globals().update(dialog.results) PL = createSpiralCurve(turns,nmod) drawSpiralCurves(PL,nwires,red,blue) if spread: at = PL.atLength(PL.nparts) X = PL.pointsAt(at) PL = curve.PolyLine(X) clear() drawSpiralCurves(PL,nwires,blue,red) if sweep: CS = createCrossSection() draw(CS) draw(CS) wait() structure = CS.sweep(PL,normal=[1.,0.,0.],upvector=eval(cross_upvector),avgdir=True) clear() smoothwire() draw(structure,color='red',bkcolor='cyan') if nwires > 1: structure = structure.toFormex().rosette(nwires,360./nwires).toMesh() draw(structure,color='orange') if flyalong: flyAlong(PL.scale(1.1).trl([0.0,0.0,0.2]),upvector=[0.,0.,1.],sleeptime=0.1) view('front') def close(): global dialog pf.PF['Sweep_data'] = dialog.results if dialog: dialog.close() dialog = None scriptRelease(__file__) def timeOut(): """What to do on a InputDialog timeout event. As a policy, all pyFormex examples should behave well on a dialog timeout. This is important for developers. Most normal users can simply ignore it. """ show() close() def createDialog(): global dialog # Create the dialog dialog = Dialog( caption = 'Sweep parameters', items = dialog_items, actions = [('Close',close),('Show',show)], default = 'Show' ) # Update its data from stored values if pf.PF.has_key('_Sweep_data_'): dialog.updateData(pf.PF['_Sweep_data_']) # Always install a timeout in official examples! dialog.timeout = timeOut if __name__ == 'draw': # Show the dialog and let the user have fun clear() createDialog() dialog.show() scriptLock(__file__) # End pyformex-0.8.6/pyformex/examples/__init__.py0000644000211500021150000000252311705104656021041 0ustar benebene00000000000000# $Id: __init__.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """pyFormex module initialisation. Currently, this does nothing. The file should be kept though, because it is needed to flag this directory as a Python module. """ pyformex-0.8.6/pyformex/examples/SpaceTrussRoof_abq.py0000644000211500021150000001045711705104656023054 0ustar benebene00000000000000# $Id: SpaceTrussRoof_abq.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Double Layer Flat Space Truss Roof level = 'advanced' topics = ['FEA'] techniques = ['color'] """ from plugins.properties import * from plugins.fe_abq import * import os #### #Data ################### dx = 1800 # Modular size [mm] ht = 900 # Deck height [mm] nx = 4 # number of bottom deck modules in x direction ny = 5 # number of bottom deck modules in y direction q = -0.005 #distributed load [N/mm^2] ############# #Creating the model ################### top = (Formex("1").replic2(nx-1,ny,1,1) + Formex("2").replic2(nx,ny-1,1,1)).scale(dx) top.setProp(3) bottom = (Formex("1").replic2(nx,ny+1,1,1) + Formex("2").replic2(nx+1,ny,1,1)).scale(dx).translate([-dx/2,-dx/2,-ht]) bottom.setProp(0) T0 = Formex(4*[[[0,0,0]]]) # 4 times the corner of the top deck T4 = bottom.select([0,1,nx,nx+1]) # 4 nodes of corner module of bottom deck dia = connect([T0,T4]).replic2(nx,ny,dx,dx) dia.setProp(1) F = (top+bottom+dia) # Show upright createView('myview1',(0.,-90.,0.)) clear();linewidth(1);draw(F,view='myview1') ############ #Creating FE-model ################### M = F.toMesh() ############### #Creating elemsets ################### # Remember: elems are in the same order as elements in F topbar = where(F.prop==3)[0] bottombar = where(F.prop==0)[0] diabar = where(F.prop==1)[0] ############### #Creating nodesets ################### nnod=M.ncoords() nlist=arange(nnod) count = zeros(nnod) for n in M.elems.flat: count[n] += 1 field = nlist[count==8] topedge = nlist[count==7] topcorner = nlist[count==6] bottomedge = nlist[count==5] bottomcorner = nlist[count==3] support = concatenate([bottomedge,bottomcorner]) edge = concatenate([topedge,topcorner]) ######################## #Defining and assigning the properties ############################# Q = 0.5*q*dx*dx P = PropertyDB() P.nodeProp(set=field,cload = [0,0,Q,0,0,0]) P.nodeProp(set=edge,cload = [0,0,Q/2,0,0,0]) P.nodeProp(set=support,bound = [1,1,1,0,0,0]) circ20 = ElemSection(section={'name':'circ20','sectiontype':'Circ','radius':10, 'cross_section':314.159}, material={'name':'S500', 'young_modulus':210000, 'shear_modulus':81000, 'poisson_ratio':0.3, 'yield_stress' : 500,'density':0.000007850}) # example of how to set the element type by set P.elemProp(set=topbar,section=circ20,eltype='T3D2') P.elemProp(set=bottombar,section=circ20,eltype='T3D2') # alternatively, we can specify the elements by an index value # in an array that we will pass in the Abqdata 'eprop' argument P.elemProp(prop=1,section=circ20,eltype='T3D2') # Since all elements have same characteristics, we could just have used: # P.elemProp(section=circ20,elemtype='T3D2') # But putting the elems in three sets allows for separate postprocessing # Print node and element property databases for p in P.nprop: print p for p in P.eprop: print p ############# #Writing the inputfile ################### step = Step() out = Output(type='field',variable='preselect') res = [ Result(kind='element',keys=['S']), Result(kind='node',keys=['U']) ] model = Model(M.coords,M.elems) if not checkWorkdir(): exit() AbqData(model,P,[step],eprop=F.prop,out=[out],res=res).write('SpaceTruss') # End pyformex-0.8.6/pyformex/examples/Viewports.py0000644000211500021150000000512611705104656021306 0ustar benebene00000000000000# $Id: Viewports.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Viewports.py level = 'advanced' topics = ['surface'] techniques = ['viewport', 'color'] Demonstrate multiple viewports. """ def atExit(): print "EXITING" layout(1) reset() nsl = 0 F = Formex.read(getcfg('datadir')+'/horse.pgf') layout(1) FA = draw(F,view='front') drawText('Viewport 0',20,20,size=20) pause('NEXT: Create Viewport 1') layout(2) drawText('Viewport 1',20,20,size=20) pf.GUI.viewports.updateAll() pause('NEXT: Create Viewport 2') layout(3) draw(F,color='green') pause('NEXT: Link Viewport 2 to Viewport 0') linkViewport(2,0) pf.GUI.viewports.updateAll() pause('NEXT: Create 4 Viewports all linked to Viewport 0') layout(4,2) viewport(0) for i in range(1,4): linkViewport(i,0) pause('NEXT: Change background colors in the viewports') colors=['indianred','olive','coral','yellow'] for i,v in enumerate(['front','right','top','iso']): viewport(i) view(v) bgcolor(colors[i]) pf.canvas.setBgColor(pf.canvas.settings.bgcolor) pf.canvas.display() pf.canvas.update() pause('NEXT: Cut the horse in viewport 3, notice results visible in all') viewport(3) G = F.cutWithPlane([0.,0.,0.],[-1.,0.,0.],side='+') clear() draw(G) # this draws in the 4 viewports ! pf.GUI.viewports.updateAll() pause('NEXT: DONE') exit() sleep(nsl) smooth() pf.GUI.viewports.updateAll() exit() from gui import canvas sleep(nsl) canvas.glLine() canvas.glFlat() pf.GUI.viewports.updateAll() #End pyformex-0.8.6/pyformex/examples/Carpetry.py0000644000211500021150000001106111705104656021070 0ustar benebene00000000000000# $Id: Carpetry.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Carpetry level = 'normal' topics = ['mesh','illustration','surface'] techniques = ['color','random'] .. Description Carpetry -------- This example illustrates the use of the Mesh conversion techniques and the creation of colored value plots on surfaces. """ from plugins import trisurface,surface_menu from elements import * def atExit(): pf.cfg['gui/autozoomfactor'] = saved_autozoomfactor pf.GUI.setBusy(False) def drawMesh(M): clear() draw(M) drawText("%s %s elements" % (M.nelems(),M.eltype),20,20,size=20) # make sure this is a good aspect ratio if you want a movie nx,ny = 4,3 saved_autozoomfactor = pf.cfg['gui/autozoomfactor'] pf.GUI.setBusy() pf.cfg['gui/autozoomfactor'] = 2.0 clear() view('front') smoothwire() transparent() M = Formex(origin()).extrude(nx,1.,0).extrude(ny,1.,1).toMesh().setProp(1) V = surface_menu.SelectableStatsValues possible_keys = [ k for k in V.keys() if not V[k][1] ][:-1] nkeys = len(possible_keys) maxconv = 10 minconv = 5 minelems = 10000 maxelems = 50000 save = False def carpet(M): conversions = [] nconv = random.randint(minconv,maxconv) while (len(conversions) < nconv and M.nelems() < maxelems) or M.nelems() < minelems: possible_conversions = M.eltype.conversions.keys() i = random.randint(len(possible_conversions)) conv = possible_conversions[i] conversions.append(conv) #clear() #draw(M) #print "%s -> %s" % (M.eltype,conv) M = M.convert(conv) #print "type %s, plex %s" % (M.eltype,M.nplex()) if M.eltype != Tri3: M = M.convert('tri3') conversions.append('tri3') print "%s patches" % M.nelems() print "conversions: %s" % conversions # Coloring key = possible_keys[random.randint(nkeys)] print "colored by %s" % key func = V[key][0] S = trisurface.TriSurface(M) val = func(S) export({'surface':S}) surface_menu.selection.set(['surface']) surface_menu.showSurfaceValue(S,str(conversions),val,False) pf.canvas.removeDecorations() clear() flatwire() lights(True) transparent(False) if pf.interactive: canvasSize(nx*200,ny*200) #canvasSize(720,576) print "running interactively" n = 1#ask("How many?",['0','1000','100','10','1']) n = int(n) save = False#ack("Save images?") if save: image.save(filename='Carpetry-000.jpg',window=False,multi=True,hotkey=False,autosave=False,border=False,rootcrop=False,format=None,quality=95,verbose=False) A = None for i in range(n): carpet(M) B = pf.canvas.actors[-1:] if A: undraw(A) A = B if save: image.saveNext() else: import sys print sys.argv print argv canvasSize(nx*200,ny*200) print "just saving image" from gui import image,guimain carpet(M) image.save('testje2.png') #exit(all=True) guimain.quitGUI() ## print "ATEXIT" ## from PyQt4 import QtGui ## print "current:%s" % pf.GUI.viewports.current.size() ## print "max:%s" % pf.GUI.viewports.current.maximumSize() ## pf.GUI.viewports.current.setMaximumSize(1000,2000) ## pf.GUI.central.setSizePolicy(QtGui.QSizePolicy.Maximum,QtGui.QSizePolicy.Maximum) ## pf.GUI.central.resize(pf.GUI.central.size().width()+0,pf.GUI.central.size().height()+0) ## pf.GUI.viewports.activate() ## pf.GUI.resize(pf.GUI.size().width()+0,pf.GUI.size().height()+0) ## pf.GUI.update() # End pyformex-0.8.6/pyformex/examples/Lamella.py0000644000211500021150000000364111705104656020653 0ustar benebene00000000000000# $Id: Lamella.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Lamella Dome level = 'beginner' topics = ['geometry','domes'] techniques = ['color'] """ clear() nx=12 # number of modules in circumferential direction ny=8 # number of modules in meridional direction rd=100 # radius of the sphere cap t=50 # slope of the dome at its base (= half angle of the sphere cap) a=2 # size of the top opening rings=False # set to True to include horizontal rings e1 = Formex([[[0,0],[1,1]]],1).rosette(4,90).translate([1,1,0]) # diagonals e2 = Formex([[[0,0],[2,0]]],0) # border f1 = e1.replic2(nx,ny,2,2) if rings: f2 = e2.replic2(nx,ny+1,2,2) else: f2 = e2.replic2(nx,2,2,2*ny) g = (f1+f2).translate([0,a,1]).spherical(scale=[180/nx,t/(2*ny+a),rd],colat=True) draw(e1+e2) draw(f1+f2) clear() draw(g) pyformex-0.8.6/pyformex/examples/FeAbq.py0000644000211500021150000001541611705104656020265 0ustar benebene00000000000000# $Id: FeAbq.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """FeAbq level = 'advanced' topics = ['FEA'] techniques = ['persistence', 'dialog', 'color'] """ from plugins.fe import * from plugins.properties import * from plugins.fe_abq import * def quad(): """Return a unit quadrilateral Formex.""" return Formex('4:0123') def triquad(): """Return a triangularized unit quadrilateral Formex.""" return Formex('3:012934') na,ma = 4,2 # Size of domain A nb,mb = 3,4 # size of domain B # Make sure the property numbers never clash! pa,pb,pc = 3,4,5 # Properties corresponding to domains A,B,C pb1 = 2 # Property for part of domain B pbc,pld = 1,6 # Properties corresponding to boundary/loaded nodes A = triquad().replic2(na,ma,1,1).setProp(pa) B = quad().replic2(nb,mb,1,1).translate([na,0,0]).setProp(pb) # Change every second element of B to property pb1 B.prop[arange(B.prop.size) % 2 == 1] = pb1 C = A.rotate(90).setProp(pc) parts = [A,B,C] # Create the finite element model # A model contains a single set of nodes and one or more sets of elements M = mergedModel([p.toMesh() for p in parts]) # Create a Formex with the nodes, mostly for drawing F = Formex(M.coords) def printModel(M): """print the model M""" print "===================\nMERGED MODEL" print "NODES" print M.coords for i,e in enumerate(M.elems): print "PART %s" %i print e print "===================" def drawModel(M,nodes=True,elems=True,nodenrs=True,elemnrs=True): """draw the model M""" smoothwire() lights(False) transparent() clear() print F.prop if nodes or nodenrs: ## F = Formex(M.coords) if nodes: draw(F) if nodenrs: drawNumbers(F) if elems or elemnrs: ## G = [Formex(M.coords[e],i+1) for i,e in enumerate(M.elems)] G = parts if elems: draw(G) if elemnrs: [ drawNumbers(i) for i in G ] zoomAll() drawModel(M) # Transfer the properties from the parts in a global set elemprops = concatenate([part.prop for part in parts]) # Create the property database P = PropertyDB() # In this simple example, we do not use a material/section database, # but define the data directly steel = { 'name': 'steel', 'young_modulus': 207000, 'poisson_ratio': 0.3, 'density': 0.1, # Not Used, but Abaqus does not like a material without } thin_plate = { 'name': 'thin_plate', 'sectiontype': 'solid', 'thickness': 0.01, 'material': 'steel', } medium_plate = { 'name': 'thin_plate', 'sectiontype': 'solid', 'thickness': 0.015, 'material': 'steel', } thick_plate = { 'name': 'thick_plate', 'sectiontype': 'solid', 'thickness': 0.02, 'material': 'steel', } print thin_plate print medium_plate print thick_plate # Create element sets according to the properties pa,pb,pb1,pc: esets = {} esets.update([(v,where(elemprops==v)[0]) for v in [pa,pb,pb1,pc]]) # Set the element properties P.elemProp(set=esets[pa],eltype='CPS3',section=ElemSection(section=thin_plate,material=steel)) P.elemProp(set=esets[pb],eltype='CPS4',section=ElemSection(section=thick_plate,material=steel)) P.elemProp(set=esets[pb1],eltype='CPS4',section=ElemSection(section=thick_plate,material=steel)) P.elemProp(set=esets[pc],eltype='CPS3',section=ElemSection(section=medium_plate,material=steel)) print "Element properties" for p in P.getProp('e'): print p # Set the nodal properties xmin,xmax = M.coords.bbox()[:,0] bnodes = where(M.coords.test(min=xmax-0.01))[0] # Right end nodes lnodes = where(M.coords.test(max=xmin+0.01))[0] # Left end nodes print "Boundary nodes: %s" % bnodes print "Loaded nodes: %s" % lnodes P.nodeProp(tag='init',set=bnodes,bound=[1,1,0,0,0,0]) P.nodeProp(tag='step1',set=lnodes,name='Loaded',cload=[-10.,0.,0.,0.,0.,0.]) P.nodeProp(tag='step2',set='Loaded',cload=[-10.,10.,0.,0.,0.,0.]) F.setProp(0) F.prop[bnodes] = pbc F.prop[lnodes] = pld print "Node properties" for p in P.getProp('n'): print p drawModel(M,elems=False) while ack("Renumber nodes?"): # renumber the nodes randomly old,new = M.renumber() drawModel(M) if widgets.input_timeout > 0: break # Request default output plus output of S in elements of part B. # If the abqdata are written with group_by_group==True (see at bottom), # all elements of each group in elems will be collected in a set named # Eset('grp',index), where index is the index of the group in the elems list. # Thus, all elements of part B will end up in Eset('grp',1) out = [ Output(type='history'), Output(type='field'), Output(type='field',kind='element',set=Eset('grp',1),keys=['S']), ] # Create requests for output to the .fil file. # - the displacements in all nodes # - the stress components in all elements # - the stresses averaged at the nodes # - the principal stresses and stress invariants in the elements of part B. # (add output='PRINT' to get the results printed in the .dat file) res = [ Result(kind='NODE',keys=['U']), Result(kind='ELEMENT',keys=['S']), Result(kind='ELEMENT',keys=['S'],pos='AVERAGED AT NODES'), Result(kind='ELEMENT',keys=['SP','SINV'],set=Eset('grp',1)), ] # Define steps (default is static) step1 = Step(time=[1., 1., 0.01, 1.],tags=['step1']) step2 = Step(time=[1., 1., 0.01, 1.],tags=['step2']) # collect all data # # !! currently output/result request are global to all steps # !! this will be changed in future # all = AbqData(M,prop=P,steps=[step1,step2],out=out,res=res,bound=['init']) if ack('Export this model in ABAQUS input format?',default='No'): fn = askNewFilename(filter='*.inp') if fn: all.write(jobname=fn,group_by_group=True) # End pyformex-0.8.6/pyformex/examples/BeamFreq.py0000644000211500021150000001257211705104656020771 0ustar benebene00000000000000# $Id: BeamFreq.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """BeamFreq level = 'normal' topics = ['FEA','curve','drawing'] techniques = ['external','viewport',] .. Description BeamFreq -------- This example shows the first natural vibration modes of an elastic beam. It requires an external program, calix, which can be downloaded from ftp://bumps.ugent.be/pub/calix/ Make sure you have version 1.5-a8 or higher. """ from plugins.curve import * import simple _required_calix_version = '1.5-a8' print utils.checkVersion('calix',_required_calix_version,True) if utils.checkVersion('calix',_required_calix_version,True) < 0: showText(""".. Error ----- An error occurred when I tried to find the program 'calix'. This probably means that calix is not installed on your system, or that the installed version is not one I can use for this example. Calix is a free program and you can install it as follows: - download calix (%s or higher) from ftp://bumps.ugent.be/pub/calix/ - unpack, compile and install (as root):: tar xvzf calix-%s.tar.gz cd calix-1.5 make (sudo) make install """ % (_required_calix_version,_required_calix_version)) exit() n = 16 nshow = 4 bcons = ['cantilever','simply supported'] verbose = False res = askItems([ ('n',n,{'text':'number of elements along beam'}), ('nshow',nshow,{'text':'number of natural modes to show'}), ('bcon',bcons[0],{'text':'beam boundary conditions','choices':bcons}), ('verbose',verbose,{'text':'show intermediate information'}), ]) if not res: exit() globals().update(res) F = simple.line([0.,0.,0.],[0.,1.,0.],n) M = F.toMesh() draw(M) nnod = M.ncoords() nel = M.nelems() nmat = 1 iout = 1 # init s=""";calix script written by pyFormex (example BeamFreq) start use program 'frame.cal' endtext """ # params s += " %s %s %s %s\n" % (nnod+1,nel,nmat,iout) # nodes for i,x in enumerate(M.coords): s += "%5d%10.3e%10.3e%10.3e\n" % ((i+1,)+tuple(x)) # orientation node s += "%5d%10.3e%10.3e%10.3e\n\n" % (nnod+1,0.0,0.0,1.0) # boundary conditions s += "%5s 0 1 1 1 1 0%5s 1\n" % (2,nnod-2) s += "%5s 1 1 1 1 1 1\n" % (nnod+1) if bcon == 'cantilever': # boundary conditions for cantilever s += "%5s 1 1 1 1 1 1\n" % (1) s += "%5s 0 1 1 1 1 0\n" % (nnod) else: # boundary conditions for simply supported s += "%5s 1 1 1 1 1 0\n" % (1) s += "%5s 1 1 1 1 1 0\n" % (nnod) s += '\n' # material s += " 3.d6 1.2d6 1.00 3000. 1.00 70.d4 110.d4\n" # elems fmt = "%5s"*(M.nplex()+3) + '\n' for i,e in enumerate(M.elems+1): s += fmt % ((i+1,1)+tuple(e)+(nnod+1,)) # action and output in a format we can easily read back s += """ exec frame_ev endtext intvar name nnod 1 intvar name ndof 7 file open 'test.out' write seq 17 user printf '(i5)' nnod $17 user printf '(i5)' ndof $17 user printf '(5g13.4)' EIG $17 user printf '(5g13.4)' DISPL $17 file close $17 stop """ #print s fil = open('temp.dta','w') fil.write(s) fil.close() if verbose: # show calix input data showFile('temp.dta') # run calix cmd = "calix temp.dta temp.res" if os.path.exists('test.out'): os.remove('test.out') sta,out = utils.runCommand(cmd) if verbose: # show calix output showText(out) showFile('temp.res') showFile('test.out') # read results from eigenvalue analysis fil = open('test.out','r') nnod,ndof = fromfile(fil,sep=' ',count=2,dtype=int) eig = fromfile(fil,sep=' ',count=4*ndof).reshape(ndof,4) nshow = min(nshow,ndof) freq = eig[:nshow,2] basefreq = freq[0] print "Frequencies: %s" % freq print "Multipliers: %s" % (freq/freq[0]) a = fromfile(fil,sep=' ',).reshape(-1,nnod,6) #print a.shape # remove the extra node a = a[:,:-1,:] #print a.shape layout(nshow,ncols=4) hscale = 0.5 def drawDeformed(M,u,r): xd = M.coords.copy() xd[:,0] += u c = NaturalSpline(xd) draw(c,color=red) draw(c.pointsOn()) for i in range(nshow): viewport(i) clear() transparent(False) lights(False) linewidth(2) draw(M) ai = a[i] u = ai[:,0] imax = argmax(abs(u)) r = ai[:,5] sc = hscale / u[imax] u *= sc r *= sc #print u,r drawDeformed(M,u,r) fi = freq[i] mi = fi/freq[0] drawText('%s Hz = %.2f f0' % (fi,mi),20,20,size=20) # End pyformex-0.8.6/pyformex/examples/WireStent_calpy.py0000644000211500021150000001603511705104656022421 0ustar benebene00000000000000# $Id: WireStent_calpy.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Wire stent analysis level = 'advanced' topics = ['FEA'] techniques = ['color'] """ ############################ # Load the needed calpy modules from plugins import calpy_itf from calpy.fe_util import * from calpy.beam3d import * ############################ ############################################ # Create geometry from pyformex.examples.WireStent import DoubleHelixStent import datetime # create a Doublehelix stent stent_diameter = 10. stent_length = 150. wire_diameter = 0.2 number_wires = 6 pitch_angle = 30. # during testing stent_length = 10. stent = DoubleHelixStent(stent_diameter,stent_length, wire_diameter,number_wires,pitch_angle,nb=1).getFormex() if pf.options.gui: # draw it reset() clear() draw(stent,view='iso') ############################################ # Perform Analysis # Create output file if not checkWorkdir(): print "Could not open a file for writing. I have to stop here" exit() outfilename = 'WireStent_calpy.out' outfile = open(outfilename,'w') message("Output is written to file '%s' in %s" % (outfilename,os.getcwd())) stdout_saved = sys.stdout sys.stdout = outfile print "# File created by pyFormex on %s" % time.ctime() print "# Script name: %s" % pf.scriptName nel = stent.nelems() print "Number of elements: %s" % nel print "Original number of nodes: %s" % stent.nnodes() # Create FE model message("Creating Finite Element model: this may take some time.") nodes,elems = stent.fuse(nodesperbox=1) nnod = nodes.shape[0] print "Compressed number of nodes: %s" % nnod # Create an extra node on the axis for beam orientations extra_node = array([[0.0,0.0,-10.0]]) coords = concatenate([nodes,extra_node]) nnod = coords.shape[0] print "After adding a node for orientation: %s" % nnod # Create element definitions: i j k matnr, where k = nnod (the extra node) # while incrementing node numbers with 1 (for calpy) # (remember props are 1,2,3, so are OK) thirdnode = nnod*ones(shape=(nel,1),dtype=int) matnr = reshape(stent.prop,(nel,1)) elements = concatenate([elems+1,thirdnode,matnr],1) # Create endnode sets (with calpy numbering) bb = stent.bbox() zlo = bb[0][2] zhi = bb[1][2] zmi = (zhi+zlo)/2. count = zeros(nnod) for n in elems.flat: count[n] += 1 unconnected = arange(nnod)[count==1] zvals = nodes[unconnected][:,2] #print zlo,zhi,zmi,zvals end0 = unconnected[zvalszmi] print "Nodes at end 0:",end0 print "Nodes at end 1:",end1 # Create End Connectors to enforce radial boundary conditions coords_end0 = coords[end0] extra_nodes = coords_end0 * array([0.80,0.80,1.0]) nnod0 = nnod coords = concatenate([coords,extra_nodes]) nnod = coords.shape[0] print "Nodes added for boundary connectors: %s" % (nnod-nnod0) print "Final number of nodes: %s" % nnod extra_elems = zeros((nnod-nnod0,4),dtype=int) end0_ext = arange(nnod0,nnod) extra_elems[:,0] = end0_ext + 1 extra_elems[:,1] = end0 + 1 extra_elems[:,2] = nnod0 extra_elems[:,3] = 4 # Extra elements have matnr 4 print extra_elems elements = concatenate([elements,extra_elems]) # Boundary conditions s = "" for n in end0_ext + 1: # NOTICE THE +1 ! s += " %d 1 1 1 1 1 1\n" % n # Also clamp the fake extra node s += " %d 1 1 1 1 1 1\n" % nnod0 print "Specified boundary conditions" print s bcon = ReadBoundary(nnod,6,s) NumberEquations(bcon) print bcon # Materials (E, G, rho, A, Izz, Iyy, J) mats = zeros((4,7),float) A = math.pi * wire_diameter ** 2 Izz = Iyy = math.pi * wire_diameter ** 4 / 4 J = math.pi * wire_diameter ** 4 / 2 E = 207000. nu = 0.3 G = E/2/(1+nu) rho = 0. mats[0] = mats[2] = [ E, G, rho, A, Izz, Iyy, J ] mats[1] = [E, G, 0.0, A*10**3, Izz*10**6, Iyy*10**6, 0.0] mats[3] = [E, G, 0.0, 0.0, Izz*10**6, Iyy*10**6, 1.0] print mats # Create loads nlc = 1 ndof = bcon.max() loads = zeros((ndof,nlc),float) zforce = [ 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 ] for n in end1: # NO +1 HERE! loads[:,0] = AssembleVector(loads[:,0],zforce,bcon[n,:]) # Perform analysis import calpy calpy.options.optimize=True print elements displ,frc = static(coords,bcon,mats,elements,loads,Echo=True) print "# Analysis finished on %s" % time.ctime() sys.stdout = stdout_saved outfile.close() ################################ #Using pyFormex as postprocessor ################################ if pf.options.gui: from gui.colorscale import * import gui.decors # Creating a formex for displaying results is fairly easy elems = elements[:,:2]-1 results = Formex(coords[elems]) clear() draw(results,color='black') # Now try to give the formex some meaningful colors. # The frc array returns element forces and has shape # (nelems,nforcevalues,nloadcases) # In this case there is only one resultant force per element (the # normal force), and only load case; we still need to select the # scalar element result values from the array into a onedimensional # vector val. val = frc[:,0,0] # create a colorscale CS = ColorScale([blue,yellow,red],val.min(),val.max(),0.,2.,2.) cval = array(map(CS.color,val)) #aprint(cval,header=['Red','Green','Blue']) clear() draw(results,color=cval) bgcolor('lightgreen') linewidth(3) x = pf.canvas.width()//2 TA = drawText('Normal force in the members',x,100,font='tr32') CL = ColorLegend(CS,100) CLA = decors.ColorLegend(CL,10,10,30,200) decorate(CLA) sleep(1) # and a deformed plot on multiple scales dscales = arange(1,6) * 1.0 loadcase = 0 for dscale in dscales: dcoords = coords + dscale * displ[:,0:3,loadcase] clear() decorate(CLA) decorate(TA) linewidth(1) draw(results,color='darkgreen',wait=False) linewidth(3) deformed = Formex(dcoords[elems]) draw(deformed,color=cval) drawText('Deformed geometry (scale %.2f)' % dscale,x,70) if ack("Show the output file?"): showFile(outfilename) # End pyformex-0.8.6/pyformex/examples/Rubik.py0000644000211500021150000002274011705104656020361 0ustar benebene00000000000000# $Id: Rubik.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """Rubik level = 'normal' topics = ['illustration','game'] techniques = ['color','dialog','draw','persistence','random'] createdby = ['tpraet'] .. Description Rubik's cube ============ Overview -------- This example illustrates how the user can interact with the canvas. Hereto a Rubik's cube with a user definable number of rows is shown. To rotate a plane of the cube, you just press the left mouse button and move it in the appropriate direction. This operation overrides the normal left mouse button action, which is to rotate the model as a whole. This operation is still available however, when depressing the ALT button together with the left mouse button movement. Indeep ------ The script creates an (n,n,n) cube as a Formex. The number n can be set from the user dialog. Randomize the colors with the **Shuffle** button. When the mouse is moved with the left mouse button (LMB) depressed, the function ``turn`` is executed. When the LMB is pressed, the current location of the mouse cursor is registered and the cube element (i.e. a quadrilateral of the Formex) closest to this position is registered. When the LMB is released, the current position of the cursor is compared to the former position. If the cursor position has changed, the selected element is rotated in the direction determined by the projection of the vector created by the position change, rotated over the current view rotation and projected onto the plane of the selected element. The cubes can be shuffled into a random position and solved subsequently. The maximum number of cubes in one row is limited to ten because of the enormous number of permutations possible, and thus the large amount of time needed to solve such large cubes. For a 7x7 cube for example, the total number of permutations is already higher than the assumed total number of atoms in the universe (eh ... that's something like ten to the power 80, but we might be way off). You can check the exact number of possible permutations for the displayed cube by pressing the **Permutations** button. This number is not stored, it is calculated each time. It's a nice example of how easily Python can handle huge numbers. """ from gui.widgets import simpleInputItem as I from gui.viewport import * from numpy.random import rand # General definitions def createCube(n=3): tol = 0.005 front = Formex('4:0123').replic2(n,n).translate([-n/2.,-n/2.,-n/2.]) sides = front+front.translate(2,n) darkPosTol = Formex('4:0123').replic2(n,n).translate([-n/2.,-n/2.,0.]).scale(1.-5.*tol/n).translate(2, -n/2.+1+tol) darkNegTol = darkPosTol.translate(2, -2*tol) dark = darkPosTol.replic(n-1, 1, dir=2) + darkNegTol.replic(n-1, 1, dir=2) cube = sides + sides.rotate(90,1) + sides.rotate(90,0) + dark + dark.rotate(90, 0) + dark.rotate(90, 1) cube.prop = append(repeat(array([5,1,3,2,6,7]),n**2), repeat(array([0]), 6*(n-1)*n**2)) return cube def refresh(): """Refresh the cube on the canvas""" global drawn clear() drawn = draw(cube) # Rotation definitions def turn(x=0,y=0,action=0): """Execute a rotation when mouse moved with left button pressed""" global busy, x1, y1, element if action==PRESS and not busy: busy = True pf.canvas.setCursorShape('pick') x1, y1 = x, y busy = False element = selectElement(pf.canvas, x, y, 2, 2) if element == [-1]: message('No element selected.\nPlease select an element of the cube.') # else: # draw(cube[element], color=red, bbox='last', linewidth=5.0) busy = False if action==RELEASE and not busy: busy = True pf.canvas.setCursorShape('default') if element != [-1] and x1!=-1: x2, y2 = x, y dx = float(x2-x1) dy = float(y2-y1) if dx == 0 and dy == 0: busy = False return x1 = -1 v = [dx, dy, 0] rot = pf.canvas.camera.rot[:3, :3] v2 = dot(v, linalg.inv(rot)) V = v2/sqrt(dot(v2,v2.conj())) centers = cube.centroids() P1 = centers[element][0] # draw(Formex([[P1, P1+V]]), color=red, bbox='last', linewidth=3.0) planeAxis = argsort(abs(P1))[-1] pos = P1[planeAxis]>0 rotateCube(cube, planeAxis, pos, P1, V) busy = False def selectElement(self, x, y, w, h): """Returns the element closest to the cursor position""" self.selection.clear() self.selection.setType('element') self.selection_filter = None self.pick_window = (x,y,w,h,GL.glGetIntegerv(GL.GL_VIEWPORT)) self.pick_parts('element', 54, store_closest=True) if len(self.picked) != 0: self.selection_front = self.closest_pick self.selection.set([self.closest_pick[0]]) self.update() try: return self.selection[0] except: return [-1] def rotateCube(self, planeAxis, pos, P, V, view=True): """Determine which elements should rotate in which direction.""" tol = 0.001 V[planeAxis] = 0. sorted = argsort(abs(V)) rotAxis, dirAxis = int(sorted[1]), int(sorted[2]) if (rotAxis+1) % 3 != dirAxis: dir = (pos==(V[dirAxis]>0)) else: dir = not (pos==(V[dirAxis]>0)) centers = self.centroids() rowElements = where(abs(centers[:, rotAxis]-P[rotAxis]) 1: message("This curve is not a closed circumference") return None sorted = conn[0] print "Sorted elements:",sorted showInfo('Click to continue') clear() M = Mesh(M.coords,sorted) drawNumbers(M) return M.toFormex() clear() flat() reset() examples = { 'Square' : square_example, 'Rectangle' : rectangle_example, 'Circle' : circle_example, 'CloseLoop' : close_loop_example, } from gui.widgets import simpleInputItem as I res = askItems([ I('example',text='Select an example',choices=examples.keys()), ]) if res: F = examples[res['example']]() if F is None: exit() draw(F) S = sectionChar(F) S.update(extendedSectionChar(S)) print mydict.CDict(S) G = Formex([[[S['xG'],S['yG']]]]) draw(G,bbox='last') showaxes([S['xG'],S['yG'],0.],S['alpha'],F.dsize(),'red') # End pyformex-0.8.6/pyformex/examples/KochLine.py0000644000211500021150000000407211705104656020777 0ustar benebene00000000000000# $Id: KochLine.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Koch line level = 'beginner' topics = ['geometry'] techniques = ['color'] """ from plugins.lima import lima wireframe() linewidth(2) n = 6 # number of generations # We use the lima module to create six generations of the Koch line F = [ Formex(lima("F",{"F":"F*F//F*F"},i, { 'F' : 'fd();', '*' : 'ro(60);', '/' : 'ro(-60);' }),i) for i in range(n) ] # and display them in series clear() # scale each Formex individually to obtain same length sc = [ 3**(-i) for i in range(n) ] sz = sc[0]/3. F = [F[i].scale(sc[i]) for i in range(n)] mode = random.randint(3) if mode == 0: # on top of each other draw([F[i].translate([0,sz*(i-1),0]) for i in range(n)]) elif mode == 1: # one above the other draw([F[i].translate([0,sz*n,0]) for i in range(n)]) else: # as radii of an n-pointed star draw([F[i].rotate(360.*i/n) for i in range(n)]) zoomAll() # End pyformex-0.8.6/pyformex/examples/Grid.py0000644000211500021150000000464211705104656020173 0ustar benebene00000000000000# $Id: Grid.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Grid level = 'beginner' topics = ['geometry'] techniques = ['dialog'] """ import gui.actors def base(type,m,n=None): """A regular pattern for type. type = 'tri' or 'quad' or 'triline' or 'quadline' m = number of cells in direction 0 n = number of cells in direction 1 """ n = n or m if type == 'triline': return Formex('l:164').replic2(m,n,1,1,0,1,0,-1) elif type == 'quadline': return Formex('l:2').replic2(m+1,n,1,1) + \ Formex('l:1').replic2(m,n+1,1,1) elif type == 'tri': return Formex('3:012934').replic2(m,n) elif type == 'quad': return Formex('4:0123').replic2(m,n) else: raise ValueError,"Unknown type '%s'" % str(type) # This is the old input format, but relies on auto-conversion res = askItems([('nx',4),('ny',3),('nz',2),('Grid type','','select',{'choices':['Box','Plane']}),('alpha',0.3)]) if not res: exit() nx = (res['nx'],res['ny'],res['nz']) gridtype = res['Grid type'] alpha = res['alpha'] if gridtype == 'Box': GA = actors.GridActor(nx=nx,linewidth=0.2,alpha=alpha) else: GA = actors.CoordPlaneActor(nx=nx,linewidth=0.2,alpha=alpha) smooth() pf.canvas.addActor(GA) pf.canvas.setBbox(GA.bbox()) zoomAll() pf.canvas.update() pyformex-0.8.6/pyformex/timer.py0000644000211500021150000000632111705104656016604 0ustar benebene00000000000000#!/usr/bin/python # $Id: timer.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A timer class.""" from datetime import datetime class Timer(object): """A class for measuring elapsed time. A Timer object measures elapsed real time since a specified time, which by default is the time of the creation of the Timer. Parameters: - `start`: a datetime object. If not specified, the time of the creation of the Timer is used. """ def __init__(self,start=None): """Create and start a timer.""" self.reset(start) def reset(self,start=None): """(Re)Start the timer. Sets the start time of the timer to the specified value, or to the current time by default. Parameters: - `start`: a datetime object. If not specified, the current time as returned by datetime.now() is used. """ if isinstance(start,datetime): self.start = start else: self.start = datetime.now() def read(self,reset=False): """Read the timer. Returns the elapsed time since the last reset (or the creation of the timer) as a datetime.timedelta object. If reset=True, the timer is reset to the time of reading. """ now = datetime.now() ret = now - self.start if reset: self.start = now return ret def seconds(self,reset=False,rounded=True): """Return the timer readings in seconds. The default return value is a rounded integer number of seconds. With ``rounded == False``, a floating point value with granularity of 1 microsecond is returned. If reset=True, the timer is reset at the time of reading. """ e = self.read(reset) tim = e.days*24*3600 + e.seconds + e.microseconds / 1000000. if rounded: tim = int(round(tim)) return tim if __name__ == "__main__": import time t = Timer() time.sleep(14.2) r = t.read() print(r.days,r.seconds,r.microseconds) print(t.seconds()) print(t.seconds(False)) # End pyformex-0.8.6/pyformex/__init__.py0000644000211500021150000000620111705105304017207 0ustar benebene00000000000000# $Id: __init__.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """pyFormex core module initialisation. This module initializes the pyFormex global variables and defines a few essential functions. """ # This is the very first thing that is executed when starting pyFormex # It is loaded even before main. __version__ = "0.8.6" __revision__ = '2151M' Version = 'pyFormex %s' % __version__ Copyright = 'Copyright (C) 2004-2012 Benedict Verhegghe' Url = 'http://pyformex.org' Description = "pyFormex is a tool for generating, manipulating and transforming large geometrical models of 3D structures by sequences of mathematical transformations." svnversion = False # The GUI parts app_started = False interactive = False app = None # the Qapplication GUI = None # the GUI QMainWindow canvas = None # the OpenGL Drawing widget controlled by the running script #board = None # the message board # set start date/time import time,datetime StartTime = datetime.datetime.now() # initialize some global variables used for communication between modules options = None # the options found on the command line print_help = None # the function to print(the pyformex help text (pyformex -h)) cfg = {} # the current session configuration prefcfg = None # the preferenced configuration refcfg = None # the reference configuration preffile = None # the file where the preferenced configuration will be saved PF = {} # explicitely exported globals #_PF_ = {} # globals that will be offered to scripts scriptName = None # define last rescue versions of message, warning and debug def message(s): print(s) warning = message def debug(s,lead="DEBUG",level=-1): """Print a debug message""" try: # to make sure that debug() can be used before options are set if options.debug < 0 or (options.debug % level > 0): raise pass except: print("%s: %s" % (lead,str(s))) def debugt(s): """Print a debug message with timer""" debug(s,time.time()) ### End pyformex-0.8.6/pyformex/script.py0000644000211500021150000005442011705104656016773 0ustar benebene00000000000000# $Id: script.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Basic pyFormex script functions The :mod:`script` module provides the basic functions available in all pyFormex scripts. These functions are available in GUI and NONGUI applications, without the need to explicitely importing the :mod:`script` module. """ import pyformex as pf import formex import geomfile import utils from project import Project from geometry import Geometry ######################## # Imported here only to make it available in scripts from mesh import Mesh ######################## import threading,os,copy,re,time ######################### Exceptions ######################################### class _Exit(Exception): """Exception raised to exit from a running script.""" pass class _ExitAll(Exception): """Exception raised to exit pyFormex from a script.""" pass class _ExitSeq(Exception): """Exception raised to exit from a sequence of scripts.""" pass class _TimeOut(Exception): """Exception raised to timeout from a dialog widget.""" pass ############################# Globals for scripts ############################ def Globals(): """Return the globals that are passed to the scripts on execution. This basically contains the globals defined in draw.py, colors.py, and formex.py, as well as the globals from numpy. It also contains the definitions put into the pyformex.PF, by preference using the export() function. During execution of the script, the global variable __name__ will be set to either 'draw' or 'script' depending on whether the script was executed in the 'draw' module (--gui option) or the 'script' module (--nogui option). """ # :DEV it is not a good idea to put the pf.PF in the globals(), # because pf.PF may contain keys that are not acceptible as # Python names #g = copy.copy(pf.PF) g = {} g.update(globals()) if pf.GUI: from gui import colors,draw g.update(colors.__dict__) g.update(draw.__dict__) g.update(formex.__dict__) return g def export(dic): """Export the variables in the given dictionary.""" pf.PF.update(dic) def export2(names,values): """Export a list of names and values.""" export(dict(zip(names,values))) def forget(names): """Remove the global variables specified in list.""" g = pf.PF for name in names: if g.has_key(name): del g[name] def forgetAll(): """Delete all the global variables.""" pf.PF = {} def rename(oldnames,newnames): """Rename the global variables in oldnames to newnames.""" g = pf.PF for oldname,newname in zip(oldnames,newnames): if g.has_key(oldname): g[newname] = g[oldname] del g[oldname] def listAll(clas=None,like=None,filter=None,dic=None): """Return a list of all objects in dictionay that match criteria. - dic: a dictionary object, defaults to pyformex.PF - clas: a class name: if specified, only instances of this class will be returned - like: a string: if given, only object names starting with this string will be returned - filter: a function taking an object name as parameter and returning True or False. If specified, only objects passing the test will be returned. The return value is a list of keys from dic. """ if dic is None: dic = pf.PF names = dic.keys() if clas is not None: names = [ n for n in names if isinstance(dic[n],clas) ] if like is not None: names = [ n for n in names if n.startswith(like) ] if filter is not None: names = [ n for n in names if filter(n) ] return names def named(name): """Returns the global object named name.""" if pf.PF.has_key(name): dic = pf.PF # elif pf._PF_.has_key(name): # pf.debug("Found %s in pyformex._PF_" % name) # dic = pf._PF_ else: # raise NameError,"Name %s is in neither pyformex.PF nor pyformex._PF_" % name raise NameError,"Name %s is not in pyformex.PF" % name return dic[name] def getcfg(name): """Return a value from the configuration.""" return pf.cfg.get(name,None) #################### Interacting with the user ############################### def ask(question,choices=None,default=''): """Ask a question and present possible answers. If no choices are presented, anything will be accepted. Else, the question is repeated until one of the choices is selected. If a default is given and the value entered is empty, the default is substituted. Case is not significant, but choices are presented unchanged. If no choices are presented, the string typed by the user is returned. Else the return value is the lowest matching index of the users answer in the choices list. Thus, ask('Do you agree',['Y','n']) will return 0 on either 'y' or 'Y' and 1 on either 'n' or 'N'. """ if choices: question += " (%s) " % ', '.join(choices) choices = [ c.lower() for c in choices ] while 1: res = raw_input(question) if res == '' and default: res = default if not choices: return res try: return choices.index(res.lower()) except ValueError: pass def ack(question): """Show a Yes/No question and return True/False depending on answer.""" return ask(question,['Y','N']) == 0 def error(message): """Show an error message and wait for user acknowlegement.""" print("pyFormex Error: "+message) if not ack("Do you want to continue?"): exit() def warning(message): print("pyFormex Warning: "+message) if not ack("Do you want to continue?"): exit() def showInfo(message): print("pyFormex Info: "+message) ##def log(s): ## """Display a message in the terminal.""" ## print(s) # message is the preferred function to send text info to the user. # The default message handler is set here. # Best candidates are log/info message = pf.message def system(cmdline,result='output'): """Run a command and return its output. If result == 'status', the exit status of the command is returned. If result == 'output', the output of the command is returned. If result == 'both', a tuple of status and output is returned. """ sta,out = utils.system(cmdline) if result == 'status': return sta elif result == 'output': return out elif result == 'both': return sta,out ########################### PLAYING SCRIPTS ############################## sleep = time.sleep scriptThread = None exitrequested = False stepmode = False starttime = 0.0 pye = False scriptlock = set() def scriptLock(id): global scriptlock scriptlock |= set([id]) def scriptRelease(id): global scriptlock scriptlock -= set([id]) def executeScript(scr,glob): """Execute a Python script in specified globals.""" exec scr in glob def playScript(scr,name=None,filename=None,argv=[],pye=False): """Play a pyformex script scr. scr should be a valid Python text. There is a lock to prevent multiple scripts from being executed at the same time. This implies that pyFormex scripts can currently not be recurrent. If a name is specified, set the global variable pyformex.scriptName to it when the script is started. If a filename is specified, set the global variable __file__ to it. If step==True, an indefinite pause will be started after each line of the script that starts with 'draw'. Also (in this case), each line (including comments) is echoed to the message board. """ global exitrequested # (We only allow one script executing at a time!) # and scripts are non-reentrant global scriptThread if scriptThread is not None and scriptThread.isAlive(): pf.message("Not executing this script because another one is already running") return if len(scriptlock) > 0: pf.message("!!Not executing because a script lock has been set: %s" % scriptlock) return scriptLock('__auto__') exitrequested = False if pf.GUI: global stepmode,exportNames,starttime #pf.debug('GUI SCRIPT MODE %s'% (stepmode)) pf.GUI.drawlock.allow() pf.canvas.update() pf.GUI.actions['Play'].setEnabled(False) pf.GUI.actions['Continue'].setEnabled(True) pf.GUI.actions['Stop'].setEnabled(True) pf.app.processEvents() # Get the globals g = Globals() if pf.GUI: modname = 'draw' # by default, we run the script in the current GUI viewport pf.canvas = pf.GUI.viewports.current else: modname = 'script' g.update({'__name__':modname}) if filename: g.update({'__file__':filename}) g.update({'argv':argv}) # Make this directory available #pf._PF_ = g # Now we can execute the script using these collected globals exportNames = [] pf.scriptName = name exitall = False #starttime = time.clock() try: try: if pf.GUI and stepmode: step_script(scr,g,True) else: if pye: if type(scr) is file: scr = scr.read() + '\n' n = len(scr) // 2 scr = utils.mergeme(scr[:n],scr[n:]) if pf.options.executor: scriptThread = threading.Thread(None,executeScript,'script-0',(scr,g)) scriptThread.daemon = True print "OK, STARTING THREAD" scriptThread.start() print "OK, STARTED THREAD" else: exec scr in g except _Exit: print "EXIT FROM SCRIPT" pass except _ExitAll: exitall = True except: raise finally: # honour the exit function if g.has_key('atExit'): atExit = g['atExit'] try: atExit() except: pf.debug('Error while calling script exit function') if pf.cfg['autoglobals']: exportNames.extend(listAll(clas=Geometry,dic=g)) ## try: ## print "ENDRUN" ## print "g is %s" % id(g) ## print "pf.PF is %s" % id(pf.PF) ## print "pf.PF['Mesh-3'] is %s" % id(pf.PF['Mesh-3']) ## except: ## pass ## print exportNames pf.PF.update([(k,g[k]) for k in exportNames]) ## try: ## print "UPDATED" ## print "g is %s" % id(g) ## print "pf.PF is %s" % id(pf.PF) ## print "pf.PF['Mesh-3'] is %s" % id(pf.PF['Mesh-3']) ## except: ## pass scriptRelease('__auto__') # release the lock #elapsed = time.clock() - starttime #pf.debug('SCRIPT RUNTIME : %s seconds' % elapsed) if pf.GUI: stepmode = False pf.GUI.drawlock.release() # release the lock pf.GUI.actions['Play'].setEnabled(True) #pf.GUI.actions['Step'].setEnabled(False) pf.GUI.actions['Continue'].setEnabled(False) pf.GUI.actions['Stop'].setEnabled(False) if exitall: pf.debug("Calling quit() from playscript") quit() def force_finish(): global scriptlock,stepmode scriptlock = set() # release all script locks (in case of an error) stepmode = False def step_script(s,glob,paus=True): buf = '' for line in s: if buf.endswith('\\'): buf[-1:] = line break else: buf += line if paus and (line.strip().startswith('draw') or line.find('draw(') >= 0 or line.strip().startswith('view') or line.find('view(') >= 0 ): pf.GUI.drawlock.block() message(buf) exec(buf) in glob buf = '' showInfo("Finished stepping through script!") def breakpt(msg=None): """Set a breakpoint where the script can be halted on a signal. If an argument is specified, it will be written to the message board. The exitrequested signal is usually emitted by pressing a button in the GUI. """ global exitrequested if exitrequested: if msg is not None: pf.message(msg) exitrequested = False # reset for next time raise _Exit def raiseExit(): #print "EEEEEEEEEEEEXXXXXXXXXXXXXXXXIIIIIIIIIIIIIIIITTTTTTTTTTTTTTTTTTT" pf.debug("RAISED EXIT") #print scriptlock if pf.GUI: pf.GUI.drawlock.release() raise _Exit,"EXIT REQUESTED FROM SCRIPT" def enableBreak(mode=True): pf.GUI.actions['Stop'].setEnabled(mode) def stopatbreakpt(): """Set the exitrequested flag.""" global exitrequested exitrequested = True def playFile(fn,argv=[]): """Play a formex script from file fn. fn is the name of a file holding a pyFormex script. A list of arguments can be passed. They will be available under the name argv. This variable can be changed by the script and the resulting argv is returned to the caller. """ from timer import Timer t = Timer() message("Running script (%s)" % fn) pf.debug(" Executing with arguments: %s" % argv) res = playScript(file(fn,'r'),fn,fn,argv,fn.endswith('.pye')) pf.debug(" Arguments left after execution: %s" % argv) message("Finished script %s in %s seconds" % (fn,t.seconds())) return res def play(fn=None,argv=[],step=False): """Play the pyFormex script with filename `fn` or the current script file. This function does nothing if no filename is passed or no current scriptfile was set. If arguments are given, they are passed to the script. If `step` is True, the script is executed in step mode. """ global stepmode if not fn: if pf.GUI.canPlay: fn = pf.cfg['curfile'] else: return stepmode = step pf.GUI.history.add(fn) stepmode = step return playFile(fn,argv) def exit(all=False): """Exit from the current script or from pyformex if no script running.""" if len(scriptlock) > 0: if all: raise _ExitAll # ask exit from pyformex else: raise _Exit # ask exit from script only def quit(): """Quit the pyFormex program This is a hard exit from pyFormex. It is normally not called directly, but results from an exit(True) call. """ if pf.app and pf.app_started: # quit the QT app pf.debug("draw.exit called while no script running") pf.app.quit() # closes the GUI and exits pyformex else: # the QT app didn't even start sys.exit(0) # use Python to exit pyformex def processArgs(args): """Run the application without gui. Arguments are interpreted as names of script files, possibly interspersed with arguments for the scripts. Each running script should pop the required arguments from the list. """ res = 0 while len(args) > 0: fn = args.pop(0) if fn.endswith('.pye'): pass elif not os.path.exists(fn) or not utils.is_pyFormex(fn): pf.message("Skipping %s: does not exist or is not a pyFormex script" % fn) continue res = playFile(fn,args) if res and pf.GUI: pf.message("Error during execution of script %s" % fn) return res def setPrefs(res,save=False): """Update the current settings (store) with the values in res. res is a dictionary with configuration values. The current settings will be update with the values in res. If save is True, the changes will be stored to the user's configuration file. """ pf.debug("Accepted settings:",res) for k in res: pf.cfg[k] = res[k] if save and pf.prefcfg[k] != pf.cfg[k]: pf.prefcfg[k] = pf.cfg[k] pf.debug("New settings:",pf.cfg) if save: pf.debug("New preferences:",pf.prefcfg) ########################## print information ################################ def printall(): """Print all Formices in globals()""" print("Formices currently in globals():\n%s" % listAll(clas=formex.Formex)) def printglobals(): print(Globals()) def printglobalnames(): a = Globals().keys() a.sort() print(a) def printconfig(): print("Reference Configuration: " + str(pf.refcfg)) print("Preference Configuration: " + str(pf.prefcfg)) print("User Configuration: " + str(pf.cfg)) def printdetected(): print(utils.reportDetected()) ### Utilities def writable(path): """Returns True if the specified path is writeable""" return os.access(path,os.W_OK) def chdir(fn): """Change the current working directory. If fn is a directory name, the current directory is set to fn. If fn is a file name, the current directory is set to the directory holding fn. In either case, the current directory is stored in the user's preferences for persistence between pyFormex invocations. If fn does not exist, nothing is done. """ if os.path.exists(fn): if not os.path.isdir(fn): fn = os.path.dirname(os.path.abspath(fn)) os.chdir(fn) setPrefs({'workdir':fn},save=True) if pf.GUI: pf.GUI.setcurdir() pf.message("Your current workdir is %s" % os.getcwd()) def runtime(): """Return the time elapsed since start of execution of the script.""" return time.clock() - starttime def startGui(args=[]): """Start the gui""" if pf.GUI is None: pf.debug("Starting the pyFormex GUI") from gui import guimain if guimain.startGUI(args) == 0: guimain.runGUI() def isWritable(path): """Check that the specified path is writeable. BEWARE: this only works if the path exists! """ return os.access(path,os.W_OK) def checkRevision(rev,comp='>='): """Check the pyFormex revision number. - rev: a positive integer. - comp: a string specifying a comparison operator. By default, this function returns True if the pyFormex revision number is equal or larger than the specified number. The comp argument may specify another comparison operator. If pyFormex is unable to find its revision number (this is the case on very old versions) the test returns False. """ try: cur = int(utils.splitStartDigits(pf.__revision__)[0]) return eval("%s %s %s" % (cur,comp,rev)) except: return False def requireRevision(rev,comp='>='): """Require a specified pyFormex revision number. The arguments are like checkRevision. Ho9wever, this function will raise an error if the requirement fails. """ if not checkRevision(rev,comp): raise RuntimeError,"Your current pyFormex revision (%s) does not pass the test %s %s" % (pf.__revision__,comp,rev) def grepSource(pattern,options='',quiet=False): """Finds pattern in the pyFormex source .py files. Uses the `grep` program to find all occurrences of some specified pattern text in the pyFormex source .py files (including the examples). Extra options can be passed to the grep command. See `man grep` for more info. Returns the output of the grep command. """ files = utils.pyformexFiles() cmd = "grep %s '%s' %s" % (options,pattern,' '.join(files)) sta,out = utils.runCommand(cmd,quiet=quiet) return out ################### read and write files ################################# def writeGeomFile(filename,objects,sep=' ',mode='w',shortlines=False): """Save geometric objects to a pyFormex Geometry File. A pyFormex Geometry File can store multiple geometrical objects in a native format that can be efficiently read back into pyformex. The format is portable over different pyFormex versions and even to other software. - `filename`: the name of the file to be written - `objects`: a list or a dictionary. If it is a dictionary, the objects will be saved with the key values as there names. Objects that can not be exported to a Geometry File will be silently ignored. - `mode`: can be set to 'a' to append to an existing file. - `sep`: the string used to separate data. If set to an empty string, the data will be written in binary format and the resulting file will be smaller but less portable. Returns the number of objects written to the file. """ f = geomfile.GeometryFile(filename,mode='w',sep=sep) if shortlines: f.fmt = {'i':'%i ','f':'%f '} f.write(objects) f.close() return len(objects) def readGeomFile(filename): """Read a pyFormex Geometry File. A pyFormex Geometry File can store multiple geometrical objects in a native format that can be efficiently read back into pyformex. The format is portable over different pyFormex versions and even to other software. - `filename`: the name of an exisiting pyFormex Geometry File. Returns a dictionary with the geometric objects read from the file. If object names were stored in the file, they will be used as the keys. Else, default names will be provided. """ f = geomfile.GeometryFile(filename,mode='r') return f.read() #### End pyformex-0.8.6/pyformex/plugins/0000755000211500021150000000000011705105304016560 5ustar benebene00000000000000pyformex-0.8.6/pyformex/plugins/datareader.py0000644000211500021150000001423211705104656021241 0ustar benebene00000000000000# $Id: datareader.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """Numerical data reader""" __all__ = ['splitFloat','readData'] import re from numpy import * Float = re.compile('[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE]\d+)?') FloatString = re.compile('(?P[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE]\d+)?)(?P.*)') def splitFloat(s): """Match a floating point number at the beginning of a string If the beginning of the string matches a floating point number, a list is returned with the float and the remainder of the string; if not, None is returned. Example: ``splitFloat('123e4rt345e6')`` returns ``[1230000.0, 'rt345e6']`` """ m = FloatString.match(s) if m: return [float(m.group('float')), m.group('string')] return None def readData(s,type,strict=False): """Read data from a line matching the 'type' specification. This is a powerful function for reading, interpreting and converting numerical data from a string. Fields in the string s are separated by commas. The 'type' argument is a list where each element specifies how the corresponding field should be interpreted. Available values are 'int', 'float' or some unit ('kg', 'm', etc.). If the type field is 'int' or 'float', the data field is converted to the matching type. If the type field is a unit, the data field should be a number and a unit separated by space or not, or just a number. If it is just a number, its value is returned unchanged (as float). If the data contains a unit, the number is converted to the requested unit. It is an error if the datafield holds a non-conformable unit. The function returns a list of ints and/or floats (without the units). If the number of data fields is not equal to the number of type specifiers, the returned list will correspond to the shortest of both and the surplus data or types are ignored, UNLESS the strict flag has been set, in which case a RuntimError is raised. Example:: readData('12, 13, 14.5e3, 12 inch, 1hr, 31kg ', ['int','float','kg','cm','s']) returns ``[12, 13.0, 14500.0, 30.48, 3600.0]`` ..warning :: You need to have the GNU ``units`` command installed for the unit conversion to work. """ import units,string out = [] data = string.split(s,',') if strict and len(data) != len(type): raise RuntimeError, "Data do not match type specifier %s\nData: '%s'" % (type,s) for t,d in zip(type,data): #print(t,d) if len(d) == 0: break v = string.strip(d) if t == 'int': val = int(v) elif t == 'float': val = float(v) else: m = Float.match(v) #print(m.start(),m.end(),len(v)) if m and m.end() == len(v): val = float(v) else: val = float(units.ConvertUnits(v,t)) out.append(val) return out ################################################### ## BV: The following functions need clean up ## or should be replaced with a common module with calpy ## def readAsciiTable(fn, header=True): """_Reads data from an ASCII text file (Table). if header is True: first line is the header, the rest is a table (2D array) it returns the header and the 2D array of data as floats. if header is False, there is no header. it returns the header as None and the 2D array of data as floats. """ fil=open(fn,'r') line = fil.readline() h = line.strip('\n').split() data = fromfile(fil,sep=' ',dtype=float32) if header==False: h=asarray(h, dtype=float) data=append(h, data) return None, data.reshape (-1,len(h) ) if header==True: data = data.reshape((-1,len(h))) return h, data def writeAsciiTable(fn, h, d, fmtdata='e'): """_Writes an ASCII text file. The first line is the header h (tuple of strings, e.g. h=[ 'n0', 'n1', 'n2' ] ). The other lines contains the data d (2D array of floats) as a table. If format of the data can be chosen with fmtdata: 1) fmtdata is 'e' , the data are written as scientific (1.123456e+01), 2) fmtdata is 'f' , the data are written as floats, 3)fmtdata is 'd' , the data are written as integers, 4) otherwise, the fmt is specified (e.g. fmtdata='d f f d'). In this case, there should be as many fmt as data columns""" fil=open(fn,'w') ncol= len(h)-1#number of columns-1 fmtH = '%s'+ncol*' %s' + '\n' fil.write(fmtH %(tuple(h )))#header if fmtdata==None:fmtdata='e' if fmtdata=='e': fmtD = '%e'+ncol*' %e' + '\n' elif fmtdata=='f': fmtD = '%f'+ncol*' %f' + '\n' elif fmtdata=='d': fmtD = '%d'+ncol*' %d' + '\n' else: dt= fmtdata.split(' ') sdt='%'+dt[0] for t in dt[1:]: sdt=sdt+' %'+t fmtD=sdt+'\n' for ld in d: fil.write(fmtD % (tuple(ld)))#line of data fil.close() if __name__ == "__main__": print(readData('12, 13, 14.5e3, 12 inch, 1hr, 5MPa', ['int','float','kg','cm','s'])) pyformex-0.8.6/pyformex/plugins/formex_menu.py0000644000211500021150000003664611705104656021506 0ustar benebene00000000000000# $Id: formex_menu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """formex_menu.py This is a pyFormex plugin. It is not intended to be executed as a script, but to be loaded as a plugin. """ import pyformex as pf from formex import * import utils from odict import ODict from gui import actors from gui import menu from gui.draw import * from plugins import objects,trisurface,inertia,partition,sectionize import commands, os, timer ##################### select, read and write ########################## selection = pf.GUI.selection['formex'] setSelection = selection.set drawSelection = selection.draw ## def readSelection(select=True,draw=True,multi=True): ## """Read a Formex (or list) from asked file name(s). ## If select is True (default), this becomes the current selection. ## If select and draw are True (default), the selection is drawn. ## """ ## types = utils.fileDescription(['pgf','all']) ## fn = askFilename(pf.cfg['workdir'],types,multi=multi) ## if fn: ## if not multi: ## fn = [ fn ] ## chdir(fn[0]) ## res = ODict() ## pf.GUI.setBusy() ## for f in fn: ## res.update(readGeomFile(f)) ## pf.GUI.setBusy(False) ## export(res) ## if select: ## oknames = [ k for k in res if isinstance(res[k],Formex) ] ## selection.set(oknames) ## pf.message("Set Formex selection to %s" % oknames) ## if draw: ## selection.draw() ## return fn ## def writeSelection(): ## """Writes the currently selected Formices to a Geometry File.""" ## F = selection.check() ## if F: ## types = utils.fileDescription(['pgf','all']) ## name = selection.names[0] ## fn = askNewFilename(os.path.join(pf.cfg['workdir'],"%s.pgf" % name), ## filter=types) ## if fn: ## if not (fn.endswith('.pgf') or fn.endswith('.formex')): ## fn += '.pgf' ## pf.message("Creating pyFormex Geometry File '%s'" % fn) ## chdir(fn) ## selection.writeToFile(fn) ## pf.message("Contents: %s" % selection.names) ## def printSize(): ## for s in selection.names: ## S = named(s) ## pf.message("Formex %s has shape %s" % (s,S.shape())) ################### Change attributes of Formex ####################### def shrink(): """Toggle the shrink mode""" if selection.shrink is None: selection.shrink = 0.8 else: selection.shrink = None selection.draw() #################### CoordPlanes #################################### _bbox = None def showBbox(): """Draw the bbox on the current selection.""" global _bbox FL = selection.check() if FL: bb = bbox(FL) pf.message("Bbox of selection: %s" % bb) nx = array([4,4,4]) _bbox = actors.CoordPlaneActor(nx=nx,ox=bb[0],dx=(bb[1]-bb[0])/nx) pf.canvas.addAnnotation(_bbox) pf.canvas.update() def removeBbox(): """Remove the bbox of the current selection.""" global _bbox if _bbox: pf.canvas.removeAnnotation(_bbox) _bbox = None #################### Axes #################################### def unitAxes(): """Create a set of three axes.""" Hx = Formex('l:1',5).translate([-0.5,0.0,0.0]) Hy = Hx.rotate(90) Hz = Hx.rotate(-90,1) Hx.setProp(4) Hy.setProp(5) Hz.setProp(6) return Formex.concatenate([Hx,Hy,Hz]) def showPrincipal(): """Show the principal axes.""" F = selection.check(single=True) if not F: return # compute the axes C,I = inertia.inertia(F.coords) pf.message("Center of gravity: %s" % C) pf.message("Inertia tensor: %s" % I) Iprin,Iaxes = inertia.principal(I,sort=True,right_handed=True) pf.message("Principal Values: %s" % Iprin) pf.message("Principal Directions: %s" % Iaxes) data = (C,I,Iprin,Iaxes) # now display the axes siz = F.dsize() H = unitAxes().scale(1.1*siz).affine(Iaxes.transpose(),C) A = 0.1*siz * Iaxes.transpose() G = Formex([[C,C+Ax] for Ax in A],3) draw([G,H]) export({'principalAxes':H,'_principal_data_':data}) return data def rotatePrincipal(): """Rotate the selection according to the last shown principal axes.""" try: data = named('_principal_data_') except: data = showPrincipal() FL = selection.check() if FL: ctr = data[0] rot = data[3] selection.changeValues([ F.trl(-ctr).rot(rot).trl(ctr) for F in FL ]) selection.drawChanges() def transformPrincipal(): """Transform the selection according to the last shown principal axes. This is analog to rotatePrincipal, but positions the Formex at its center. """ try: data = named('_principal_data_') except: data = showPrincipal() FL = selection.check() if FL: ctr = data[0] rot = data[3] selection.changeValues([ F.trl(-ctr).rot(rot) for F in FL ]) selection.drawChanges() ################### Perform operations on Formex ####################### def scaleSelection(): """Scale the selection.""" FL = selection.check() if FL: res = askItems([['scale',1.0]], caption = 'Scale Factor') if res: scale = float(res['scale']) selection.changeValues([ F.scale(scale) for F in FL ]) selection.drawChanges() def scale3Selection(): """Scale the selection with 3 scale values.""" FL = selection.check() if FL: res = askItems([['x-scale',1.0],['y-scale',1.0],['z-scale',1.0]], caption = 'Scaling Factors') if res: scale = map(float,[res['%c-scale'%c] for c in 'xyz']) selection.changeValues([ F.scale(scale) for F in FL ]) selection.drawChanges() def translateSelection(): """Translate the selection.""" FL = selection.check() if FL: res = askItems([['direction',0],['distance','1.0']], caption = 'Translation Parameters') if res: dir = int(res['direction']) dist = float(res['distance']) selection.changeValues([ F.translate(dir,dist) for F in FL ]) selection.drawChanges() def centerSelection(): """Center the selection.""" FL = selection.check() if FL: selection.changeValues([ F.translate(-F.center()) for F in FL ]) selection.drawChanges() def rotateSelection(): """Rotate the selection.""" FL = selection.check() if FL: res = askItems([['axis',2],['angle','90.0']]) if res: axis = int(res['axis']) angle = float(res['angle']) selection.changeValues([ F.rotate(angle,axis) for F in FL ]) selection.drawChanges() def rotateAround(): """Rotate the selection.""" FL = selection.check() if FL: res = askItems([['axis',2],['angle','90.0'],['around','[0.0,0.0,0.0]']]) if res: axis = int(res['axis']) angle = float(res['angle']) around = eval(res['around']) pf.debug('around = %s'%around) selection.changeValues([ F.rotate(angle,axis,around) for F in FL ]) selection.drawChanges() def rollAxes(): """Rotate the selection.""" FL = selection.check() if FL: selection.changeValues([ F.rollAxes() for F in FL ]) selection.drawChanges() def clipSelection(): """Clip the selection.""" FL = selection.check() if FL: res = askItems([['axis',0],['begin',0.0],['end',1.0],['nodes','all','select',['all','any','none']]],caption='Clipping Parameters') if res: bb = bbox(FL) axis = int(res['axis']) xmi = bb[0][axis] xma = bb[1][axis] dx = xma-xmi xc1 = xmi + float(res['begin']) * dx xc2 = xmi + float(res['end']) * dx selection.changeValues([ F.clip(F.test(nodes=res['nodes'],dir=axis,min=xc1,max=xc2)) for F in FL ]) selection.drawChanges() def cutSelection(): """Cut the selection with a plane.""" FL = selection.check() FLnot = [ F for F in FL if F.nplex() not in [2,3] ] if FLnot: warning("Currently I can only cut Formices with plexitude 2 or 3.\nPlease change your selection.") return dsize = bbox(FL).dsize() if dsize > 0.: esize = 10 ** (niceLogSize(dsize)-5) else: esize = 1.e-5 res = askItems([['Point',(0.0,0.0,0.0)], ['Normal',(1.0,0.0,0.0)], ['New props',[1,2,2,3,4,5,6]], ['Side','positive', 'radio', ['positive','negative','both']], ['Tolerance',esize], ],caption = 'Define the cutting plane') if res: P = res['Point'] N = res['Normal'] atol = res['Tolerance'] p = res['New props'] side = res['Side'] if side == 'both': G = [F.cutWithPlane(P,N,side=side,atol=atol,newprops=p) for F in FL] draw(G[0]) G_pos = [ g[0] for g in G ] G_neg = [ g[1] for g in G ] export(dict([('%s/pos' % n,g) for n,g in zip(selection,G_pos)])) export(dict([('%s/neg' % n,g) for n,g in zip(selection,G_neg)])) selection.set(['%s/pos' % n for n in selection] + ['%s/neg' % n for n in selection]) selection.draw() else: selection.changeValues([ F.cutWithPlane(P,N,side=side,atol=atol,newprops=p) for F in FL ]) selection.drawChanges() def concatenateSelection(): """Concatenate the selection.""" FL = selection.check() if FL: plexitude = array([ F.nplex() for F in FL ]) if plexitude.min() == plexitude.max(): res = askItems([['name','combined']],'Name for the concatenation') if res: name = res['name'] export({name:Formex.concatenate(FL)}) selection.set(name) selection.draw() else: warning('You can only concatenate Formices with the same plexitude!') def partitionSelection(): """Partition the selection.""" F = selection.check(single=True) if not F: return name = selection[0] pf.message("Partitioning Formex '%s'" % name) cuts = partition.partition(F) pf.message("Subsequent cutting planes: %s" % cuts) if ack('Save cutting plane data?'): types = [ 'Text Files (*.txt)', 'All Files (*)' ] fn = askNewFilename(pf.cfg['workdir'],types) if fn: chdir(fn) fil = open(fn,'w') fil.write("%s\n" % cuts) fil.close() def createParts(): """Create parts of the current selection, based on property values.""" F = selection.check(single=True) if not F: return name = selection[0] partition.splitProp(F,name) def sectionizeSelection(): """Sectionize the selection.""" F = selection.check(single=True) if not F: return name = selection[0] pf.message("Sectionizing Formex '%s'" % name) ns,th,segments = sectionize.createSegments(F) if not ns: return sections,ctr,diam = sectionize.sectionize(F,segments,th) #pf.message("Centers: %s" % ctr) #pf.message("Diameters: %s" % diam) if ack('Save section data?'): types = [ 'Text Files (*.txt)', 'All Files (*)' ] fn = askNewFilename(pf.cfg['workdir'],types) if fn: chdir(fn) fil = open(fn,'w') fil.write("%s\n" % ctr) fil.write("%s\n" % diam) fil.close() if ack('Draw circles?'): circles = sectionize.drawCircles(sections,ctr,diam) ctrline = sectionize.connectPoints(ctr) if ack('Draw circles on Formex ?'): sectionize.drawAllCircles(F,circles) circles = Formex.concatenate(circles) circles.setProp(3) ctrline.setProp(1) draw(ctrline,color='red') export({'circles':circles,'ctrline':ctrline,'flypath':ctrline}) if ack('Fly through the Formex ?'): flyAlong(ctrline) ## if ack('Fly through in smooth mode ?'): ## smooth() ## flytruCircles(ctr) selection.draw() def fly(): path = named('flypath') if path is not None: flyAlong(path) else: warning("You should define the flypath first") ################### menu ################# _menu = 'Formex' def create_menu(): """Create the Formex menu.""" MenuData = [ ("&Shrink",shrink), ("---",None), ("&Bbox", [('&Show Bbox Planes',showBbox), ('&Remove Bbox Planes',removeBbox), ]), ("&Transform", [("&Scale Selection",scaleSelection), ("&Scale non-uniformly",scale3Selection), ("&Translate",translateSelection), ("&Center",centerSelection), ("&Rotate",rotateSelection), ("&Rotate Around",rotateAround), ("&Roll Axes",rollAxes), ]), ("&Clip/Cut", [("&Clip",clipSelection), ("&Cut With Plane",cutSelection), ]), ("&Undo Last Changes",selection.undoChanges), ("---",None), ("Show &Principal Axes",showPrincipal), ("Rotate to &Principal Axes",rotatePrincipal), ("Transform to &Principal Axes",transformPrincipal), ("---",None), ("&Concatenate Selection",concatenateSelection), ("&Partition Selection",partitionSelection), ("&Create Parts",createParts), ("&Sectionize Selection",sectionizeSelection), ("---",None), ("&Fly",fly), ("---",None), ("&Reload menu",reload_menu), ("&Close",close_menu), ] return menu.Menu(_menu,items=MenuData,parent=pf.GUI.menu,before='help') def show_menu(): """Show the Tools menu.""" if not pf.GUI.menu.item(_menu): create_menu() def close_menu(): """Close the Tools menu.""" m = pf.GUI.menu.item(_menu) if m : m.remove() def reload_menu(): """Reload the Postproc menu.""" close_menu() show_menu() #################################################################### ######### What to do when the script is executed ################### if __name__ == "draw": reload_menu() # End pyformex-0.8.6/pyformex/plugins/units.py0000644000211500021150000001544011705104656020311 0ustar benebene00000000000000# $Id: units.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A Python wrapper for unit conversion of physical quantities. This module uses the standard UNIX program 'units' (available from http://www.gnu.org/software/units/units.html) to do the actual conversions. Obviously, it will only work on systems that have this program available. If you really insist on running another OS lacking the units command, have a look at http://home.tiscali.be/be052320/Unum.html and make an implementation based on unum. If you GPL it and send it to me, I might include it in this distribution. """ #from pyformex import utils #utils.hasExternal('units') import commands,string def convertUnits(From,To): """Converts between conformable units. This function converts the units 'From' to units 'To'. The units should be conformable. The 'From' argument can (and usually does) include a value. The return value is the converted value without units. Thus: convertUnits('3.45 kg','g') will return '3450'. This function is merely a wrapper around the GNU 'units' command, which should be installed for this function to work. """ status,output = commands.getstatusoutput('units \"%s\" \"%s\"' % (From,To)) if status: raise RuntimeError, 'Could not convert units from \"%s\" to \"%s\"' % (From,To) return string.split(output)[1] class UnitsSystem(object): """A class for handling and converting units of physical quantities. The units class provides two built-in consistent units systems: International() and Engineering(). International() returns the standard International Standard units. Engineering() returns a consistent engineering system,which is very practical for use in mechanical engineering. It uses 'mm' for length and 'MPa' for pressure and stress. To keep it consistent however, the density is rather unpractical: 't/mm^3'. If you want to use t/m^3, you can make a custom units system. Beware with non-consistent unit systems though! The better practice is to allow any unit to be specified at input (and eventually requested for output), and to convert everyting internally to a consistent system. Apart from the units for usual physical quantities, Units stores two special purpose values in its units dictionary: 'model' : defines the length unit used in the geometrical model 'problem' : defines the unit system to be used in the problem. Defaults are: model='m', problem='international'. """ def __init__(self,system='international'): self.units = self.Predefined(system) self.units['model'] = 'm' self.units['problem'] = 'international' def Add(self,un): """Add the units from dictionary un to the units system""" for key,val in un.items(): self.units[key] = val def Predefined(self,system): """Returns the predefined units for the specified system""" if system == 'international': return self.International() elif system == 'engineering': return self.Engineering() elif system == 'user-defined': return {} else: raise RuntimeError,"Undefined Units system '%s'" % system def International(self): """Returns the international units system.""" return {'length': 'm', 'mass': 'kg', 'force': 'N', 'pressure': 'Pa', 'density': 'kg/m^3', 'time': 's', 'acceleration': 'm/s^2', 'temperature': 'tempC', 'degrees': 'K'} def Engineering(self): """Returns a consistent engineering units system.""" return {'length': 'mm', 'mass': 't', 'force': 'N', 'pressure': 'MPa', 'density': 't/mm^3', 'time': 's', 'acceleration': 'mm/s^2', 'temperature': 'tempC', 'degrees': 'K'} def Read(self,filename): """Read units from file with specified name. The units file is an ascii file where each line contains a couple of words separated by a colon and a blank. The first word is the type of quantity, the second is the unit to be used for this quantity. Lines starting with '#' are ignored. A 'problem: system' line sets all units to the corresponding value of the specified units system. """ fil = open(filename,'r') self.units = {} for line in fil: if line[0] == '#': continue s = string.split(line) if len(s) == 2: key,val = s key = string.lower(string.rstrip(key,':')) self.units[key] = val if key == 'problem': self.Add(self.Predefined(string.lower(val))) else: print("Ignoring line : %s" % line) fil.close() def Get(self,ent): """Get units list for the specified entitities. If ent is a single entity, returns the corresponding unit if an entry ent exists in the current system or else returns ent unchanged. If ent is a list of entities, returns a list of corresponding units. Example: with the default units system:: Un = UnitsSystem() Un.Get(['length','mass','float']) returns: ``['m', 'kg', 'float']`` """ if isinstance(ent,list): return [ self.Get(e) for e in ent ] else: if self.units.has_key(ent): return self.units[ent] else: return ent if __name__ == '__main__': test = (('21cm','in'), ('31e6mg','kg'), ('1 lightyear','km'), ) for f,t in test: print("%s = %s %s" % (f,convertUnits(f,t),t)) ### End pyformex-0.8.6/pyformex/plugins/fe.py0000644000211500021150000001624611705104656017546 0ustar benebene00000000000000# $Id: fe.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Finite Element Models in pyFormex. Finite element models are geometrical models that consist of a unique set of nodal coordinates and one of more sets of elements. """ import pyformex as pf from coords import * from connectivity import * from geometry import Geometry #from numpy import * from mesh import Mesh,mergeMeshes from utils import deprecation import warnings ######################## Finite Element Model ########################## class Model(Geometry): """Contains all FE model data.""" _set_coords = Geometry._set_coords_inplace def __init__(self,coords,elems): """Create new model data. coords is an array with nodal coordinates elems is either a single element connectivity array, or a list of such. In a simple case, coords and elems can be the arrays obtained by ``coords, elems = F.feModel()``. This is however limited to a model where all elements have the same number of nodes. Then you can use the list of elems arrays. The 'fe' plugin has a helper function to create this list. E.g., if ``FL`` is a list of Formices (possibly with different plexitude), then ``fe.mergeModels([Fi.feModel() for Fi in FL])`` will return the (coords,elems) tuple to create the Model. The model can have node and element property numbers. """ if not type(elems) == list: elems = [ elems ] self.coords = Coords(coords) self.elems = [ Connectivity(e) for e in elems ] self.meshes = [ Mesh(self.coords,e) for e in self.elems ] nnodes = [ m.nnodes() for m in self.meshes ] nelems = [ m.nelems() for m in self.meshes ] nplex = [ m.nplex() for m in self.meshes ] self.cnodes = cumsum([0]+nnodes) self.celems = cumsum([0]+nelems) pf.message("Number of nodes: %s" % self.coords.shape[0]) pf.message("Number of elements: %s" % self.celems[-1]) pf.message("Number of element groups: %s" % len(nelems)) #pf.message("Number of nodes per group: %s" % nnodes) pf.message("Number of elements per group: %s" % nelems) pf.message("Plexitude of each group: %s" % nplex) def nnodes(self): """Return the number of nodes in the model.""" return self.coords.shape[0] def nelems(self): """Return the number of elements in the model.""" return self.celems[-1] def ngroups(self): """Return the number of element groups in the model.""" return len(self.elems) def mplex(self): """Return the maximum plexitude of the model.""" return max([e.nplex() for e in self.elems]) def splitElems(self,set): """Splits a set of element numbers over the element groups. Returns two lists of element sets, the first in global numbering, the second in group numbering. Each item contains the element numbers from the given set that belong to the corresponding group. """ set = unique(set) split = [] n = 0 for e in self.celems[1:]: i = set.searchsorted(e) split.append(set[n:i]) n = i return split,[ asarray(s) - ofs for s,ofs in zip(split,self.celems) ] def elemNrs(group,set): """Return the global element numbers for elements set in group""" return self.celems[group] + set def getElems(self,sets): """Return the definitions of the elements in sets. sets should be a list of element sets with length equal to the number of element groups. Each set contains element numbers local to that group. As the elements can be grouped according to plexitude, this function returns a list of element arrays matching the element groups in self.elems. Some of these arrays may be empty. It also provide the global and group element numbers, since they had to be calculated anyway. """ return [ e[s] for e,s in zip(self.elems,sets) ] def renumber(self,old=None,new=None): """Renumber a set of nodes. old and new are equally sized lists with unique node numbers, each smaller that the number of nodes in the model. The old numbers will be renumbered to the new numbers. If one of the lists is None, a range with the length of the other is used. If the lists are shorter than the number of nodes, the remaining nodes will be numbered in an unspecified order. If both lists are None, the nodes are renumbered randomly. This function returns a tuple (old,new) with the full renumbering vectors used. The first gives the old node numbers of the current numbers, the second gives the new numbers cooresponding with the old ones. """ nnodes = self.nnodes() if old is None and new is None: old = unique(random.randint(0,nnodes-1,nnodes)) new = unique(random.randint(0,nnodes-1,nnodes)) nn = max(old.size,new.size) old = old[:nn] new = new[:nn] elif old is None: new = asarray(new).reshape(-1) checkUniqueNumbers(new,0,nnodes) old = arange(new.size) elif new is None: old = asarray(old).reshape(-1) checkUniqueNumbers(old,0,nnodes) new = arange(old.size) all = arange(nnodes) old = concatenate([old,setdiff1d(all,old)]) new = concatenate([new,setdiff1d(all,new)]) oldnew = old[new] newold = argsort(oldnew) self.coords = self.coords[oldnew] self.elems = [ Connectivity(newold[e]) for e in self.elems ] return oldnew,newold def mergedModel(meshes,**kargs): """Returns the fe Model obtained from merging individual meshes. The input arguments are (coords,elems) tuples. The return value is a merged fe Model. """ return Model(*mergeMeshes(meshes,**kargs)) if __name__ == "__main__": print(__doc__) # End pyformex-0.8.6/pyformex/plugins/trisurface.py0000644000211500021150000023136211705104656021321 0ustar benebene00000000000000# $Id: trisurface.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Operations on triangulated surfaces. A triangulated surface is a surface consisting solely of triangles. Any surface in space, no matter how complex, can be approximated with a triangulated surface. """ import pyformex as pf from formex import * from connectivity import Connectivity,connectedLineElems,adjacencyArrays from mesh import Mesh import mesh_ext # load the extended Mesh functions import geomtools import inertia import tetgen import filewrite import utils from gui.drawable import interpolateNormals import os,tempfile import tempfile utils.hasExternal('admesh') utils.hasExternal('tetgen') utils.hasExternal('gts') from utils import deprecation # Conversion of surface file formats def stlConvert(stlname,outname=None,options='-d'): """Transform an .stl file to .off or .gts format. If outname is given, it is either '.off' or '.gts' or a filename ending on one of these extensions. If it is only an extension, the stlname will be used with extension changed. If the outname file exists and its mtime is more recent than the stlname, the outname file is considered uptodate and the conversion programwill not be run. The conversion program will be choosen depending on the extension. This uses the external commands 'admesh' or 'stl2gts'. The return value is a tuple of the output file name, the conversion program exit code (0 if succesful) and the stdout of the conversion program (or a 'file is already uptodate' message). """ if not outname: outname = pf.cfg.get('surface/stlread','.off') if outname.startswith('.'): outname = utils.changeExt(stlname,outname) if os.path.exists(outname) and utils.mtime(stlname) < utils.mtime(outname): return outname,0,"File '%s' seems to be up to date" % outname if outname.endswith('.off'): cmd = "admesh %s --write-off '%s' '%s'" % (options,outname,stlname) elif outname.endswith('.gts'): cmd = "stl2gts < '%s' > '%s'" % (stlname,outname) else: return outname,1,"Can not convert file '%s' to '%s'" % (stlname,outname) sta,out = utils.runCommand(cmd) return outname,sta,out # Input of surface file formats def read_gts(fn): """Read a GTS surface mesh. Return a coords,edges,faces tuple. """ pf.message("Reading GTS file %s" % fn) fil = open(fn,'r') header = fil.readline().split() ncoords,nedges,nfaces = map(int,header[:3]) if len(header) >= 7 and header[6].endswith('Binary'): sep='' else: sep=' ' coords = fromfile(fil,dtype=Float,count=3*ncoords,sep=' ').reshape(-1,3) edges = fromfile(fil,dtype=int32,count=2*nedges,sep=' ').reshape(-1,2) - 1 faces = fromfile(fil,dtype=int32,count=3*nfaces,sep=' ').reshape(-1,3) - 1 pf.message("Read %d coords, %d edges, %d faces" % (ncoords,nedges,nfaces)) if coords.shape[0] != ncoords or \ edges.shape[0] != nedges or \ faces.shape[0] != nfaces: pf.message("Error while reading GTS file: the file is probably incorrect!") return coords,edges,faces def read_off(fn): """Read an OFF surface mesh. The mesh should consist of only triangles! Returns a nodes,elems tuple. """ pf.message("Reading .OFF %s" % fn) fil = open(fn,'r') head = fil.readline().strip() if head != "OFF": print("%s is not an OFF file!" % fn) return None,None nnodes,nelems,nedges = map(int,fil.readline().split()) nodes = fromfile(file=fil, dtype=Float, count=3*nnodes, sep=' ') # elems have number of vertices + 3 vertex numbers elems = fromfile(file=fil, dtype=int32, count=4*nelems, sep=' ') pf.message("Read %d nodes and %d elems" % (nnodes,nelems)) return nodes.reshape((-1,3)),elems.reshape((-1,4))[:,1:] def read_stl(fn,intermediate=None): """Read a surface from .stl file. This is done by first coverting the .stl to .gts or .off format. The name of the intermediate file may be specified. If not, it will be generated by changing the extension of fn to '.gts' or '.off' depending on the setting of the 'surface/stlread' config setting. Return a coords,edges,faces or a coords,elems tuple, depending on the intermediate format. """ ofn,sta,out = stlConvert(fn,intermediate) if sta: pf.debug("Error during conversion of file '%s' to '%s'" % (fn,ofn)) pf.debug(out) return () if ofn.endswith('.gts'): return read_gts(ofn) elif ofn.endswith('.off'): return read_off(ofn) def read_gambit_neutral(fn): """Read a triangular surface mesh in Gambit neutral format. The .neu file nodes are numbered from 1! Returns a nodes,elems tuple. """ scr = os.path.join(pf.cfg['bindir'],'gambit-neu ') utils.runCommand("%s '%s'" % (scr,fn)) nodesf = utils.changeExt(fn,'.nodes') elemsf = utils.changeExt(fn,'.elems') nodes = fromfile(nodesf,sep=' ',dtype=Float).reshape((-1,3)) elems = fromfile(elemsf,sep=' ',dtype=int32).reshape((-1,3)) return nodes, elems-1 # Output of surface file formats def write_stla(f,x): """Export an x[n,3,3] float array as an ascii .stl file.""" own = type(f) == str if own: f = open(f,'w') f.write("solid Created by %s\n" % pf.Version) area,norm = geomtools.areaNormals(x) degen = degenerate(area,norm) print("The model contains %d degenerate triangles" % degen.shape[0]) for e,n in zip(x,norm): f.write(" facet normal %s %s %s\n" % tuple(n)) f.write(" outer loop\n") for p in e: f.write(" vertex %s %s %s\n" % tuple(p)) f.write(" endloop\n") f.write(" endfacet\n") f.write("endsolid\n") if own: f.close() def write_smesh(fn,nodes,elems): """Write a tetgen surface model to .node and .smesh files. The provided file name is the .node or the .smesh filename. """ tetgen.writeSurface(fn,nodes,elems) def surface_volume(x,pt=None): """Return the volume inside a 3-plex Formex. - `x`: an (ntri,3,3) shaped float array, representing ntri triangles. - `pt`: a point in space. If unspecified, it is taken equal to the center() of the coordinates `x`. Returns an (ntri) shaped array with the volume of the tetraeders formed by the triangles and the point `pt`. If `x` represents a closed surface, the sum of this array will represent the volume inside the surface. """ if pt is None: pt = x.center() x = x - pt a,b,c = [ x[:,i,:] for i in range(3) ] d = cross(b,c) e = (a*d).sum(axis=-1) # IS THIS ANY DIFFERENT FROM e / 6. ???? v = sign(e) * abs(e)/6. return v def curvature(coords,elems,edges,neighbours=1): """Calculate curvature parameters at the nodes. Algorithms based on Dong and Wang 2005; Koenderink and Van Doorn 1992. This uses the nodes that are connected to the node via a shortest path of 'neighbours' edges. Eight values are returned: the Gaussian and mean curvature, the shape index, the curvedness, the principal curvatures and the principal directions. """ # calculate n-ring neighbourhood of the nodes (n=neighbours) adj = adjacencyArrays(edges,nsteps=neighbours)[-1] adjNotOk = adj<0 # for nodes that have less than three adjacent nodes, remove the adjacencies adjNotOk[(adj>=0).sum(-1) <= 2] = True # calculate unit length average normals at the nodes p # a weight 1/|gi-p| could be used (gi=center of the face fi) p = coords n = interpolateNormals(coords,elems,atNodes=True) # double-precision: this will allow us to check the sign of the angles p = p.astype(float64) n = n.astype(float64) vp = p[adj] - p[:,newaxis] vn = n[adj] - n[:,newaxis] # where adjNotOk, set vectors = [0.,0.,0.] # this will result in NaN values vp[adjNotOk] = 0. vn[adjNotOk] = 0. # calculate unit length projection of vp onto the tangent plane t = geomtools.projectionVOP(vp,n[:,newaxis]) t = normalize(t) # calculate normal curvature k = dotpr(vp,vn)/dotpr(vp,vp) # calculate maximum normal curvature and corresponding coordinate system imax = nanargmax(k,-1) kmax = k[range(len(k)),imax] tmax = t[range(len(k)),imax] tmax1 = tmax tmax2 = cross(n,tmax1) tmax2 = normalize(tmax2) # calculate angles (tmax1,t) theta,rot = geomtools.rotationAngle(repeat(tmax1[:,newaxis],t.shape[1],1),t,angle_spec=Rad) theta = theta.reshape(t.shape[:2]) rot = rot.reshape(t.shape) # check the sign of the angles d = dotpr(rot,n[:,newaxis])/(length(rot)*length(n)[:,newaxis]) # divide by length for round-off errors cw = isClose(d,[-1.]) theta[cw] = -theta[cw] # calculate coefficients a = kmax a11 = nansum(cos(theta)**2*sin(theta)**2,-1) a12 = nansum(cos(theta)*sin(theta)**3,-1) a21 = a12 a22 = nansum(sin(theta)**4,-1) a13 = nansum((k-a[:,newaxis]*cos(theta)**2)*cos(theta)*sin(theta),-1) a23 = nansum((k-a[:,newaxis]*cos(theta)**2)*sin(theta)**2,-1) denom = (a11*a22-a12**2) b = (a13*a22-a23*a12)/denom c = (a11*a23-a12*a13)/denom # calculate the Gaussian and mean curvature K = a*c-b**2/4 H = (a+c)/2 # calculate the principal curvatures and principal directions k1 = H+sqrt(H**2-K) k2 = H-sqrt(H**2-K) theta0 = 0.5*arcsin(b/(k2-k1)) w = apply_along_axis(isClose,0,-b,2*(k2-k1)*cos(theta0)*sin(theta0)) theta0[w] = pi-theta0[w] e1 = cos(theta0)[:,newaxis]*tmax1+sin(theta0)[:,newaxis]*tmax2 e2 = cos(theta0)[:,newaxis]*tmax2-sin(theta0)[:,newaxis]*tmax1 # calculate the shape index and curvedness S = 2./pi*arctan((k1+k2)/(k1-k2)) C = square((k1**2+k2**2)/2) return K,H,S,C,k1,k2,e1,e2 ############################################################################ def fillBorder(border,method='radial'): """Create a surface inside a given closed border line. The border line is a closed polygonal line and can be specified as one of the following: - a closed PolyLine, - a 2-plex Mesh, with a Connectivity table such that the elements in order form a closed polyline, - a simple Coords specifying the subsequent vertices of the polygonal border line. The return value is a TriSurface filling the hole inside the border. There are currently two fill methods available: - 'radial': this method adds a central point and connects all border segments with the center to create triangles. - 'border': this method creates subsequent triangles by connecting the endpoints of two consecutive border segments and thus works its way inwards until the hole is closed. Triangles are created at the line segments that form the smallest angle. The 'radial' method produces nice results if the border is relative smooth, nearly convex and nearly planar. It adds an extra point though, which may be unwanted. On irregular 3D borders there is a high change that the result contains intersecting triangles. This 'border' method is slower on large borders, does not introduce any new point and has a better chance of avoiding intersecting triangles on irregular 3D borders. The resulting surface can be checked for intersecting triangles by the :meth:`check` method. ..note :: Because the 'border' does not create any new points, the returned surface will use the same point coordinate array as the input object. """ from plugins.trisurface import TriSurface if method != 'border': border = border.compact() if isinstance(border,Mesh) and border.nplex()==2: coords = border.coords elems = border.elems[:,0] elif isinstance(border,PolyLine): coords = border.coords elems = None elif isinstance(border,Coords): coords = border.reshape(-1,3) elems = None else: raise ValueError,"Expected a 2-plex Mesh, a PolyLine or a Coords array as first argument" if elems is None: elems = arange(coords.shape[0]) n = elems.shape[0] if n < 3: raise ValueError,"Expected at least 3 points." if method == 'radial': coords = Coords.concatenate([coords,coords.center()]) elems = column_stack([elems,roll(elems,-1),n*ones(elems.shape[0],dtype=Int)]) elif method == 'border': # creating elems array at once (more efficient than appending) tri = -ones((n-2,3),dtype=Int) # compute all internal angles x = coords[elems] e = arange(n) v = roll(x,-1,axis=0) - x v = normalize(v) c = vectorPairCosAngle(roll(v,1,axis=0),v) # loop in order of smallest angles itri = 0 while n > 3: # find minimal angle j = c.argmin() i = (j - 1) % n k = (j + 1) % n tri[itri] = [ e[i],e[j],e[k]] # remove the point j ii = (i-1) % n kk = (k+1) % n v1 = normalize([ v[e[ii]], x[e[k]] - x[e[i]] ]) v2 = normalize([ x[e[k]] - x[e[i]], v[e[k]] ]) cnew = vectorPairCosAngle(v1,v2) c = roll(concatenate([cnew,roll(c,-i)[3:]]),i) e = roll(roll(e,-j)[1:],j) n -= 1 itri += 1 tri[itri] = e elems = elems[tri] else: raise ValueError,"Strategy should be either 'radial' or 'border'" return TriSurface(coords,elems) @utils.deprecation("surfaceInsideBorder is deprecated. Please use fillBorder instead.") def surfaceInsideBorder(border,method='radial'): """Create a surface inside a closed curve defined by a 2-plex Mesh. border is a 2-plex Mesh representing a closed polyline. The return value is a TriSurface filling the hole inside the border. There are two fill methods: - 'radial': this method adds a central point and connects all border segments with the center to create triangles. It is fast and works well if the border is smooth, nearly convex and nearly planar. - 'border': this method creates subsequent triangles by connecting the endpoints of two consecutive border segments and thus works its way inwards until the hole is closed. Triangles are created at the segments that form the smallest angle. This method is slower, but works also for most complex borders. Because it does not create any new points, the returned surface uses the same point coordinate array as the input Mesh. """ if border.nplex() != 2: raise ValueError,"Expected Mesh with plexitude 2, got %s" % border.nplex() if method == 'radial': x = border.getPoints().center() n = zeros_like(border.elems[:,:1]) + border.coords.shape[0] elems = concatenate([border.elems,n],axis=1) coords = Coords.concatenate([border.coords,x]) elif method == 'border': coords = border.coords segments = border.elems elems = empty((0,3,),dtype=int) while len(segments) != 3: segments,triangle = _create_border_triangle(coords,segments) elems = row_stack([elems,triangle]) elems = row_stack([elems,segments[:,0]]) else: raise ValueError,"Strategy should be either 'radial' or 'border'" return TriSurface(coords,elems) # This should be replaced with an algorith using PolyLine def _create_border_triangle(coords,elems): """Create a triangle within a border. The triangle is created from the two border elements with the sharpest angle. Coords is a (npoints,3) shaped array of floats. Elems is a (nelems,2) shaped array of integers representing the border element numbers and must be ordered. A list of two objects is returned: the new border elements and the triangle. """ border = coords[elems] # calculate angles between edges of border edges1 = border[:,1]-border[:,0] edges2 = border[:,0]-border[:,1] # roll axes so that edge i of edges1 and edges2 are neighbours edges2 = roll(edges2,-1,0) len1 = length(edges1) len2 = length(edges2) inpr = diagonal(inner(edges1,edges2)) cos = inpr/(len1*len2) # angle between 0 and 180 degrees angle = arccosd(cos) # determine sharpest angle i = where(angle == angle.min())[0][0] # create triangle and new border elements j = i + 1 n = shape(elems)[0] if j == n: j -= n old_edges = take(elems,[i,j],0) elems = delete(elems,[i,j],0) new_edge = asarray([old_edges[0,0],old_edges[-1,1]]) if j == 0: elems = insert(elems,0,new_edge,0) else: elems = insert(elems,i,new_edge,0) triangle = append(old_edges[:,0],old_edges[-1,1].reshape(1),0) return elems,triangle ############################################################################ # The TriSurface class class TriSurface(Mesh): """A class representing a triangulated 3D surface. The surface contains `ntri` triangles, each having 3 vertices with 3 coordinates. The surface can be initialized from one of the following: - a (ntri,3,3) shaped array of floats - a Formex with plexitude 3 - a Mesh with plexitude 3 - an (ncoords,3) float array of vertex coordinates and an (ntri,3) integer array of vertex numbers - an (ncoords,3) float array of vertex coordinates, an (nedges,2) integer array of vertex numbers, an (ntri,3) integer array of edges numbers. Additionally, a keyword argument prop= may be specified to set property values. """ def __init__(self,*args,**kargs): """Create a new surface.""" self.areas = self.normals = None self.adj = None if hasattr(self,'edglen'): del self.edglen if len(args) == 0: Mesh.__init__(self) return # an empty surface if len(args) == 1: # argument should be a suitably structured geometry object # TriSurface, Mesh, Formex, Coords, ndarray, ... a = args[0] if isinstance(a,Mesh): if a.nplex() != 3 or a.eltype.name() != 'tri3': raise ValueError,"Only meshes with plexitude 3 and eltype 'tri3' can be converted to TriSurface!" Mesh.__init__(self,a.coords,a.elems,a.prop,'tri3') else: if not isinstance(a,Formex): # something that can be converted to a Formex try: a = Formex(a) except: raise ValueError,"Can not convert objects of type %s to TriSurface!" % type(a) if a.nplex() != 3: raise ValueError,"Expected an object with plexitude 3!" coords,elems = a.fuse() Mesh.__init__(self,coords,elems,a.prop,'tri3') else: # arguments are (coords,elems) or (coords,edges,faces) coords = Coords(args[0]) if len(coords.shape) != 2: raise ValueError,"Expected a 2-dim coordinates array" if len(args) == 2: # arguments are (coords,elems) elems = Connectivity(args[1],nplex=3) Mesh.__init__(self,coords,elems,None,'tri3') elif len(args) == 3: # arguments are (coords,edges,faces) edges = Connectivity(args[1],nplex=2) if edges.size > 0 and edges.max() >= coords.shape[0]: raise ValueError,"Some vertex number is too high" faces = Connectivity(args[2],nplex=3) if faces.max() >= edges.shape[0]: raise ValueError,"Some edge number is too high" elems = faces.combine(edges) Mesh.__init__(self,coords,elems,None,'tri3') # since we have the extra data available, keep them self.edges = edges self.faces = faces else: raise RuntimeError,"Too many positional arguments" if 'prop' in kargs: self.setProp(kargs['prop']) def __setstate__(self,state): """Set the object from serialized state. This allows to read back old pyFormex Project files where the Surface class did not set an element type. """ self.__dict__.update(state) self.setType('tri3') ########################################################################### # # Return information about a TriSurface # def nedges(self): """Return the number of edges of the TriSurface.""" return self.getEdges().shape[0] def nfaces(self): """Return the number of faces of the TriSurface.""" return self.getElemEdges().shape[0] def vertices(self): """Return the coordinates of the nodes of the TriSurface.""" return self.coords def shape(self): """Return the number of points, edges, faces of the TriSurface.""" return self.ncoords(),self.nedges(),self.nfaces() def getElemEdges(self): """Get the faces' edge numbers.""" if self.elem_edges is None: self.elem_edges,self.edges = self.elems.insertLevel([[0,1],[1,2],[2,0]]) return self.elem_edges ########################################################################### # # Operations that change the TriSurface itself # # Make sure that you know what you're doing if you use these # # # Changes to the geometry should by preference be done through the # __init__ function, to ensure consistency of the data. # Convenience functions are defined to change some of the data. # def setCoords(self,coords): """Change the coords.""" self.__init__(coords,self.elems,prop=self.prop) return self def setElems(self,elems): """Change the elems.""" self.__init__(self.coords,elems,prop=self.prop) def setEdgesAndFaces(self,edges,faces): """Change the edges and faces.""" self.__init__(self.coords,edges,faces,prop=self.prop) def append(self,S): """Merge another surface with self. This just merges the data sets, and does not check whether the surfaces intersect or are connected! This is intended mostly for use inside higher level functions. """ coords = concatenate([self.coords,S.coords]) elems = concatenate([self.elems,S.elems+self.ncoords()]) ## What to do if one of the surfaces has properties, the other one not? ## The current policy is to use zero property values for the Surface ## without props prop = None if self.prop is not None or S.prop is not None: if self.prop is None: self.prop = zeros(shape=self.nelems(),dtype=Int) if S.prop is None: p = zeros(shape=S.nelems(),dtype=Int) else: p = S.prop prop = concatenate((self.prop,p)) self.__init__(coords,elems,prop=prop) ########################################################################### # # read and write # @classmethod def read(clas,fn,ftype=None): """Read a surface from file. If no file type is specified, it is derived from the filename extension. Currently supported file types: - .stl (ASCII or BINARY) - .gts - .off - .neu (Gambit Neutral) - .smesh (Tetgen) """ if ftype is None: ftype = os.path.splitext(fn)[1] # deduce from extension ftype = ftype.strip('.').lower() if ftype == 'off': return TriSurface(*read_off(fn)) elif ftype == 'gts': return TriSurface(*read_gts(fn)) elif ftype == 'stl': return TriSurface(*read_stl(fn)) elif ftype == 'neu': return TriSurface(*read_gambit_neutral(fn)) elif ftype == 'smesh': return TriSurface(*tetgen.readSurface(fn)) else: raise "Unknown TriSurface type, cannot read file %s" % fn def write(self,fname,ftype=None): """Write the surface to file. If no filetype is given, it is deduced from the filename extension. If the filename has no extension, the 'gts' file type is used. """ if ftype is None: ftype = os.path.splitext(fname)[1] if ftype == '': ftype = 'off' else: ftype = ftype.strip('.').lower() pf.message("Writing surface to file %s" % fname) if ftype == 'pgf': Geometry.write(self,fname) elif ftype == 'gts': faces,edges=self.elems.insertLevel([[0,1],[1,2],[2,0]]) filewrite.writeGTS(fname,self.coords,edges,faces) ## filewrite.writeGTS(fname,self.coords,self.getEdges(),self.getElemEdges()) pf.message("Wrote %s vertices, %s edges, %s faces" % self.shape()) elif ftype in ['stl','off','smesh']: if ftype == 'stl': write_stla(fname,self.coords[self.elems]) elif ftype == 'off': filewrite.writeOFF(fname,self.coords,self.elems) elif ftype == 'smesh': write_smesh(fname,self.coords,self.elems) pf.message("Wrote %s vertices, %s elems" % (self.ncoords(),self.nelems())) else: print("Cannot save TriSurface as file %s" % fname) ####################### TriSurface Data ###################### def avgVertexNormals(self): """Compute the average normals at the vertices.""" return interpolateNormals(self.coords,self.elems,atNodes=True) def areaNormals(self): """Compute the area and normal vectors of the surface triangles. The normal vectors are normalized. The area is always positive. The values are returned and saved in the object. """ if self.areas is None or self.normals is None: self.areas,self.normals = geomtools.areaNormals(self.coords[self.elems]) return self.areas,self.normals def facetArea(self): """Return the area of the surface triangles.""" return self.areaNormals()[0] def area(self): """Return the area of the surface""" area = self.areaNormals()[0] return area.sum() def volume(self): """Return the enclosed volume of the surface. This will only be correct if the surface is a closed manifold. """ x = self.coords[self.elems] return surface_volume(x).sum() def curvature(self,neighbours=1): """Return the curvature parameters at the nodes. This uses the nodes that are connected to the node via a shortest path of 'neighbours' edges. Eight values are returned: the Gaussian and mean curvature, the shape index, the curvedness, the principal curvatures and the principal directions. """ curv = curvature(self.coords,self.elems,self.getEdges(),neighbours=neighbours) return curv def inertia(self): """Return inertia related quantities of the surface. This returns the center of gravity, the principal axes of inertia, the principal moments of inertia and the inertia tensor. """ return self.centroids().inertia(mass=self.facetArea()) def surfaceType(self): """Check whether the TriSurface is a manifold and if it's closed.""" ncon = self.nEdgeConnected() #nadj = self.nEdgeAdjacent() maxcon = ncon.max() mincon = ncon.min() manifold = maxcon == 2 closed = mincon == 2 return manifold,closed,mincon,maxcon def borderEdges(self): """Detect the border elements of TriSurface. The border elements are the edges having less than 2 connected elements. Returns True where edge is on the border. """ return self.nEdgeConnected() <= 1 def borderEdgeNrs(self): """Returns the numbers of the border edges.""" return where(self.nEdgeConnected() <= 1)[0] def borderNodeNrs(self): """Detect the border nodes of TriSurface. The border nodes are the vertices belonging to the border edges. Returns a list of vertex numbers. """ border = self.getEdges()[self.borderEdgeNrs()] return unique(border) def isManifold(self): """Check whether the TriSurface is a manifold. A surface is a manifold if a small sphere exists that cuts the surface to a surface that can continously be deformed to an open disk. """ return self.surfaceType()[0] def nonManifoldEdges(self): """ Finds edges and faces that are not Manifold. Returns a tuple of: - the edges that connect 3 or more faces, - the faces connected to these edges. """ conn = self.edgeConnections() ed = (conn!=-1).sum(axis=1)>2 fa = unique(conn[ed]) return arange(len(ed))[ed], fa[fa!=-1] def isClosedManifold(self): """Check whether the TriSurface is a closed manifold.""" stype = self.surfaceType() return stype[0] and stype[1] def checkBorder(self): """Return the border of TriSurface as a set of segments.""" border = self.getEdges()[self.borderEdges()] if len(border) > 0: return connectedLineElems(border) else: return [] def border(self,): """Return the border(s) of TriSurface. The complete border of the surface is returned as a list of plex-2 Meshes. Each Mesh constitutes a continuous part of the border. """ return [ Mesh(self.coords,e) for e in self.checkBorder() ] def fillBorder(self,method='radial'): """Fill the border areas of a surface to make it closed. Returns a list of surfaces, each of which fills a singly connected part of the border of the input surface. Adding these surfaces to the original will create a closed surface. The surfaces will have property values set above those used in the parent surface. If the surface is already closed, an empty list is returned. There are two methods, 'radial' and 'border' corresponding with the methods of the surfaceInsideBorder. """ if self.prop is None: mprop = 1 else: mprop = self.prop.max()+1 return [ fillBorder(b,method).setProp(mprop+i) for i,b in enumerate(self.border()) ] def edgeCosAngles(self): """Return the cos of the angles over all edges. The surface should be a manifold (max. 2 elements per edge). Edges adjacent to only one element get cosangles = 1.0. """ conn = self.edgeConnections() # Bail out if some edge has more than two connected faces if conn.shape[1] != 2: raise RuntimeError,"TriSurface is not a manifold" angles = ones(self.nedges()) conn2 = (conn >= 0).sum(axis=-1) == 2 n = self.areaNormals()[1][conn[conn2]] angles[conn2] = dotpr(n[:,0],n[:,1]) return angles.clip(min=-1.,max=1.) def edgeAngles(self): """Return the angles over all edges (in degrees). It is the angle (0 to 180) between 2 face normals.""" return arccosd(self.edgeCosAngles()) def _compute_data(self): """Compute data for all edges and faces.""" if hasattr(self,'edglen'): return self.areaNormals() edg = self.coords[self.getEdges()] edglen = length(edg[:,1]-edg[:,0]) facedg = edglen[self.getElemEdges()] edgmin = facedg.min(axis=-1) edgmax = facedg.max(axis=-1) altmin = 2*self.areas / edgmax aspect = edgmax/altmin self.edglen,self.facedg,self.edgmin,self.edgmax,self.altmin,self.aspect = edglen,facedg,edgmin,edgmax,altmin,aspect def aspectRatio(self): """Return the apect ratio of the triangles of the surface. The aspect ratio of a triangle is the ratio of the longest edge over the smallest altitude of the triangle. Equilateral triangles have the smallest edge ratio (2 over square root 3). """ self._compute_data() return self.aspect def smallestAltitude(self): """Return the smallest altitude of the triangles of the surface.""" self._compute_data() return self.altmin def longestEdge(self): """Return the longest edge of the triangles of the surface.""" self._compute_data() return self.edgmax def shortestEdge(self): """Return the shortest edge of the triangles of the surface.""" self._compute_data() return self.edgmin def stats(self): """Return a text with full statistics.""" bbox = self.bbox() manifold,closed,mincon,maxcon = self.surfaceType() self._compute_data() area = self.area() s = """ Size: %s vertices, %s edges and %s faces Bounding box: min %s, max %s Minimal/maximal number of connected faces per edge: %s/%s Surface is%s a%s manifold Facet area: min %s; mean %s; max %s Edge length: min %s; mean %s; max %s Shortest altitude: %s; largest aspect ratio: %s """ % ( self.ncoords(),self.nedges(),self.nfaces(), bbox[0],bbox[1], mincon,maxcon, {True:'',False:' not'}[manifold],{True:' closed',False:''}[closed], self.areas.min(),self.areas.mean(),self.areas.max(), self.edglen.min(),self.edglen.mean(),self.edglen.max(), self.altmin.min(),self.aspect.max(), ) if manifold: angles = self.edgeAngles() # getAngles is currently removed # vangles = self.getAngles() if closed: volume = self.volume() s += """Angle between adjacent facets: min: %s; mean: %s; max: %s """ % ( angles.min(),angles.mean(),angles.max()) s += "Total area: %s; " % area if manifold and closed: s += "Enclosed volume: %s" % volume else: s += "No volume (not a closed manifold!)" return s def distanceOfPoints(self,X,return_points=False): """Find the distances of points X to the TriSurface. The distance of a point is either: - the closest perpendicular distance to the facets; - the closest perpendicular distance to the edges; - the closest distance to the vertices. X is a (nX,3) shaped array of points. If return_points = True, a second value is returned: an array with the closest (foot)points matching X. """ from timer import Timer t = Timer() # distance from vertices Vp = self.coords res = geomtools.vertexDistance(X,Vp,return_points) # OKdist, (OKpoints) dist = res[0] if return_points: points = res[1] print "Vertex distance: %s seconds" % t.seconds(True) #print dist # distance from edges Ep = self.coords[self.getEdges()] res = geomtools.edgeDistance(X,Ep,return_points) # OKpid, OKdist, (OKpoints) okE,distE = res[:2] closer = distE < dist[okE] #print okE,closer if closer.size > 0: dist[okE[closer]] = distE[closer] if return_points: points[okE[closer]] = res[2][closer] print "Edge distance: %s seconds" % t.seconds(True) #print dist # distance from faces Fp = self.coords[self.elems] res = geomtools.faceDistance(X,Fp,return_points) # OKpid, OKdist, (OKpoints) okF,distF = res[:2] closer = distF < dist[okF] #print okF,closer if closer.size > 0: dist[okF[closer]] = distF[closer] if return_points: points[okF[closer]] = res[2][closer] print "Face distance: %s seconds" % t.seconds(True) #print dist if return_points: return dist,points else: return dist ################## Transform surface ############################# # All transformations now return a new surface def offset(self,distance=1.): """Offset a surface with a certain distance. All the nodes of the surface are translated over a specified distance along their normal vector. """ n = self.avgVertexNormals() coordsNew = self.coords + n*distance return TriSurface(coordsNew,self.getElems(),prop=self.prop) ## def reflect(self,*args,**kargs): ## """Reflect the Surface in direction dir against plane at pos. ## Parameters: ## - `dir`: int: direction of the reflection (default 0) ## - `pos`: float: offset of the mirror plane from origin (default 0.0) ## - `inplace`: boolean: change the coordinates inplace (default False) ## - `reverse`: boolean: revert the normals of the triangles ## (default True). ## Reflection of the coordinates of a 2D Mesh reverses the surface ## sides. Setting this parameter True will cause an extra ## reversion. This is what is expected in most surface mirroring ## operations. ## """ ## return Mesh.reflect(self,*args,**kargs) ################## Partitioning a surface ############################# def edgeFront(self,startat=0,okedges=None,front_increment=1): """Generator function returning the frontal elements. startat is an element number or list of numbers of the starting front. On first call, this function returns the starting front. Each next() call returns the next front. front_increment determines how the property increases at each frontal step. There is an extra increment +1 at each start of a new part. Thus, the start of a new part can always be detected by a front not having the property of the previous plus front_increment. """ #print("FRONT_INCREMENT %s" % front_increment) p = -ones((self.nfaces()),dtype=int) if self.nfaces() <= 0: return # Construct table of elements connected to each edge conn = self.edgeConnections() # Bail out if some edge has more than two connected faces if conn.shape[1] != 2: pf.warning("Surface is not a manifold") return # Check size of okedges if okedges is not None: if okedges.ndim != 1 or okedges.shape[0] != self.nedges(): raise ValueError,"okedges has incorrect shape" # Remember edges left for processing todo = ones((self.nedges(),),dtype=bool) elems = clip(asarray(startat),0,self.nfaces()) prop = 0 while elems.size > 0: # Store prop value for current elems p[elems] = prop yield p prop += front_increment # Determine border edges = unique(self.getElemEdges()[elems]) edges = edges[todo[edges]] if edges.size > 0: # flag edges as done todo[edges] = 0 # take connected elements if okedges is None: elems = conn[edges].ravel() else: elems = conn[edges[okedges[edges]]].ravel() elems = elems[(elems >= 0) * (p[elems] < 0) ] if elems.size > 0: continue # No more elements in this part: start a new one #print("NO MORE ELEMENTS") elems = where(p<0)[0] if elems.size > 0: # Start a new part elems = elems[[0]] prop += 1 def walkEdgeFront(self,startat=0,nsteps=-1,okedges=None,front_increment=1): """Grow a selection using a frontal method. Starting from element `startat`, grow a selection `nsteps` times following the common edges of the triangles. The property of each new front is augmented by `front_increment`. """ for p in self.edgeFront(startat=startat,okedges=okedges,front_increment=front_increment): if nsteps > 0: nsteps -= 1 elif nsteps == 0: break return p def growSelection(self,sel,mode='node',nsteps=1): """Grow a selection of a surface. `p` is a single element number or a list of numbers. The return value is a list of element numbers obtained by growing the front `nsteps` times. The `mode` argument specifies how a single frontal step is done: - 'node' : include all elements that have a node in common, - 'edge' : include all elements that have an edge in common. """ if mode == 'node': p = self.walkNodeFront(startat=sel,nsteps=nsteps) elif mode == 'edge': p = self.walkEdgeFront(startat=sel,nsteps=nsteps) return where(p>=0)[0] def partitionByEdgeFront(self,okedges,firstprop=0,startat=0): """Detect different parts of the surface using a frontal method. okedges flags the edges where the two adjacent triangles are to be in the same part of the surface. startat is a list of elements that are in the first part. The partitioning is returned as a property type array having a value corresponding to the part number. The lowest property number will be firstprop. """ return firstprop + self.walkEdgeFront(startat=startat,okedges=okedges,front_increment=0) def partitionByAngle(self,angle=60.,firstprop=0,sort='number',sortedbyarea=None): """Partition the surface by splitting it at sharp edges. The surface is partitioned in parts in which all elements can be reach without ever crossing a sharp edge angle. More precisely, any two elements that can be connected by a line not crossing an edge between two elements having their normals differ more than angle (in degrees), will belong to the same part. The partitioning is returned as a property type array having a value corresponding to the part number. The lowest property number will be firstprop. By default the parts are assigned property numbers in decreasing order of the number of triangles in the part. Setting the sort argument to 'area' will sort the parts according to decreasing area. Any other value will return the parts unsorted. """ if sortedbyarea is not None: pf.warning("The sortedbyarea parameter is obsolete. Use the sort parameter instead.") if sortedbyarea: sort = 'area' conn = self.edgeConnections() # Flag edges that connect two faces conn2 = (conn >= 0).sum(axis=-1) == 2 # compute normals and flag small angles over edges cosangle = cosd(angle) a, n = self.areaNormals() n = n[conn[conn2]] small_angle = ones(conn2.shape,dtype=bool) small_angle[conn2] = dotpr(n[:,0],n[:,1]) >= cosangle p = self.partitionByEdgeFront(small_angle) if sort == 'number': h = histogram(p,list(unique(p))+[p.max()+1])[0] elif sort == 'area': h = [a[p==j].sum() for j in unique(p)] else: sort = False if sort: srt = argsort(h)[::-1] inv = inverseUniqueIndex(srt) p = inv[p] return firstprop + p def cutWithPlane(self,*args,**kargs): """Cut a surface with a plane or a set of planes. Cuts the surface with one or more plane and returns either one side or both. Parameters: - `p`,`n`: a point and normal vector defining the cutting plane. p and n can be sequences of points and vector, allowing to cut with multiple planes. Both p and n have shape (3) or (npoints,3). The parameters are the same as in :meth:`Formex.CutWithPlane`. The returned surface will have its normals fixed wherever possible. """ return TriSurface(self.toFormex().cutWithPlane(*args,**kargs)).fixNormals() def connectedElements(self,target,elemlist=None): """Return the elements from list connected with target""" if elemlist is None: A = self elemlist = arange(self.nelems()) else: A = self.select(elemlist) if target not in elemlist: return [] p = A.partitionByConnection() prop = p[elemlist == target] return elemlist[p==prop] def intersectionWithPlane(self,p,n): """Return the intersection lines with plane (p,n). Returns a plex-2 mesh with the line segments obtained by cutting all triangles of the surface with the plane (p,n) p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components. The return value is a plex-2 Mesh where the line segments defining the intersection are sorted to form continuous lines. The Mesh has property numbers such that all segments forming a single continuous part have the same property value. The splitProp() method can be used to get a list of Meshes. """ n = asarray(n) p = asarray(p) # The vertices are classified based on their distance d from the plane: # - inside: d = 0 # - up: d > 0 # - down: d < 0 # First, reduce the surface to the part intersecting with the plane: # remove triangles with all up or all down vertices d = self.distanceFromPlane(p,n) d_ele = d[self.elems] ele_all_up = (d_ele > 0.).all(axis=1) ele_all_do = (d_ele < 0.).all(axis=1) S = self.cclip(ele_all_up+ele_all_do) # If there is no intersection, we're done if S.nelems() == 0: return Mesh(Coords(),[]) Mparts = [] coords = S.coords edg = S.getEdges() fac = S.getElemEdges() ele = S.elems d = S.distanceFromPlane(p,n) # Get the edges intersecting with the plane: 1 up and 1 down vertex d_edg = d[edg] edg_1_up = (d_edg > 0.).sum(axis=1) == 1 edg_1_do = (d_edg < 0.).sum(axis=1) == 1 w = edg_1_up * edg_1_do ind = where(w)[0] # compute the intersection points if ind.size != 0: rev = inverseUniqueIndex(ind) M = Mesh(S.coords,edg[w]) x = geomtools.intersectionPointsSWP(M.toFormex().coords,p,n,mode='pair',return_all=True).reshape(-1,3) # For each triangle, compute the number of cutting edges cut = w[fac] ncut = cut.sum(axis=1) # Split the triangles based on the number of inside vertices d_ele = d[ele] ins = d_ele == 0. nins = ins.sum(axis=1) ins0,ins1,ins2,ins3 = [ where(nins==i)[0] for i in range(4) ] # No inside vertices -> 2 cutting edges if ins0.size > 0: cutedg = fac[ins0][cut[ins0]].reshape(-1,2) e0 = rev[cutedg] Mparts.append(Mesh(x,e0).compact()) # One inside vertex if ins1.size > 0: ncut1 = ncut[ins1] cut10,cut11 = [ where(ncut1==i)[0] for i in range(2) ] # 0 cutting edges: does not generate a line segment # 1 cutting edge if cut11.size != 0: e11ins = ele[ins1][cut11][ins[ins1][cut11]].reshape(-1,1) cutedg = fac[ins1][cut11][cut[ins1][cut11]].reshape(-1,1) e11cut = rev[cutedg] x11 = Coords.concatenate([coords,x],axis=0) e11 = concatenate([e11ins,e11cut+len(coords)],axis=1) Mparts.append(Mesh(x11,e11).compact()) # Two inside vertices -> 0 cutting edges if ins2.size > 0: e2 = ele[ins2][ins[ins2]].reshape(-1,2) Mparts.append(Mesh(coords,e2).compact()) # Three inside vertices -> 0 cutting edges if ins3.size > 0: insedg = fac[ins3].reshape(-1) insedg.sort(axis=0) double = insedg == roll(insedg,1,axis=0) insedg = setdiff1d(insedg,insedg[double]) if insedg.size != 0: e3 = edg[insedg] Mparts.append(Mesh(coords,e3).compact()) # Done with getting the segments if len(Mparts) == 0: # No intersection: return empty mesh return Mesh(Coords(),[]) elif len(Mparts) == 1: M = Mparts[0] else: M = Mesh.concatenate(Mparts) # Remove degenerate and duplicate elements M = Mesh(M.coords,M.elems.removeDegenerate().removeDuplicate()) # Split in connected loops parts = connectedLineElems(M.elems) prop = concatenate([ [i]*p.nelems() for i,p in enumerate(parts)]) elems = concatenate(parts,axis=0) return Mesh(M.coords,elems,prop=prop) def slice(self,dir=0,nplanes=20): """Intersect a surface with a sequence of planes. A sequence of nplanes planes with normal dir is constructed at equal distances spread over the bbox of the surface. The return value is a list of intersectionWithPlane() return values, i.e. a list of Meshes, one for every cutting plane. In each Mesh the simply connected parts are identified by property number. """ o = self.center() if type(dir) is int: dir = unitVector(dir) xmin,xmax = self.coords.directionalExtremes(dir,o) P = Coords.interpolate(xmin,xmax,nplanes) return [ self.intersectionWithPlane(p,dir) for p in P ] ################## Smooth a surface ############################# def smooth(self,method='lowpass',iterations=1,lambda_value=0.5,neighbourhood=1,alpha=0.0,beta=0.2,verbose=False): """Smooth the surface. Returns a TriSurface which is a smoothed version of the original. Three smoothing methods are available: 'lowpass', 'laplace', and 'gts'. The first two are built-in, the latter uses the external command `gtssmooth`. Parameters: - `method`: 'lowpass', 'laplace', or 'gts' - `iterations`: int: number of iterations - `lambda_value`: float: lambda value used in the filters Extra parameters for 'lowpass' and 'laplace': - `neighbourhood`: int: maximum number of edges to follow to define node neighbourhood Extra parameters for 'laplace': - `alpha`, `beta`: float: parameters for the laplace method. Extra parameters for 'gts': - `verbose`: boolean: requests more verbose output of the `gtssmooth` command Returns: the smoothed TriSurface """ method = method.lower() if method == 'gts': return self._gts_smooth(iterations,lambda_value,verbose) # find adjacency adj = adjacencyArrays(self.getEdges(),nsteps=neighbourhood)[1:] adj = column_stack(adj) # find interior vertices bound_edges = self.borderEdgeNrs() inter_vertex = resize(True,self.ncoords()) inter_vertex[unique(self.getEdges()[bound_edges])] = False # calculate weights w = ones(adj.shape,dtype=float) w[adj<0] = 0. val = (adj>=0).sum(-1).reshape(-1,1) w /= val w = w.reshape(adj.shape[0],adj.shape[1],1) # recalculate vertices if method == 'laplace': xo = self.coords x = self.coords.copy() for step in range(iterations): xn = x + lambda_value*(w*(x[adj]-x.reshape(-1,1,3))).sum(1) xd = xn - (alpha*xo + (1-alpha)*x) x[inter_vertex] = xn[inter_vertex] - (beta*xd[inter_vertex] + (1-beta)*(w[inter_vertex]*xd[adj[inter_vertex]]).sum(1)) else: # default: lowpass k = 0.1 mu_value = -lambda_value/(1-k*lambda_value) x = self.coords.copy() for step in range(iterations): x[inter_vertex] = x[inter_vertex] + lambda_value*(w[inter_vertex]*(x[adj[inter_vertex]]-x[inter_vertex].reshape(-1,1,3))).sum(1) x[inter_vertex] = x[inter_vertex] + mu_value*(w[inter_vertex]*(x[adj[inter_vertex]]-x[inter_vertex].reshape(-1,1,3))).sum(1) return TriSurface(x,self.elems,prop=self.prop) def smoothLowPass(self,iterations=2,lambda_value=0.5,neighbours=1): """Apply a low pass smoothing to the surface.""" return self.smooth('lowpass',iterations/2,lambda_value,neighbours) def smoothLaplaceHC(self,iterations=2,lambda_value=0.5,alpha=0.,beta=0.2,neighbours=1): """Apply Laplace smoothing with shrinkage compensation to the surface.""" return self.smooth('lowpass',iterations,lambda_value,neighbours,alpha,beta) ################################################################### ## Methods using admesh/GTS/tetgen ##################################### def fixNormals(self): """Fix the orientation of the normals. Some surface operations may result in improperly oriented normals. This tries to reverse improperly oriented normals so that a single oriented surface is achieved. It only works on a closed surface. In the current version, this uses the external program `admesh`, so this should be installed on the machine. If the surface was a (possibly non-orientable) manifold, the result will be an orientable manifold. This is a necessary condition for the `gts` methods to be applicable. """ if self.nelems() == 0: # Protect against impossible file handling for empty surfaces return self tmp = tempfile.mktemp('.stl') pf.message("Writing temp file %s" % tmp) self.write(tmp,'stl') tmp1 = tempfile.mktemp('.off') cmd = "admesh -d --write-off='%s' '%s'" % (tmp1,tmp) pf.message("Fixing surface normals with command\n %s" % cmd) sta,out = utils.runCommand(cmd) pf.message("Reading result from %s" % tmp1) S = TriSurface.read(tmp1) os.remove(tmp) os.remove(tmp1) return S.setProp(self.prop) def check(self,matched=True): """Check the surface using gtscheck. Uses `gtscheck` to check whether the surface is an orientable, non self-intersecting manifold. This is a necessary condition the `gts` methods: split, coarsen, refine, boolean. (Additionally, the surface should be closed, wich can be checked with :meth:`isClosedManifold`). Returns a tuple of: - an integer return code with the value: - 0: the surface is an orientable, non self-intersecting manifold. - 2: the surface is not an orientable manifold. This may be due to misoriented normals. The :meth:`fixNormals` and :meth:`reverse` methods may be used to help fixing the problem in such case. - 3: the surface is an orientable manifold but is self-intersecting. The self intersecting triangles are returned as the second return value. - the intersecting triangles in the case of a return code 3, else None. If matched==True, intersecting triangles are returned as element indices of self, otherwise as a separate TriSurface object. """ tmp = tempfile.mktemp('.gts') self.write(tmp,'gts') cmd = "gtscheck < %s" % tmp sta,out = utils.runCommand(cmd,False) os.remove(tmp) if sta == 0: pf.message('The surface is an orientable non self-intersecting manifold') return sta, None if sta==2: pf.message('The surface is not an orientable manifold (this may be due to badly oriented normals)') return sta, None if sta==3: pf.message('The surface is an orientable manifold but is self-intersecting') tmp = tempfile.mktemp('.gts') pf.message("Writing temp file %s" % tmp) fil = open(tmp,'w') fil.write(out) fil.close() Si = TriSurface.read(tmp) os.remove(tmp) if matched: return sta, self.matchCentroids(Si) else: return sta, Si else: pf.message('Status of gtscheck not understood') return sta, None def checkSelfIntersectionsWithTetgen(self,verbose=False): """check self intersections using tetgen Returns couples of intersecting triangles """ from plugins.tetgen import writeSurface cmd = 'tetgen -d ' tmp = tempfile.mktemp('') print tmp pf.message("Writing temp file %s" % tmp) writeSurface(tmp,self.coords, self.elems) if verbose: cmd += '-V ' cmd=cmd+ tmp pf.message("Checking with command\n %s" % cmd) sta,out = utils.runCommand(cmd) if sta: pf.message('Tetgen got an error') return sta try: os.remove(tmp+'.node') os.remove(tmp+'.smesh') os.remove(tmp+'.1.face') os.remove(tmp+'.1.node') except: pass if sta or verbose: pf.message(out) return asarray( [int(l.split(' ')[0]) for l in out.split('acet #')[1:]]).reshape(-1, 2)-1 def split(self,base,verbose=False): """Split the surface using gtssplit. Splits the surface into connected and manifold components. This uses the external program `gtssplit`. The surface should be a closed orientable non-intersecting manifold. Use the :meth:`check` method to find out. This method creates a series of files with given base name, each file contains a single connected manifold. """ cmd = 'gtssplit -v %s' % base if verbose: cmd += ' -v' tmp = tempfile.mktemp('.gts') pf.message("Writing temp file %s" % tmp) self.write(tmp,'gts') pf.message("Splitting with command\n %s" % cmd) cmd += ' < %s' % tmp sta,out = utils.runCommand(cmd) os.remove(tmp) if sta or verbose: pf.message(out) # # WE SHOULD READ THIS FILES BACK !!! # def coarsen(self,min_edges=None,max_cost=None, mid_vertex=False, length_cost=False, max_fold=1.0, volume_weight=0.5, boundary_weight=0.5, shape_weight=0.0, progressive=False, log=False, verbose=False): """Coarsen the surface using gtscoarsen. Construct a coarsened version of the surface. This uses the external program `gtscoarsen`. The surface should be a closed orientable non-intersecting manifold. Use the :meth:`check` method to find out. Parameters: - `min_edges`: int: stops the coarsening process if the number of edges was to fall below it - `max_cost`: float: stops the coarsening process if the cost of collapsing an edge is larger - `mid_vertex`: boolean: use midvertex as replacement vertex instead of the default, which is a volume optimized point - `length_cost`: boolean: use length^2 as cost function instead of the default optimized point cost - `max_fold`: float: maximum fold angle in degrees - `volume_weight`: float: weight used for volume optimization - `boundary_weight`: float: weight used for boundary optimization - `shape_weight`: float: weight used for shape optimization - `progressive`: boolean: write progressive surface file - `log`: boolean: log the evolution of the cost - `verbose`: boolean: print statistics about the surface """ if min_edges is None and max_cost is None: min_edges = self.nedges() / 2 cmd = 'gtscoarsen' if min_edges: cmd += ' -n %d' % min_edges if max_cost: cmd += ' -c %f' % max_cost if mid_vertex: cmd += ' -m' if length_cost: cmd += ' -l' if max_fold: cmd += ' -f %f' % max_fold cmd += ' -w %f' % volume_weight cmd += ' -b %f' % boundary_weight cmd += ' -s %f' % shape_weight if progressive: cmd += ' -p' if log: cmd += ' -L' if verbose: cmd += ' -v' tmp = tempfile.mktemp('.gts') tmp1 = tempfile.mktemp('.gts') pf.message("Writing temp file %s" % tmp) self.write(tmp,'gts') pf.message("Coarsening with command\n %s" % cmd) cmd += ' < %s > %s' % (tmp,tmp1) sta,out = utils.runCommand(cmd) os.remove(tmp) if sta or verbose: pf.message(out) pf.message("Reading coarsened model from %s" % tmp1) S = TriSurface.read(tmp1) os.remove(tmp1) return S def refine(self,max_edges=None,min_cost=None,log=False,verbose=False): """Refine the surface using gtsrefine. Construct a refined version of the surface. This uses the external program `gtsrefine`. The surface should be a closed orientable non-intersecting manifold. Use the :meth:`check` method to find out. Parameters: - `max_edges`: int: stop the refining process if the number of edges exceeds this value - `min_cost`: float: stop the refining process if the cost of refining an edge is smaller - `log`: boolean: log the evolution of the cost - `verbose`: boolean: print statistics about the surface """ if max_edges is None and min_cost is None: max_edges = self.nedges() * 2 cmd = 'gtsrefine' if max_edges: cmd += ' -n %d' % max_edges if min_cost: cmd += ' -c %f' % min_cost if log: cmd += ' -L' if verbose: cmd += ' -v' tmp = tempfile.mktemp('.gts') tmp1 = tempfile.mktemp('.gts') pf.message("Writing temp file %s" % tmp) self.write(tmp,'gts') pf.message("Refining with command\n %s" % cmd) cmd += ' < %s > %s' % (tmp,tmp1) sta,out = utils.runCommand(cmd) os.remove(tmp) if sta or verbose: pf.message(out) pf.message("Reading refined model from %s" % tmp1) S = TriSurface.read(tmp1) os.remove(tmp1) return S def _gts_smooth(self,iterations=1,lambda_value=0.5,verbose=False): """Smooth the surface using gtssmooth. Smooth a surface by applying iterations of a Laplacian filter. This uses the external program `gtssmooth`. The surface should be a closed orientable non-intersecting manifold. Use the :meth:`check` method to find out. Parameters: - `lambda_value`: float: Laplacian filter parameter - `iterations`: int: number of iterations - `verbose`: boolean: print statistics about the surface See also: :meth:`smoothLowPass`, :meth:`smoothLaplaceHC` """ cmd = 'gtssmooth' # if fold_smoothing: # cmd += ' -f %s' % fold_smoothing cmd += ' %s %s' % (lambda_value,iterations) if verbose: cmd += ' -v' tmp = tempfile.mktemp('.gts') tmp1 = tempfile.mktemp('.gts') pf.message("Writing temp file %s" % tmp) self.write(tmp,'gts') pf.message("Smoothing with command\n %s" % cmd) cmd += ' < %s > %s' % (tmp,tmp1) sta,out = utils.runCommand(cmd) os.remove(tmp) if sta or verbose: pf.message(out) pf.message("Reading smoothed model from %s" % tmp1) S = TriSurface.read(tmp1) os.remove(tmp1) return S def boolean(self,surf,op,intersection_curve=False,check=False,verbose=False): """Perform a boolean operation with another surface. Boolean operations between surfaces are a basic operation in free surface modeling. Both surfaces should be closed orientable non-intersecting manifolds. Use the :meth:`check` method to find out. The boolean operations are set operations on the enclosed volumes: union('+'), difference('-') or intersection('*'). Parameters: - `intersection_curve`: boolean: output an OOGL (Geomview) representation of the curve intersection of the surfaces - `check`: boolean: check that the surfaces are not self-intersecting; if one of them is, the set of self-intersecting faces is written (as a GtsSurface) on standard output - `verbose`: boolean: print statistics about the surface """ cmd = 'gtsset' ops = {'+':'union', '-':'diff', '*':'inter'} if intersection_curve: cmd += ' -i' if check: cmd += ' -s' if verbose: cmd += ' -v' cmd += ' '+ops[op] tmp = tempfile.mktemp('.gts') tmp1 = tempfile.mktemp('.gts') tmp2 = tempfile.mktemp('.stl') pf.message("Writing temp file %s" % tmp) self.write(tmp,'gts') pf.message("Writing temp file %s" % tmp1) surf.write(tmp1,'gts') pf.message("Performing boolean operation with command\n %s" % cmd) cmd += ' %s %s | gts2stl > %s' % (tmp,tmp1,tmp2) sta,out = utils.runCommand(cmd) os.remove(tmp) os.remove(tmp1) if sta or verbose: pf.message(out) pf.message("Reading result from %s" % tmp2) S = TriSurface.read(tmp2) os.remove(tmp2) return S ########################################################################## ################# Non-member and obsolete functions ###################### def read_error(cnt,line): """Raise an error on reading the stl file.""" raise RuntimeError,"Invalid .stl format while reading line %s\n%s" % (cnt,line) def degenerate(area,norm): """Return a list of the degenerate faces according to area and normals. A face is degenerate if its surface is less or equal to zero or the normal has a nan. """ return unique(concatenate([where(area<=0)[0],where(isnan(norm))[0]])) def read_stla(fn,dtype=Float,large=False,guess=True): """Read an ascii .stl file into an [n,3,3] float array. If the .stl is large, read_ascii_large() is recommended, as it is a lot faster. """ if large: return read_ascii_large(fn,dtype=dtype) if guess: n = utils.countLines(fn) / 7 # ASCII STL has 7 lines per triangle else: n = 100 f = open(fn,'r') a = zeros(shape=[n,3,3],dtype=dtype) x = zeros(shape=[3,3],dtype=dtype) i = 0 j = 0 cnt = 0 finished = False for line in f: cnt += 1 s = line.strip().split() if s[0] == 'vertex': if j >= 3: read_error(cnt,line) x[j] = map(float,s[1:4]) j += 1 elif s[0] == 'outer': j = 0 elif s[0] == 'endloop': a[i] = x elif s[0] == 'facet': if i >= a.shape[0]: # increase the array size a.resize([2*a.shape[0],3,3]) elif s[0] == 'endfacet': i += 1 elif s[0] == 'solid': pass elif s[0] == 'endsolid': finished = True break if f: f.close() if finished: return a[:i] raise RuntimeError,"Incorrect stl file: read %d lines, %d facets" % (cnt,i) def read_ascii_large(fn,dtype=Float): """Read an ascii .stl file into an [n,3,3] float array. This is an alternative for read_ascii, which is a lot faster on large STL models. It requires the 'awk' command though, so is probably only useful on Linux/UNIX. It works by first transforming the input file to a .nodes file and then reading it through numpy's fromfile() function. """ tmp = '%s.nodes' % fn utils.runCommand("awk '/^[ ]*vertex[ ]+/{print $2,$3,$4}' '%s' | d2u > '%s'" % (fn,tmp)) nodes = fromfile(tmp,sep=' ',dtype=dtype).reshape((-1,3,3)) return nodes def off_to_tet(fn): """Transform an .off model to tetgen (.node/.smesh) format.""" pf.message("Transforming .OFF model %s to tetgen .smesh" % fn) nodes,elems = read_off(fn) write_node_smesh(utils.changeExt(fn,'.smesh'),nodes,elems) def find_row(mat,row,nmatch=None): """Find all rows in matrix matching given row.""" if nmatch is None: nmatch = mat.shape[1] return where((mat == row).sum(axis=1) == nmatch)[0] def find_nodes(nodes,coords): """Find nodes with given coordinates in a node set. nodes is a (nnodes,3) float array of coordinates. coords is a (npts,3) float array of coordinates. Returns a (n,) integer array with ALL the node numbers matching EXACTLY ALL the coordinates of ANY of the given points. """ return concatenate([ find_row(nodes,c) for c in coords]) def find_first_nodes(nodes,coords): """Find nodes with given coordinates in a node set. nodes is a (nnodes,3) float array of coordinates. coords is a (npts,3) float array of coordinates. Returns a (n,) integer array with THE FIRST node number matching EXACTLY ALL the coordinates of EACH of the given points. """ res = [ find_row(nodes,c) for c in coords ] return array([ r[0] for r in res ]) def find_triangles(elems,triangles): """Find triangles with given node numbers in a surface mesh. elems is a (nelems,3) integer array of triangles. triangles is a (ntri,3) integer array of triangles to find. Returns a (ntri,) integer array with the triangles numbers. """ magic = elems.max()+1 mag1 = enmagic3(elems,magic) mag2 = enmagic3(triangles,magic) nelems = elems.shape[0] srt = mag1.argsort() old = arange(nelems)[srt] mag1 = mag1[srt] pos = mag1.searchsorted(mag2) tri = where(mag1[pos]==mag2, old[pos], -1) return tri def remove_triangles(elems,remove): """Remove triangles from a surface mesh. elems is a (nelems,3) integer array of triangles. remove is a (nremove,3) integer array of triangles to remove. Returns a (nelems-nremove,3) integer array with the triangles of nelems where the triangles of remove have been removed. """ pf.message("Removing %s out of %s triangles" % (remove.shape[0],elems.shape[0])) magic = elems.max()+1 mag1 = enmagic3(elems,magic) mag2 = enmagic3(remove,magic) mag1.sort() mag2.sort() nelems = mag1.shape[0] pos = mag1.searchsorted(mag2) mag1[pos] = -1 mag1 = mag1[mag1 >= 0] elems = demagic3(mag1,magic) pf.message("Actually removed %s triangles, leaving %s" % (nelems-mag1.shape[0],elems.shape[0])) return elems ##################################################################### ### Some simple surfaces ### ### Should go to simple module ? def Rectangle(nx,ny): """Create a plane rectangular surface consisting of a nx,ny grid.""" F = Formex('3:012934').replic2(nx,ny,1,1) return TriSurface(F) def Cube(): """Create the surface of a cube Returns a TriSurface representing the surface of a unit cube. Each face of the cube is represented by two triangles. """ back = Formex('3:012934') fb = back.reverse() + back.translate(2,1) faces = fb + fb.rollAxes(1) + fb.rollAxes(2) return TriSurface(faces) def Sphere(level=4,verbose=False,filename=None): """Create a spherical surface by calling the gtssphere command. If a filename is given, it is stored under that name, else a temporary file is created. Beware: this may take a lot of time if level is 8 or higher. """ cmd = 'gtssphere ' if verbose: cmd += ' -v' cmd += ' %s' % level if filename is None: tmp = tempfile.mktemp('.gts') else: tmp = filename cmd += ' > %s' % tmp pf.message("Writing file %s" % tmp) sta,out = utils.runCommand(cmd) if sta or verbose: pf.message(out) pf.message("Reading model from %s" % tmp) S = TriSurface.read(tmp) if filename is None: os.remove(tmp) return S ####### Unsupported functions needing cleanup ####################### def checkDistanceLinesPointsTreshold(p, q, m, dtresh): """_p are np points, q m are nl lines, dtresh are np distances. It returns the indices of lines, points which are in a distance < dtresh. The distance point-line is calculated using Pitagora as it seems the fastest way.""" cand=[] m=normalize(m) for i in range(q.shape[0]): hy= p-q[i] dpl=(abs(length(hy)**2.-dotpr(hy, m[i])**2.))**0.5 wd= dpl<=dtresh cand.append([i,where(wd)[0]] ) candwl= concatenate([repeat(cand[i][0], cand[i][1].shape[0]) for i in range(len(cand))]) candwt= concatenate([cand[i][1] for i in range(len(cand))]) return candwl, candwt def intersectLineWithPlaneOne2One(q,m,p,n): """_it returns for each pair of line(q,m) and plane (p,n) the point of intersection. plane: (x-p)n=0, line: x=q+t m. It find the scalar t and returns the point.""" t=dotpr(n, (p-q))/dotpr(n, m) return q+m*t[:, newaxis] # # Efficiency should be compared with geomtools.insideTriangle # @deprecation("checkPointInsideTriangleOne2One is deprecated: use geomtools.insideTriangle instead") def checkPointInsideTriangleOne2One(tpi, pi, atol=1.e-5): """_return a 1D boolean with the same dimension of tpi and pi. The value [i] is True if the point pi[i] is inside the triangle tpi[i]. It uses areas to check it. """ print tpi.shape, pi.shape tpi3= column_stack([tpi[:, 0], tpi[:, 1], pi, tpi[:, 0], pi, tpi[:, 2], pi, tpi[:, 1], tpi[:, 2]]).reshape(pi.shape[0]*3, 3, 3) #areas Atpi3=(length(cross(tpi3[:,1]-tpi3[:,0],tpi3[:,2]-tpi3[:,1]))*0.5).reshape(pi.shape[0], 3).sum(axis=1)#area and sum Atpi=length(cross(tpi[:,1]-tpi[:,0],tpi[:,2]-tpi[:,1]))*0.5#area return -(Atpi3>Atpi+atol)#True mean point inside triangle def intersectSurfaceWithLines(ts, qli, mli, atol=1.e-5): """_it takes a TriSurface ts and a set of lines ql,ml and intersect the lines with the TriSurface. It returns the points of intersection and the indices of the intersected line and triangle. TODO: the slowest part is computing the distances of lines from triangles, can it be faster? """ #find Bounding Sphere for each triangle tsr, tsc, tsn = geomtools.triangleBoundingCircle(ts.coords[ts.elems]) wl, wt=checkDistanceLinesPointsTreshold(tsc,qli, mli, tsr)#slow part #find the intersection points xc only for the candidates wl,wt npl= ts.areaNormals()[1] xc= intersectLineWithPlaneOne2One(qli[wl],mli[wl],tsc[wt],npl[wt]) #check if each intersection is really inside the triangle tsw=ts.select(wt) tsw=tsw.coords[tsw.elems] #xIn = checkPointInsideTriangleOne2One(tsw, xc, atol) xIn = geomtools.insideTriangle(tsw,xc[newaxis,...]).reshape(-1) #takes only intersections that fall inside the triangle return xc[xIn], wl[xIn], wt[xIn] def intersectSurfaceWithSegments(s1, segm, atol=1.e-5): """_it takes a TriSurface ts and a set of segments (-1,2,3) and intersect the segments with the TriSurface. It returns the points of intersections and, for each point, the indices of the intersected segment and triangle""" p, il, it=intersectSurfaceWithLines(s1, segm[:, 0], normalize(segm[:, 1]-segm[:, 0]),atol) win= length(p-segm[:, 0][il])+ length(p-segm[:, 1][il])< length(segm[:, 1][il]-segm[:, 0][il])+atol return p[win], il[win], it[win] # For compatibility with older project files, this can be uncommented # Surface = TriSurface if __name__ == '__main__': f = open('unit_triangle.stl','r') a = read_ascii(f) f.close() print(a) # End pyformex-0.8.6/pyformex/plugins/plot2d.py0000644000211500021150000000421611705104656020352 0ustar benebene00000000000000# $Id: plot2d.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """plot2d.py Generic 2D plotting functions for pyFormex. """ import pyformex from pyformex import utils from numpy import * def showHistogram(x,y,txt,**options): """Show a histogram of x,y data. """ plot2d_system = pyformex.cfg['gui/plot2d'] if plot2d_system == 'gnuplot': if not utils.hasModule('gnuplot'): error("You do not have the Python Gnuplot module installed.\nI can not draw the requested plot.") return import Gnuplot maxlen = min(len(x),len(y)) data = Gnuplot.Data(x[:maxlen],y[:maxlen],title=txt, with_='histeps') g = Gnuplot.Gnuplot(persist=1) g.title('pyFormex histogram: %s' % txt) g.plot(data) elif plot2d_system == 'qwt': pass #from PyQt4.Qwt5.qplt import * def createHistogram(data,cumulative=False,**kargs): """Create a histogram from data """ y,x = histogram(data,**kargs) if cumulative: y = y.cumsum() return y,x # End pyformex-0.8.6/pyformex/plugins/jobs_menu.py0000644000211500021150000002413111705104656021125 0ustar benebene00000000000000# $Id: jobs_menu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## import pyformex as pf from gui import menu from gui.widgets import simpleInputItem as I import utils import os from numpy import * from formex import * from gui.draw import * from gui.colors import * from subprocess import call def about(): showInfo("""Jobs.py This is pyFormex plugin allowing the user to - submit computational jobs to a cluster, - check available job results on a remote host, - copy job results to the local workstation, - execute a command on the remote host. While primarily intended for use with the BuMPer cluster at IBiTech-bioMMeda, we have made this plugin available to the general public, as an example of how to integrate external commands and hosts into a pyFormex menu. In order for these commands to work, you need to have `ssh` access to the host system. """) def configure(): from gui.prefMenu import updateSettings from gui.widgets import simpleInputItem as I, groupInputItem as G, tabInputItem as T dia = None def close(): dia.close() def accept(save=False): dia.acceptData() res = dia.results res['_save_'] = save if res['_addhost_']: res['jobs/hosts'] = pf.cfg['jobs/hosts'] + [ res['_addhost_'] ] res['jobs/host'] = res['_addhost_'] pf.debug(res) updateSettings(res) def acceptAndSave(): accept(save=True) def autoSettings(keylist): return [I(k,pf.cfg[k]) for k in keylist] jobs_settings = [ I('jobs/host',pf.cfg.get('jobs/host','localhost'),text="Host",tooltip="The host machine where your job input/output files are located.",choices=pf.cfg.get('jobs/hosts',['localhost'])), #,buttons=[('Add Host',addHost)]), I('jobs/inputdir',pf.cfg.get('jobs/inputdir','bumper/requests'),text="Input directory"), I('jobs/outputdir',pf.cfg.get('jobs/outputdir','bumper/results'),text="Output directory"), I('_addhost_','',text="New host",tooltip="To set a host name that is not yet in the list of hosts, you can simply fill it in here."), ] dia = widgets.InputDialog( caption='pyFormex Settings', store=pf.cfg, items=jobs_settings, actions=[ ('Close',close), ('Accept and Save',acceptAndSave), ('Accept',accept), ]) dia.show() def getSubdirs(host,userdir): """Get a list of all subdirs in userdir on host. The host should be a machine where the user has ssh access. The userdir is relative to the user's home dir. """ cmd = "ssh %s 'cd %s;ls -F|egrep \".*/\"'" % (host,userdir) sta,out = utils.runCommand(cmd,False) if sta: out = '' dirs = out.split('\n') dirs = [ j.strip('/') for j in dirs ] return dirs def getFiles(host,userdir,files,targetdir): """Copy files from userdir on host to targetdir. files is a list of file names. """ files = [ '%s:%s/%s' % (host,userdir.rstrip('/'),f) for f in files ] cmd = "scp %s %s" % (' '.join(files),targetdir) sta,out = utils.runCommand(cmd) return sta==0 def remoteCommand(host=None,command=None): """Execute a remote command. host: the hostname where the command is executed command: the command line """ if host is None or command is None: res = askItems( [ I('host',choices=['bumpfs','bumpfs2','--other--']), I('other','',text='Other host name'), I('command','hostname'), ], enablers = [('host','--other--','other')], ) if res: host = res['host'] if host == '--other--': host = res['other'] command = res['command'] if host and command: cmd = "ssh %s '%s'" % (host,command) sta,out = utils.runCommand(cmd) message(out) def submitToCluster(filename=None): """Submit an Abaqus job to the cluster.""" if not filename: filename = askFilename(pf.cfg['workdir'],filter="Abaqus input files (*.inp)",exist=True) if filename: if not filename.endswith('.inp'): filename += '.inp' jobname = os.path.basename(filename)[:-4] res = askItems([ ('ncpus',4,{'text':'Number of cpus','min':1,'max':1024}), ('postabq',False,{'text':'Run postabq on the results?'}), ]) if res: reqtxt = 'cpus=%s\n' % res['ncpus'] if res['postabq']: reqtxt += 'postproc=postabq\n' host = pf.cfg.get('jobs/host','mecaflix') reqdir = pf.cfg.get('jobs/inputdir','bumper/requests') cmd = "scp %s %s:%s" % (filename,host,reqdir) ret = call(['scp',filename,'%s:%s' % (host,reqdir)]) print ret ret = call(['ssh',host,"echo '%s' > %s/%s.request" % (reqtxt,reqdir,jobname)]) print ret def killClusterJob(jobname=None): """Kill a job to the cluster.""" res = askItems([('jobname','')]) if res: jobname = res['jobname'] host = pf.cfg.get('jobs/host','mecaflix') reqdir = pf.cfg.get('jobs/inputdir','bumper/requests') cmd = "touch %s/%s.kill" % (reqdir,jobname) print host print cmd ret = call(['ssh',host,"%s" % cmd]) print ret the_host = None the_userdir = None the_jobnames = None the_jobname = None def checkResultsOnServer(host=None,userdir=None): """Get a list of job results from the cluster. Specify userdir='bumper/running' to get a list of running jobs. """ global the_host,the_userdir,the_jobnames if host is None or userdir is None: res = askItems([ ('host',None,'select',{'choices':['bumpfs','bumpfs2','other']}), ('other','',{'text':'Other host name'}), ('status',None,'select',{'choices':['results','running','custom']}), ('userdir','bumper/results/',{'text':'Custom user directory'}), ]) if not res: return host = res['host'] if host == 'other': host = res['other'] status = res['status'] if status in ['results','running']: userdir = 'bumper/%s/' % status else: userdir = res['userdir'] jobnames = getSubdirs(host,userdir) if jobnames: the_host = host the_userdir = userdir the_jobnames = jobnames else: the_host = None the_userdir = None the_jobnames = None pf.message(the_jobnames) def changeTargetDir(fn): from gui import draw return draw.askDirname(fn) def getResultsFromServer(jobname=None,targetdir=None,ext=['.fil']): """Get results back from cluster.""" global the_jobname print "getRESULTS" if targetdir is None: targetdir = pf.cfg['workdir'] if jobname is None: if the_jobnames is None: jobname_input = [ ('host',pf.cfg['jobs/host']), ('userdir','bumper/results'), ('jobname',''), ] else: jobname_input = [ dict(name='jobname',value=the_jobname,choices=the_jobnames) ] print jobname_input res = askItems(jobname_input + [ dict(name='target dir',value=targetdir,itemtype='button',func=changeTargetDir), ('.fil',True), ('.post',True), ('_post.py',False), ]) if res: host = res.get('host',the_host) userdir = res.get('userdir',the_userdir) jobname = res['jobname'] targetdir = res['target dir'] ext = [ e for e in ['.fil','.post','_post.py'] if res[e] ] if jobname and ext: files = [ '%s%s' % (jobname,e) for e in ext ] userdir = "%s/%s" % (userdir,jobname) if getFiles(host,userdir,files,targetdir): the_jobname = jobname #################################################################### ######### MENU ############# _menu = 'Jobs' def create_menu(): """Create the Jobs menu.""" MenuData = [ ("&About",about), ("&Configure Job Plugin",configure), ("&Submit Abaqus Job",submitToCluster), ("&Kill Cluster Job",killClusterJob), ("&List available results on server",checkResultsOnServer), ("&Get results from server",getResultsFromServer), ("&Execute remote command",remoteCommand), ("---",None), ("&Reload Menu",reload_menu), ("&Close Menu",close_menu), ] return menu.Menu(_menu,items=MenuData,parent=pf.GUI.menu,before='help') def show_menu(): """Show the menu.""" if not pf.GUI.menu.item(_menu): create_menu() def close_menu(): """Close the menu.""" pf.GUI.menu.removeItem(_menu) def reload_menu(): """Reload the menu.""" close_menu() show_menu() #################################################################### ######### What to do when the script is executed ################### if __name__ == "draw": reload_menu() # End pyformex-0.8.6/pyformex/plugins/mesh_ext.py0000644000211500021150000003267711705104656020776 0ustar benebene00000000000000# $Id: mesh_ext.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Extended functionality of the Mesh class. This module defines extended Mesh functionality which is considered to be experimental, maybe incomplete or even buggy. The functions in this module can be called as functions operating on a Mesh object, but are also available as Mesh methods. """ from mesh import Mesh from elements import elementType,_default_facetype from formex import * from utils import deprecation ############################################################################## # # These functions replace the standard Mesh.report method. # They are here for demonstration purposes. # def report(self): """Create a report on the Mesh shape and size. The report contains the number of nodes, number of elements, plexitude, element type, bbox and size. """ bb = self.bbox() return """ Shape: %s nodes, %s elems, plexitude %s Eltype: %s (ndim=%s), BBox: %s, %s Size: %s """ % (self.ncoords(),self.nelems(),self.nplex(),self.eltype,self.eltype.ndim,bb[0],bb[1],bb[1]-bb[0]) def alt_report(self): """Create a report on the Mesh shape and size. The report contains the number of nodes, number of elements, plexitude, element type, bbox and size. """ bb = self.bbox() return """ Number of nodes: %s Number of elements: %s Plexitude: %s Eltype: %s Dimensionality: %s Min Coords: %s Max Coords: %s Size: %s Area: %s Volume: %s """ % (self.ncoords(),self.nelems(),self.nplex(),self.eltype,self.eltype.ndim,bb[1],bb[0],bb[1]-bb[0],self.area(),self.volume()) ############################################################################## # #GDS connectivity functions valid for all mesh types, moved from trisurface.py def nodeFront(self,startat=0,front_increment=1): """Generator function returning the frontal elements. startat is an element number or list of numbers of the starting front. On first call, this function returns the starting front. Each next() call returns the next front. """ p = -ones((self.nelems()),dtype=int) if self.nelems() <= 0: return # Construct table of elements connected to each element adj = self.nodeAdjacency() # Remember nodes left for processing todo = ones((self.npoints(),),dtype=bool) elems = clip(asarray(startat),0,self.nelems()) prop = 0 while elems.size > 0: # Store prop value for current elems p[elems] = prop yield p prop += front_increment # Determine adjacent elements elems = unique(adj[elems]) elems = elems[(elems >= 0) * (p[elems] < 0) ] if elems.size > 0: continue # No more elements in this part: start a new one elems = where(p<0)[0] if elems.size > 0: # Start a new part elems = elems[[0]] prop += 1 def walkNodeFront(self,startat=0,nsteps=-1,front_increment=1): for p in self.nodeFront(startat=startat,front_increment=front_increment): if nsteps > 0: nsteps -= 1 elif nsteps == 0: break return p def partitionByNodeFront(self,firstprop=0,startat=0): """Detects different parts of the Mesh using a frontal method. okedges flags the edges where the two adjacent elems are to be in the same part of the Mesh. startat is a list of elements that are in the first part. The partitioning is returned as a property type array having a value corresponding to the part number. The lowest property number will be firstprop. """ return firstprop +self.walkNodeFront( startat=startat,front_increment=0) def partitionByConnection(self): """Detect the connected parts of a Mesh. The Mesh is partitioned in parts in which all elements are connected. Two elements are connected if it is possible to draw a continuous (poly)line from a point in one element to a point in the other element without leaving the Mesh. The partitioning is returned as a property type array having a value corresponding to the part number. The lowest property number will be firstprop. """ return self.partitionByNodeFront() def splitByConnection(self): """Split the Mesh into connected parts. Returns a list of Meshes that each form a connected part. """ split = self.setProp(self.partitionByConnection()).splitProp() if split: return split.values() else: return [ self ] def largestByConnection(self): """Return the largest connected part of the Mesh.""" p = self.partitionByConnection() nparts = p.max()+1 if nparts == 1: return self,nparts else: t = [ p == pi for pi in range(nparts) ] n = [ ti.sum() for ti in t ] w = array(n).argmax() return self.clip(t[w]),nparts ######################################## def rings(self, sources, nrings): """ It finds the rings of elems connected to sources by node. Sources is a list of elem indices. A list of rings is returned, from zero (equal to sources) to step. If step is -1, all rings are returned. """ r=self.walkNodeFront(startat=sources,nsteps=nrings, front_increment=1) ar, rr= arange(len(r)), range(r.max()+1) return [ar[r==i] for i in rr ] def correctNegativeVolumes(self): """Modify the connectivity of negative-volume elements to make positive-volume elements. Negative-volume elements (hex or tet with inconsistent face orientation) may appear by error during geometrical trnasformations (e.g. reflect, sweep, extrude, revolve). This function fixes those elements. Currently it only works with linear tet and hex. """ vol=self.volumes()<0. if self.eltype.name()=='tet4': self.elems[vol]=self.elems[vol][:, [0, 2, 1, 3]] if self.eltype.name()=='hex8': self.elems[vol]=self.elems[vol][:, [4, 5, 6, 7, 0, 1, 2, 3]] return self def scaledJacobian(self, scaled=True): """ Returns a quality measure for volume meshes. If scaled if False, it returns the Jacobian at the corners of each element. If scaled is True, it returns a quality metrics, being the minumum value of the scaled Jacobian in each element (at one corner, the Jacobian divided by the volume of a perfect brick). Each tet or hex element gives a value between -1 and 1. Acceptable elements have a positive scaled Jacobian. However, good quality requires a minimum of 0.2. Quadratic meshes are first converted to linear. If the mesh contain mainly negative Jacobians, it probably has negative volumes and can be fixed with the correctNegativeVolumes. """ ne = self.nelems() if self.eltype.name()=='hex20': self = self.convert('hex8') if self.eltype.name()=='tet10': self = self.convert('tet4') if self.eltype.name()=='tet4': iacre=array([ [[0, 1], [1, 2],[2, 0],[3, 2]], [[0, 2], [1, 0],[2, 1],[3, 1]], [[0, 3], [1, 3],[2, 3],[3, 0]], ], dtype=int) nc = 4 if self.eltype.name()=='hex8': iacre=array([ [[0, 4], [1, 5],[2, 6],[3, 7], [4, 7], [5, 4],[6, 5],[7, 6]], [[0, 1], [1, 2],[2, 3],[3, 0], [4, 5], [5, 6],[6, 7],[7, 4]], [[0, 3], [1, 0],[2, 1],[3, 2], [4, 0], [5, 1],[6, 2],[7, 3]], ], dtype=int) nc = 8 acre = self.coords[self.elems][:, iacre] vacre = acre[:, :,:,1]-acre[:, :,:,0] cvacre = concatenate(vacre, axis=1) if self.eltype.name()=='tet4': #Tet4 has Jacobian equal on every corner, but differnt scaled J vacre = concatenate(vacre[:, :, 0], axis=1).reshape(-1, 2, 3) J = vectorTripleProduct(*vacre).repeat(nc).reshape(ne, 4) else: J = vectorTripleProduct(*cvacre).reshape(ne, nc) if not scaled: return J else: normvol = prod(length(cvacre), axis=0).reshape(ne, nc)#volume of 3 nprmal edges Jscaled = J/normvol return Jscaled.min(axis=1) ## THIS NEEDS WORK ### ## surfacetype is also eltype ?? def areas(self): """area of elements For surface element the faces' area is returned. For volume elements the sum of the faces'areas is returned. """ #In case of quadratic faces, the face's area should be #the area inside the polygon of face vertices or #the area of the equivalent linear face? ##this function would require some changes (here proposed inside the function as starting): ##create a _default_surfacetype to create quad8 instead of hex8 ?maybe also a _default_volumetype to create tet4 instead of quad4 ? def defaultSurfacetype(nplex): """Default face type for a surface mesh with given plexitude. For the most common cases of plexitudes, we define a default face type. The full list of default types can be found in mesh._default_facetype. """ return _default_surfacetype.get(nplex,None) import geomtools nfacperel= len(self.eltype.faces[1])#nfaces per elem mf=Mesh(self.coords, self.getFaces())#mesh of all faces mf.eltype = elementType(defaultSurfacetype(mf.nplex())) ntriperfac= mf.select([0]).convert('tri3').nelems()#how many tri per face elfacarea = geomtools.areaNormals( mf.convert('tri3').toFormex()[:])[0].reshape(self.nelems(), nfacperel*ntriperfac)#elems'faces'areas return elfacarea.sum(axis=1)#elems'areas def area(self): """Return the total area of the Mesh. For a Mesh with dimensionality 2, the total area of the Mesh is returned. For a Mesh with dimensionality 3, the total area of all the element faces is returned. Use Mesh.getBorderMesh().area() if you only want the total area of the border faces. For a Mesh with dimensionality < 2, 0 is returned. """ try: return self.areas().sum() except: return 0.0 def partitionByAngle(self,**arg): """Partition a surface Mesh by the angle between adjacent elements. The Mesh is partitioned in parts bounded by the sharp edges in the surface. The arguments and return value are the same as in :meth:`TriSurface.partitionByAngle`. Currently this only works for 'tri3' and 'quad4' type Meshes. Also, the 'quad4' partitioning method currently only works corectly if the quads are nearly planar. """ from plugins.trisurface import TriSurface if self.eltype.name() not in [ 'tri3', 'quad4' ]: raise ValueError, "partitionByAngle currently only works for 'tri3' and 'quad4' type Meshes." S = TriSurface(self.convert('tri3')) p = S.partitionByAngle(**arg) if self.eltype.name() == 'tri3': return p if self.eltype.name() == 'quad4': p = p.reshape(-1,2) if not (p[:,0] == p[:,1]).all(): pf.warning("The partitioning may be incorrect due to nonplanar 'quad4' elements") return p[:,0] # BV: This is not mesh specific and can probably be achieved by t1 * t2 @deprecation("Deprecated") def tests(t): """Intersection of multiple test operations. t is a list of n 1D boolean list, obtained by n Mesh.test operations. A new 1D boolean list is returned. t1=M.test(nodes=...) t2=M.test(nodes=...) T=tests( [t1, t2] ) is the intersection if t1 and t2 and can be used for Mesh.clip(T) """ return array(t, int).sum(axis=0)==len(t) ############################################################################## # # Initialize # def initialize(): """Initialize the Mesh extensions. Calling this function will install some of the mesh functions defined in this modules as Mesh methods. """ Mesh.report = alt_report def _auto_initialize(): """Auto-initialize Mesh extensions. Calling this function will install some of the mesh functions defined in this modules as Mesh methods. This function is called when the module is loaded, so the functions installed here will always be available as Mesh methods just by importing the mesh_ext module. """ Mesh.report = report Mesh.areas = areas Mesh.area = area Mesh.nodeFront = nodeFront Mesh.walkNodeFront = walkNodeFront Mesh.partitionByNodeFront = partitionByNodeFront Mesh.partitionByConnection = partitionByConnection Mesh.splitByConnection = splitByConnection Mesh.largestByConnection = largestByConnection Mesh.rings = rings Mesh.correctNegativeVolumes = correctNegativeVolumes Mesh.scaledJacobian = scaledJacobian Mesh.partitionByAngle = partitionByAngle _auto_initialize() # End pyformex-0.8.6/pyformex/plugins/export.py0000644000211500021150000000423011705104656020463 0ustar benebene00000000000000# $Id: export.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Classes and functions for exporting geometry in various formats. """ import pyformex class ObjFile(object): def __init__(self,filename): self.file = open(filename,'w') self.file.write("# .obj file written by %s\n" % pyformex.Version) def write(self,mesh,name=None): """Write a mesh to file in .obj format. mesh is a Mesh instance or another object having compatible coords and elems attributes. """ if name is not None: self.file.write("o %s\n" % str(name)) for v in mesh.coords: self.file.write("v %s %s %s\n" % tuple(v)) # element code: p(oint), l(ine) or f(ace) nplex = mesh.elems.shape[1] code = { 1:'p', 2:'l' }.get(nplex,'f') s = code+(' %s'*nplex)+'\n' for e in mesh.elems+1: # .obj format starts at 1 self.file.write(s % tuple(e)) def close(self): self.file.write('# End\n') self.file.close() self.file = None # End pyformex-0.8.6/pyformex/plugins/lima.py0000644000211500021150000000714211705104656020071 0ustar benebene00000000000000# $Id: lima.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## "Lindenmayer Systems" import turtle class Lima(object): """A class for operations on Lindenmayer Systems.""" def __init__(self,axiom="",rules={}): self.axiom = axiom self.product = axiom self.rule = rules self.gen = 0 def status (self): """Print the status of the Lima""" print("Lima status:") print(" Axiom: %s" % self.axiom) print(" Rules: %r" % self.rule) print(" Generation: %d" % self.gen) print(" Product: %s" % self.product) def addRule (self,atom,product): """Add a new rule (or overwrite an existing)""" self.rule[atom] = product def translate (self,rule,keep=False): """Translate the product by the specified rule set. If keep=True is specified, atoms that do not have a translation in the rule set, will be kept unchanged. The default (keep=False) is to remove those atoms. """ product = "" default = "" for c in self.product: if keep: default=c product += rule.get(c,default) return product def grow (self, ngen=1): for gen in range(ngen): self.product = self.translate(self.rule,keep=True) self.gen += 1 return self.product def lima(axiom,rules,level,turtlecmds,glob=None): """Create a list of connected points using a Lindenmayer system. axiom is the initial string, rules are translation rules for the characters in the string, level is the number of generations to produce, turtlecmds are the translation rules of the final string to turtle cmds, glob is an optional list of globals to pass to the turtle script player. This is a convenience function for quickly creating a drawing of a single generation member. If you intend to draw multiple generations of the same Lima, it is better to use the grow() and translate() methods directly. """ A = Lima(axiom,rules) A.grow(level) scr = "reset();"+A.translate(turtlecmds,keep=False) list = turtle.play(scr,glob) return list if __name__ == "__main__": def test(): TurtleRules = { 'F' : 'fd();', '*' : 'ro(60);', '/' : 'ro(-60);' } print(lima("F",{"F":"F*F//F*F"},1,{ 'F' : 'fd();', '*' : 'ro(60);', '/' : 'ro(-60);' })) print(lima("F",{"F":"F*F//F*F"},2,{ 'F' : 'fd();', '*' : 'ro(60);', '/' : 'ro(-60);' }) ) test() test() pyformex-0.8.6/pyformex/plugins/f2flu.py0000644000211500021150000001556111705104656020171 0ustar benebene00000000000000# $Id: f2flu.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Formex to Fluent translator. This module contains some functions that can aid in exporting pyFormex models to Fluent. This script should be executed with the command pyformex --nogui f2flu.py """ import sys from plugins import tetgen from elements import Tet4 from time import strftime, gmtime from numpy import * def writeHeading(fil, nodes, elems, text=''): """Write the heading of the Gambit neutral file.""" #currently only for hexahedral mesh fil.write(" CONTROL INFO 2.2.30\n") fil.write("** GAMBIT NEUTRAL FILE\n") fil.write('%s\n' %text) fil.write('PROGRAM: Gambit VERSION: 2.2.30\n') fil.write(strftime('%d %b %Y %H:%M:%S\n', gmtime())) fil.write(' NUMNP NELEM NGRPS NBSETS NDFCD NDFVL\n') fil.write('%10i%10i%10i%10i%10i%10i\n' % (shape(nodes)[0],shape(elems)[0],1,0,3,3)) fil.write('ENDOFSECTION\n') def writeNodes(fil, nodes, nofs=1): """Write nodal coordinates. The nofs specifies an offset for the node numbers. The default is 1, because Gambit numbering starts at 1. """ fil.write(' NODAL COORDINATES 2.2.30\n') for i,n in enumerate(nodes): fil.write("%10d%20.11e%20.11e%20.11e\n" % ((i+nofs,)+tuple(n))) fil.write('ENDOFSECTION\n') def writeElems(fil, elems1, eofs=1, nofs=1): """Write element connectivity. The eofs and nofs specify offsets for element and node numbers. The default is 1, because Gambit numbering starts at 1. """ #pyFormex uses the same convention for hexahedral elements as ABAQUS #Gambit uses a different convention #function currently only for hexahedral mesh elems = elems1.copy() elems[:,2] = elems1[:,3] elems[:,3] = elems1[:,2] elems[:,6] = elems1[:,7] elems[:,7] = elems1[:,6] fil.write(' ELEMENTS/CELLS 2.2.30\n') for i,e in enumerate(elems+nofs): fil.write('%8d %2d %2d %8d%8d%8d%8d%8d%8d%8d\n %8d\n' % ((i+eofs,4,8)+tuple(e))) fil.write('ENDOFSECTION\n') def writeGroup(fil, elems): """Write group of elements. The eofs and nofs specify offsets for element and node numbers. The default is 1, because Gambit numbering starts at 1. """ fil.write(' ELEMENT GROUP 2.2.30\n') fil.write('GROUP:%11d ELEMENTS:%11d MATERIAL:%11d NFLAGS:%11d\n' % (1,shape(elems)[0],2,1)) fil.write('%32s\n' %'fluid') fil.write('%8d\n' %0) n = shape(elems)[0]/10 for i in range(n): fil.write('%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d\n' %(10*i+1,10*i+2,10*i+3,10*i+4,10*i+5,10*i+6,10*i+7,10*i+8,10*i+9,10*i+10)) for j in range(shape(elems)[0]-10*n): fil.write('%8d' %(10*n+j+1)) fil.write('\n') fil.write('ENDOFSECTION\n') def read_tetgen(filename): """Read a tetgen tetraeder model. filename is the base of the path of the input files. For a filename 'proj', nodes are expected in 'proj.1.node' and elems are in file 'proj.1.ele'. """ nodes = tetgen.readNodes(filename+'.1.node') print("Read %d nodes" % nodes.shape[0]) elems = tetgen.readElems(filename+'.1.ele') print("Read %d tetraeders" % elems.shape[0]) return nodes,elems def encode(i,j,k,n): return n*(n*i+j)+k def decode(code,n): q,k = code/n, code%n i,j = q/n, q%n return i,j,k def output_fluent(fil,nodes,elems): """Write a tetraeder mesh in Fluent format to fil. The tetraeder mesh consists of an array of nodal coordinates and an array of element connectivity. """ print("Nodal coordinates") print(nodes) print("Element connectivity") print(elems) faces = array(Tet4.faces[1]) # Turning faces into an array is important ! print("Tetraeder faces") print(faces) elf = elems.take(faces,axis=1) # Remark: the shorter syntax elems[faces] takes its elements along the # axis 0. Then we would need to transpose() first (and probably # swap axes again later) print("The faces of the elements:") print(elf) # We need a copy to sort the nodes (sorting is done in-place) elfs = elf.copy() elfs.sort(axis=2) print("The faces with sorted nodes:") print(elfs) magic = elems.max()+1 print("Magic number = %d" % magic) code = encode(elfs[:,:,0],elfs[:,:,1],elfs[:,:,2],magic) # Remark how nice the encode function works on the whole array print("Encoded faces:") print(code) code = code.ravel() print(code) print("Just A Check:") print("Element 5 face 2 is %s " % elf[5,2]) print("Element 5 face 2 is %s " % list(decode(code[4*5+2],magic))) srt = code.argsort() print(srt) print(code[srt]) # Now shipout the faces in this order, removing the doubles j = -1 for i in srt: if j < 0: # no predecessor (or predecessor already shipped) j = i else: e1,f1 = j/4, j%4 if code[i] == code[j]: e2,f2 = i/4, i%4 j = -1 else: e2 = -1 j = i print("Face %s belongs to el %s and el %s" % ( elf[e1,f1], e2, e1 )) def tetgen2fluent(filename): """Convert a tetgen tetraeder model to fluent. filename is the base path of the tetgen input files. This will create a Fluent model in filename+'.flu' """ nodes,elems = read_tetgen(filename) if nodes is None or elems is None: print("Error while reading model %s" % filename) return fil = open(filename+'.flu','w') if fil: output_fluent(fil,nodes,elems) fil.close() # This is special for pyFormex scripts ! if __name__ == "script": for arg in argv: print("Converting model %s" % arg) tetgen2fluent(arg) argv = [ 'hallo' ] # End pyformex-0.8.6/pyformex/plugins/surface_menu.py0000644000211500021150000013456511705104656021635 0ustar benebene00000000000000# $Id: surface_menu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """surface_menu.py Surface operations plugin menu for pyFormex. """ import pyformex as pf from gui import actors,colors,decors,widgets,menu from gui.colorscale import ColorScale,ColorLegend from gui.draw import * from plugins.trisurface import * from plugins.objects import * from plugins import plot2d,formex_menu,fe_abq import simple from plugins.tools import Plane from pyformex.arraytools import niceLogSize from gui.widgets import simpleInputItem as _I, groupInputItem as _G import os, timer _name_ = '_surface_menu_' ##################### selection and annotations ########################## def draw_edge_numbers(n): """Draw the edge numbers of the named surface.""" S = named(n) F = Formex(S.coords[S.getEdges()]) return drawNumbers(F,color='green') ## def draw_node_numbers(n): ## """Draw the node numbers of the named surface.""" ## S = named(n) ## F = Formex(S.coords) ## return drawNumbers(F,color='red') def draw_normals(n,avg=False): """Draw the surface normals at centers or averaged normals at the nodes.""" S = named(n) if avg: C = S.coords N = S.avgVertexNormals() else: C = S.centroids() A,N = S.areaNormals() siz = pf.cfg['mark/normalsize'] if siz == 'area' and not avg: siz = sqrt(A).reshape(-1,1) else: try: siz = float(siz) except: siz = 0.05 * C.dsize() if avg: color = 'orange' else: color = 'red' return drawVectors(C,N,size=siz,color=color,wait=False) def draw_avg_normals(n): return draw_normals(n,True) class SurfaceObjects(DrawableObjects): def __init__(self): DrawableObjects.__init__(self,clas=TriSurface) def toggleEdgeNumbers(self,onoff=None): self.toggleAnnotation(draw_edge_numbers,onoff) def toggleNormals(self,onoff=None): self.toggleAnnotation(draw_normals,onoff) def toggleAvgNormals(self,onoff=None): self.toggleAnnotation(draw_avg_normals,onoff) selection = SurfaceObjects() ##################### select, read and write ########################## def read_Surface(fn,exportName=True): pf.message("Reading file %s" % fn) t = timer.Timer() S = TriSurface.read(fn) pf.message("Read surface with %d vertices, %d edges, %d triangles in %s seconds" % (S.ncoords(),S.nedges(),S.nelems(),t.seconds())) if exportName: name = utils.projectName(fn) export({name:S}) selection.set([name]) return S def readSelection(select=True,draw=True,multi=True): """Read a Surface (or list) from asked file name(s). If select is True (default), this becomes the current selection. If select and draw are True (default), the selection is drawn. """ types = [ 'Surface Files (*.gts *.stl *.off *.neu *.smesh)', 'All Files (*)' ] fn = askFilename(pf.cfg['workdir'],types,multi=multi) if fn: if not multi: fn = [ fn ] chdir(fn[0]) names = map(utils.projectName,fn) pf.GUI.setBusy() surfaces = [ read_Surface(f,False) for f in fn ] for i,S in enumerate(surfaces): S.setProp(i) pf.GUI.setBusy(False) export(dict(zip(names,surfaces))) if select: pf.message("Set selection to %s" % str(names)) selection.set(names) if draw: if max([named(s).nfaces() for s in selection]) < 100000 or ack(""" This is a large model and drawing could take quite some time. You should consider coarsening the model before drawing it. Shall I proceed with the drawing now? """): selection.draw() return fn def printSize(): for s in selection.names: S = named(s) pf.message("Surface %s has %d vertices, %s edges and %d faces" % (s,S.ncoords(),S.nedges(),S.nelems())) def printType(): for s in selection.names: S = named(s) if S.isClosedManifold(): pf.message("Surface %s is a closed manifold" % s) elif S.isManifold(): pf.message("Surface %s is an open manifold" % s) else: pf.message("Surface %s is not a manifold" % s) def printArea(): for s in selection.names: S = named(s) pf.message("Surface %s has area %s" % (s,S.area())) def printVolume(): for s in selection.names: S = named(s) pf.message("Surface %s has volume %s" % (s,S.volume())) def printStats(): for s in selection.names: S = named(s) pf.message("Statistics for surface %s" % s) print(S.stats()) def toFormex(suffix=''): """Transform the selection to Formices. If a suffix is given, the Formices are stored with names equal to the surface names plus the suffix, else, the surface names will be used (and the surfaces will thus be cleared from memory). """ if not selection.check(): selection.ask() if not selection.names: return newnames = selection.names if suffix: newnames = [ n + suffix for n in newnames ] newvalues = [ named(n).toFormex() for n in newnames ] export2(newnames,newvalues) if not suffix: selection.clear() formex_menu.selection.set(newnames) clear() formex_menu.selection.draw() def fromFormex(suffix=''): """Transform the Formex selection to TriSurfaces. If a suffix is given, the TriSurfaces are stored with names equal to the Formex names plus the suffix, else, the Formex names will be used (and the Formices will thus be cleared from memory). """ if not formex_menu.selection.check(): formex_menu.selection.ask() if not formex_menu.selection.names: return names = formex_menu.selection.names formices = [ named(n) for n in names ] if suffix: names = [ n + suffix for n in names ] t = timer.Timer() surfaces = dict([ (n,TriSurface(F)) for n,F in zip(names,formices) if F.nplex() == 3]) print("Converted in %s seconds" % t.seconds()) print(surfaces.keys()) export(surfaces) if not suffix: formex_menu.selection.clear() selection.set(surfaces.keys()) def toMesh(suffix=''): """Transform the selection to Meshes. If a suffix is given, the Meshes are stored with names equal to the surface names plus the suffix, else, the surface names will be used (and the surfaces will thus be cleared from memory). """ from plugins import geometry_menu if not selection.check(): selection.ask() if not selection.names: return newnames = selection.names if suffix: newnames = [ n + suffix for n in newnames ] newvalues = [ named(n).toMesh() for n in newnames ] export2(newnames,newvalues) if not suffix: selection.clear() geometry_menu.selection.set(newnames) clear() geometry_menu.selection.draw() def fromMesh(suffix=''): """Transform the Mesh selection to TriSurfaces. If a suffix is given, the TriSurfaces are stored with names equal to the Mesh names plus the suffix, else, the Mesh names will be used (and the Meshes will thus be cleared from memory). """ from plugins import geometry_menu if not geometry_menu.selection.check(): geometry_menu.selection.ask() if not geometry_menu.selection.names: return names = geometry_menu.selection.names meshes = [ named(n) for n in names ] if suffix: names = [ n + suffix for n in names ] t = timer.Timer() surfaces = dict([ (n,TriSurface(M)) for n,M in zip(names,meshes) if M.eltype == 'tri3']) print("Converted in %s seconds" % t.seconds()) print(surfaces.keys()) export(surfaces) if not suffix: geometry_menu.selection.clear() selection.set(surfaces.keys()) def toggle_shrink(): """Toggle the shrink mode""" if selection.shrink is None: selection.shrink = 0.8 else: selection.shrink = None selection.draw() def toggle_auto_draw(): global autodraw autodraw = not autodraw def fixNormals(): """Fix the normals of the selected surfaces.""" SL = selection.check() if SL: SL = [ S.fixNormals() for S in SL ] export2(selection.names,SL) selection.draw() def reverseNormals(): """Reverse the normals of the selected surfaces.""" SL = selection.check() if SL: SL = [ S.reverse() for S in SL ] export2(selection.names,SL) selection.draw() def merge(): """Merge the selected surfaces.""" SL = selection.check(warn=False) if len(SL) < 2: warning("You should at least select two surfaces!") return S = TriSurface.concatenate(SL) name = '--merged-surface--' export({name:S}) selection.set(name) selection.draw() def write_surface(types=['surface','gts','stl','off','neu','smesh']): F = selection.check(single=True) if F: if type(types) == str: types = [ types ] types = map(utils.fileDescription,types) fn = askNewFilename(pf.cfg['workdir'],types) if fn: pf.message("Exporting surface model to %s" % fn) pf.GUI.setBusy() F.write(fn) pf.GUI.setBusy(False) # # Operations with surface type, border, ... # def showBorder(): S = selection.check(single=True) if S: border = S.border() if border: print("The border consists of %s parts" % len(border)) print("The sorted border edges are: ") print('\n'.join([" %s: %s" % (i,b.elems) for i,b in enumerate(border)])) coloredB = [ b.compact().setProp(i+1) for i,b in enumerate(border) ] draw(coloredB,linewidth=3) for i,b in enumerate(coloredB): c = roll(pf.canvas.settings.colormap,i+1,axis=0) drawText3D(b.center(),str(i),color=c,font='sans',size=18,ontop=True) export({'border':coloredB}) else: warning("The surface %s does not have a border" % selection[0]) forget('border') return S def fillBorders(): _data_ = _name_+'fillBorders_data' S = showBorder() try: B = named('border') except: return if B: props = [ b.prop[0] for b in B ] dia = Dialog([ _I('Fill which borders',itemtype='radio',choices=['All','One']), _I('Filling method',itemtype='radio',choices=['radial','border']), _I('merge',False,text='Merge fills into current surface'), ]) if pf.PF.has_key(_data_): dia.updateData(pf.PF[_data_]) res = dia.getResults() if res: pf.PF[_data_] = res if res['Fill which borders'] == 'One': B = B[:1] fills = [ fillBorder(b,method=res['Filling method']).setProp(i+1) for i,b in enumerate(B) ] if res['merge']: name = selection.names[0] S = named(name) for f in fills: S += f #print "MERGE",type(S) export({name:S}) selection.draw() else: draw(fills) export(dict([('fill-%s'%i,f) for i,f in enumerate(fills)])) def deleteTriangles(): S = selection.check(single=True) if S: picked = pick('element') #print picked if picked: picked = picked[0] #print picked if len(picked) > 0: #print S.nelems() S = S.cselect(picked) #print "DELETE",type(S) name = selection.names[0] #print name #print S.nelems() export({name:S}) selection.draw() ## def fillHoles(): ## """Fill the holes in the selected surface.""" ## from connectivity import connectedLineElems ## S = selection.check(single=True) ## if S: ## border_elems = S.getEdges()[S.borderEdges()] ## if border_elems.size != 0: ## # partition borders ## print(border_elems) ## border_elems = connectedLineElems(border_elems) ## print(border_elems) ## # draw borders in new viewport ## R = pf.canvas.camera.getRot() ## P = pf.canvas.camera.perspective ## layout(2) ## viewport(1) ## pf.canvas.camera.rot = R ## toolbar.setPerspective(P) ## for i,elems in enumerate(border_elems): ## draw(Formex(S.coords[elems],i)) ## zoomAll() ## # pick borders for which the hole must be filled ## info("PICK HOLES WHICH HAVE TO BE FILLED.") ## picked = pick(mode='actor') ## layout(1) ## # fill the holes ## triangles = empty((0,3,),dtype=int) ## if picked.has_key(-1): ## for i in picked[-1]: ## triangles = row_stack([triangles,fillHole(S.coords,border_elems[int(i)])]) ## T = TriSurface(S.coords,triangles) ## S.append(T) ## draw(T,color='red',bbox=None) ## else: ## warning("No borders were picked.") ## else: ## warning("The surface %s does not have a border." % selection[0]) # Selectable values for display/histogram # Each key is a description of a result # Each value consist of a tuple # - function to calculate the values # - domain to display: True to display on edges, False to display on elements SelectableStatsValues = odict.ODict([ ('Aspect ratio', (TriSurface.aspectRatio,False)), ('Facet Area', (TriSurface.facetArea,False)), ('Smallest altitude', (TriSurface.smallestAltitude,False)), ('Longest edge', (TriSurface.longestEdge,False)), ('Shortest edge', (TriSurface.shortestEdge,False)), ('Number of node adjacent elements', (TriSurface.nNodeAdjacent,False)), ('Number of edge adjacent elements', (TriSurface.nEdgeAdjacent,False)), ('Edge angle', (TriSurface.edgeAngles,True)), ('Number of connected elements', (TriSurface.nEdgeConnected,True)), ('Curvature', (TriSurface.curvature,False)), ]) CurvatureValues = ['Gaussian curvature','Mean curvature','Shape index','Curvedness','First principal curvature','Second principal curvature'] def showHistogram(key,val,cumulative): y,x = plot2d.createHistogram(val,cumulative=cumulative) return plot2d.showHistogram(x,y,key) _stat_dia = None def showStatistics(key=None,domain=True,dist=False,cumdist=False,clip=None,vmin=None,vmax=None,percentile=False): """Show the values corresponding with key in the specified mode. key is one of the keys of SelectableStatsValues mode is one of ['On Domain','Histogram','Cumulative Histogram'] """ S = selection.check(single=True) if S: func,onEdges = SelectableStatsValues[key] kargs = {} if key == 'Curvature': kargs['neighbours'] = _stat_dia.results['neighbours'] val = func(S,**kargs) if key == 'Curvature': ind = CurvatureValues.index(_stat_dia.results['curval']) val = val[ind] val = val[S.elems] # !! THIS SHOULD BE IMPLEMENTED AS A GENERAL VALUE CLIPPER # !! e.g popping up when clicking the legend # !! and the values should be changeable if clip: clip = clip.lower() if percentile: try: from scipy.stats.stats import scoreatpercentile except: warning(""".. **The **percentile** clipping option is not available. Most likely because 'python-scipy' is not installed on your system.""") return Q1 = scoreatpercentile(val,vmin) Q3 = scoreatpercentile(val,vmax) factor = 3 if vmin: vmin = Q1-factor*(Q3-Q1) if vmax: vmax = Q3+factor*(Q3-Q1) if clip == 'top': val = val.clip(max=vmax) elif clip == 'bottom': val = val.clip(min=vmin) else: val = val.clip(vmin,vmax) if domain: clear() lights(False) showSurfaceValue(S,key,val,onEdges) if dist: showHistogram(key,val,cumulative=False) if cumdist: showHistogram(key,val,cumulative=True) def _show_stats(domain,dist): _stat_dia.acceptData() res = _stat_dia.results key = res['Value'] if dist and res['Cumulative Distribution']: cumdist = True dist = Fals/e else: cumdist = False clip = res['clip'] if clip == 'None': clip = None percentile = res['Clip Mode'] != 'Range' minval = res['Bottom'] maxval = res['Top'] showStatistics(key,domain,dist,cumdist,clip=clip,vmin=minval,vmax=maxval,percentile=percentile) def _show_domain(): _show_stats(True,False) def _show_dist(): _show_stats(False,True) def _close_stats_dia(): global _stat_dia if _stat_dia: _stat_dia.close() _stat_dia = None def showStatisticsDialog(): global _stat_dia if _stat_dia: _close_stats_dia() dispmodes = ['On Domain','Histogram','Cumulative Histogram'] keys = SelectableStatsValues.keys() _stat_dia = widgets.InputDialog( caption='Surface Statistics',items=[ _I('Value',itemtype='vradio',choices=keys), _I('neighbours',text='Curvature Neighbourhood',value=1), _I('curval',text='Curvature Value',itemtype='vradio',choices=CurvatureValues), _I('clip',itemtype='hradio',choices=['None','Top','Bottom','Both']), _I('Clip Mode',itemtype='hradio',choices=['Range','Percentile']), _G('Clip Values',checkable=True,items=[ _I('Top',1.0), _I('Bottom',0.0), ], ), _I('Cumulative Distribution',False), ], actions=[ ('Close',_close_stats_dia), ('Distribution',_show_dist), ('Show on domain',_show_domain)], default='Show on domain' ) _stat_dia.show() def showSurfaceValue(S,txt,val,onEdges): val = nan_to_num(val) mi,ma = val.min(),val.max() # Test: replace min with max dec = min(abs(mi),abs(ma)) if dec > 0.0: dec = max(0,3-int(log10(dec))) else: dec = 2 # create a colorscale and draw the colorlegend CS = ColorScale('RAINBOW',mi,ma,0.5*(mi+ma),1.) cval = array(map(CS.color,ravel(val))) cval = cval.reshape(append(val.shape,cval.shape[-1])) if onEdges: F = Formex(S.coords[S.getEdges()]) draw(F,color=cval) else: draw(S,color=cval) CL = ColorLegend(CS,100) CLA = decors.ColorLegend(CL,10,10,30,200,dec=dec) pf.canvas.addDecoration(CLA) drawText(txt,10,230,font='hv18') def colorByFront(): S = selection.check(single=True) if S: res = askItems([_I('front type',choices=['node','edge']), _I('number of colors',-1), _I('front width',1), _I('start at',0), _I('first prop',0), ]) pf.app.processEvents() if res: selection.remember() t = timer.Timer() ftype = res['front type'] nwidth = res['front width'] nsteps = nwidth * res['number of colors'] startat = res['start at'] firstprop = res['first prop'] if ftype == 'node': p = S.walkNodeFront(nsteps=nsteps,startat=startat) else: p = S.walkEdgeFront(nsteps=nsteps,startat=startat) S.setProp(p/nwidth + firstprop) print("Colored in %s parts (%s seconds)" % (S.prop.max()+1,t.seconds())) selection.draw() def partitionByConnection(): S = selection.check(single=True) if S: selection.remember() t = timer.Timer() S.prop = S.partitionByConnection() print("Partitioned in %s parts (%s seconds)" % (S.prop.max()+1,t.seconds())) selection.draw() def partitionByAngle(): S = selection.check(single=True) if S: res = askItems([_I('angle',60.), _I('firstprop',1), #_I('startat',0) ]) pf.app.processEvents() if res: selection.remember() t = timer.Timer() S.prop = S.partitionByAngle(**res) print("Partitioned in %s parts (%s seconds)" % (S.prop.max()+1,t.seconds())) selection.draw() ############################################################################# # Transformation of the vertex coordinates (based on Coords) # # !! These functions could be made identical to those in Formex_menu # !! (and thus could be unified) if the surface transfromations were not done # !! inplace but returned a new surface instance instead. # def scaleSelection(): """Scale the selection.""" FL = selection.check() if FL: res = askItems([_I('scale',1.0), ],caption = 'Scaling Factor') if res: scale = float(res['scale']) selection.remember(True) selection.changeValues([F.scale(scale) for F in FL]) selection.drawChanges() def scale3Selection(): """Scale the selection with 3 scale values.""" FL = selection.check() if FL: res = askItems([_I('scale',[1.0,1.0,1.0],itemtype='point'), ],caption = 'Scaling Factors') if res: scale = res['scale'] selection.remember(True) selection.changeValues([F.scale(scale) for F in FL]) selection.drawChanges() def translateSelection(): """Translate the selection.""" FL = selection.check() if FL: res = askItems([_I('direction',0), _I('distance','1.0'), ],caption = 'Translation Parameters') if res: dir = res['direction'] dist = res['distance'] selection.remember(True) selection.changeValues([F.translate(dir,dist) for F in FL]) selection.drawChanges() def centerSelection(): """Center the selection.""" FL = selection.check() if FL: selection.remember(True) selection.changeValues([F.translate(-F.coords.center()) for F in FL]) selection.drawChanges() def rotate(mode='global'): """Rotate the selection. mode is one of 'global','parallel','central','general' """ FL = selection.check() if FL: if mode == 'global': res = askItems([_I('angle',90.0), _I('axis',2), ]) if res: angle = res['angle'] axis = res['axis'] around = None elif mode == 'parallel': res = askItems([_I('angle',90.0), _I('axis',2), _I('point',[0.0,0.0,0.0],itemtype='point'), ]) if res: angle = res['angle'] axis = res['axis'] around = res['point'] elif mode == 'central': res = askItems([_I('angle',90.0), _I('axis',[1.0,0.0,0.0],itemtype='point'), ]) if res: angle = res['angle'] axis = res['axis'] around = None elif mode == 'general': res = askItems([_I('angle',90.0), _I('axis',[1.0,0.0,0.0],itemtype='point'), _I('point',[0.0,0.0,0.0],itemtype='point'), ]) if res: angle = res['angle'] axis = res['axis'] around = res['point'] if res: selection.remember(True) selection.changeValues([F.rotate(angle,axis,around) for F in FL]) selection.drawChanges() def rotateGlobal(): rotate('global') def rotateParallel(): rotate('parallel') def rotateCentral(): rotate('central') def rotateGeneral(): rotate('general') def rollAxes(): """Rotate the selection.""" FL = selection.check() if FL: selection.remember(True) selection.changeValues([F.rollAxes() for F in FL]) selection.drawChanges() def clipSelection(): """Clip the selection. The coords list is not changed. """ FL = selection.check() if FL: res = askItems([_I('axis',0), _I('begin',0.0), _I('end',1.0), _I('nodes','all',choices=['all','any','none']), ],caption='Clipping Parameters') if res: bb = bbox(FL) axis = res['axis'] xmi = bb[0][axis] xma = bb[1][axis] dx = xma-xmi xc1 = xmi + float(res['begin']) * dx xc2 = xmi + float(res['end']) * dx selection.changeValues([F.clip(F.test(nodes=res['nodes'],dir=axis,min=xc1,max=xc2)) for F in FL]) selection.drawChanges() def clipAtPlane(): """Clip the selection with a plane.""" FL = selection.check() if not FL: return dsize = bbox(FL).dsize() esize = 10 ** (niceLogSize(dsize)-5) res = askItems([_I('Point',[0.0,0.0,0.0],itemtype='point'), _I('Normal',[1.0,0.0,0.0],itemtype='point'), _I('Keep side',itemtype='radio',choices=['positive','negative']), _I('Nodes',itemtype='radio',choices=['all','any','none']), _I('Tolerance',esize), _I('Property',1), ],caption = 'Define the clipping plane') if res: P = res['Point'] N = res['Normal'] side = res['Keep side'] nodes = res['Nodes'] atol = res['Tolerance'] prop = res['Property'] selection.remember(True) if side == 'positive': func = TriSurface.clip else: func = TriSurface.cclip FL = [ func(F,F.test(nodes=nodes,dir=N,min=P,atol=atol)) for F in FL] FL = [ F.setProp(prop) for F in FL ] export(dict([('%s/clip' % n,F) for n,F in zip(selection,FL)])) selection.set(['%s/clip' % n for n in selection]) selection.draw() def cutWithPlane(): """Cut the selection with a plane.""" FL = selection.check() if not FL: return dsize = bbox(FL).dsize() esize = 10 ** (niceLogSize(dsize)-5) res = askItems([_I('Point',[0.0,0.0,0.0],itemtype='point'), _I('Normal',[1.0,0.0,0.0],itemtype='point'), _I('New props',[1,2,2,3,4,5,6]), _I('Side','positive',itemtype='radio',choices=['positive','negative','both']), _I('Tolerance',esize), ],caption = 'Define the cutting plane') if res: P = res['Point'] N = res['Normal'] p = res['New props'] side = res['Side'] atol = res['Tolerance'] selection.remember(True) if side == 'both': G = [F.toFormex().cutWithPlane(P,N,side=side,atol=atol,newprops=p) for F in FL] G_pos = [] G_neg =[] for F in G: G_pos.append(TriSurface(F[0])) G_neg.append(TriSurface(F[1])) export(dict([('%s/pos' % n,g) for n,g in zip(selection,G_pos)])) export(dict([('%s/neg' % n,g) for n,g in zip(selection,G_neg)])) selection.set(['%s/pos' % n for n in selection] + ['%s/neg' % n for n in selection]) selection.draw() else: selection.changeValues([F.cutWithPlane(P,N,newprops=p,side=side,atol=atol) for F in FL]) selection.drawChanges() def cutSelectionByPlanes(): """Cut the selection with one or more planes, which are already created.""" S = selection.check(single=True) if not S: return planes = listAll(clas=Plane) if len(planes) == 0: warning("You have to define some planes first.") return res1 = widgets.ListSelection(planes,caption='Known %sobjects' % selection.object_type(),sort=True).getResult() if res1: res2 = askItems([_I('Tolerance',0.), _I('Color by','side',itemtype='radio',choices=['side', 'element type']), _I('Side','both',itemtype='radio',choices=['positive','negative','both']), ],caption = 'Cutting parameters') if res2: planes = map(named, res1) p = [plane.P for plane in planes] n = [plane.n for plane in planes] atol = res2['Tolerance'] color = res2['Color by'] side = res2['Side'] if color == 'element type': newprops = [1,2,2,3,4,5,6] else: newprops = None if side == 'both': Spos, Sneg = S.toFormex().cutWithPlane(p,n,newprops=newprops,side=side,atol=atol) elif side == 'positive': Spos = S.toFormex().cutWithPlane(p,n,newprops=newprops,side=side,atol=atol) Sneg = Formex() elif side == 'negative': Sneg = S.toFormex().cutWithPlane(p,n,newprops=newprops,side=side,atol=atol) Spos = Formex() if Spos.nelems() !=0: Spos = TriSurface(Spos) if color == 'side': Spos.setProp(2) else: Spos = None if Sneg.nelems() != 0: Sneg = TriSurface(Sneg) if color == 'side': Sneg.setProp(3) else: Sneg = None name = selection.names[0] export({name+"/pos":Spos}) export({name+"/neg":Sneg}) selection.set([name+"/pos",name+"/neg"]+res1) selection.draw() def intersectWithPlane(): """Intersect the selection with a plane.""" FL = selection.check() if not FL: return res = askItems([_I('Name suffix','intersect'), _I('Point',(0.0,0.0,0.0)), _I('Normal',(1.0,0.0,0.0)), ],caption = 'Define the cutting plane') if res: suffix = res['Name suffix'] P = res['Point'] N = res['Normal'] M = [ S.intersectionWithPlane(P,N) for S in FL ] draw(M,color='red') export(dict([('%s/%s' % (n,suffix), m) for (n,m) in zip(selection,M)])) def slicer(): """Slice the surface to a sequence of cross sections.""" S = selection.check(single=True) if not S: return res = askItems([_I('Direction',[1.,0.,0.]), _I('# slices',20), ],caption = 'Define the slicing planes') if res: axis = res['Direction'] nslices = res['# slices'] pf.GUI.setBusy(True) t = timer.Timer() slices = S.slice(dir=axis,nplanes=nslices) print "Sliced in %s seconds" % t.seconds() pf.GUI.setBusy(False) print [ s.nelems() for s in slices ] draw([ s for s in slices if s.nelems() > 0],color='red',bbox='last',view=None) export({'%s/slices' % selection[0]:slices}) def spliner(): """Slice the surface to a sequence of cross sections.""" import olist from plugins.curve import BezierSpline S = selection.check(single=True) if not S: return res = askItems([_I('Direction',[1.,0.,0.]), _I('# slices',20), _I('remove_invalid',False), ],caption = 'Define the slicing planes') if res: axis = res['Direction'] nslices = res['# slices'] remove_cruft = res['remove_invalid'] pf.GUI.setBusy(True) slices = S.slice(dir=axis,nplanes=nslices) pf.GUI.setBusy(False) print [ s.nelems() for s in slices ] split = [ s.splitProp().values() for s in slices if s.nelems() > 0 ] split = olist.flatten(split) hasnan = [ isnan(s.coords).any() for s in split ] print hasnan print sum(hasnan) #print [s.closed for s in split] export({'%s/split' % selection[0]:split}) draw(split,color='blue',bbox='last',view=None) splines = [ BezierSpline(s.coords[s.elems[:,0]],closed=True) for s in split ] draw(splines,color='red',bbox='last',view=None) export({'%s/splines' % selection[0]:splines}) ################## Smooth the selected surface ############################# def smooth(): """Smooth the selected surface.""" S = selection.check(single=True) if S: res = askItems([ _I('method','lowpass',itemtype='radio',choices=['lowpass','laplace','gts']), _I('iterations',1), _I('lambda_value',0.5,min=0.0,max=1.0), _I('neighbourhood',1), _I('alpha',0.0), _I('beta',0.2), _I('verbose',False), ]) if res: if not 0.0 <= res['lambda_value'] <= 1.0: warning("Lambda should be between 0 and 1.") return selection.remember(True) S = S.smooth(**res) selection.changeValues([S]) selection.drawChanges() ################################################################### ########### The following functions are in need of a make-over def flytru_stl(): """Fly through the stl model.""" global ctr Fc = Formex(array(ctr).reshape((-1,1,3))) path = connect([Fc,Fc],bias=[0,1]) flyAlong(path) def create_volume(): """Generate a volume tetraeder mesh inside an stl surface.""" types = [ 'STL/OFF Files (*.stl *.off)', 'All Files (*)' ] fn = askFilename(pf.cfg['workdir'],types) if fn: tetgen.runTetgen(fn) def export_surface(): S = selection.check(single=True) if S: types = [ "Abaqus INP files (*.inp)" ] fn = askNewFilename(pf.cfg['workdir'],types) if fn: print("Exporting surface model to %s" % fn) updateGUI() fe_abq.exportMesh(fn,S,eltype='S3',header="Abaqus model generated by pyFormex from input file %s" % os.path.basename(fn)) def export_volume(): if PF['volume'] is None: return types = [ "Abaqus INP files (*.inp)" ] fn = askNewFilename(pf.cfg['workdir'],types) if fn: print("Exporting volume model to %s" % fn) updateGUI() mesh = Mesh(PF['volume']) fe_abq.exportMesh(fn,mesh,eltype='C3D%d' % elems.shape[1],header="Abaqus model generated by tetgen from surface in STL file %s.stl" % PF['project']) def show_nodes(): n = 0 data = askItems([_I('node number',n), ]) n = int(data['node number']) if n > 0: nodes,elems = PF['surface'] print("Node %s = %s",(n,nodes[n])) def trim_border(elems,nodes,nb,visual=False): """Removes the triangles with nb or more border edges. Returns an array with the remaining elements. """ b = border(elems) b = b.sum(axis=1) trim = where(b>=nb)[0] keep = where(b 0: prop = zeros(shape=(F.nelems(),),dtype=int32) prop[trim] = 2 # red prop[keep] = 1 # yellow F = Formex(nodes[elems],prop) clear() draw(F,view='left') return elems[keep] def trim_surface(): check_surface() res = askItems([_I('Number of trim rounds',1), _I('Minimum number of border edges',1), ]) n = int(res['Number of trim rounds']) nb = int(res['Minimum number of border edges']) print("Initial number of elements: %s" % elems.shape[0]) for i in range(n): elems = trim_border(elems,nodes,nb) print("Number of elements after border removal: %s" % elems.shape[0]) def read_tetgen(surface=True, volume=True): """Read a tetgen model from files fn.node, fn.ele, fn.smesh.""" ftype = '' if surface: ftype += ' *.smesh' if volume: ftype += ' *.ele' fn = askFilename(pf.cfg['workdir'],"Tetgen files (%s)" % ftype) nodes = elems =surf = None if fn: chdir(fn) project = utils.projectName(fn) set_project(project) nodes,nodenrs = tetgen.readNodes(project+'.node') # print("Read %d nodes" % nodes.shape[0]) if volume: elems,elemnrs,elemattr = tetgen.readElems(project+'.ele') print("Read %d tetraeders" % elems.shape[0]) PF['volume'] = (nodes,elems) if surface: surf = tetgen.readSurface(project+'.smesh') print("Read %d triangles" % surf.shape[0]) PF['surface'] = (nodes,surf) if surface: show_surface() else: show_volume() def read_tetgen_surface(): read_tetgen(volume=False) def read_tetgen_volume(): read_tetgen(surface=False) def scale_volume(): if PF['volume'] is None: return nodes,elems = PF['volume'] nodes *= 0.01 PF['volume'] = (nodes,elems) def show_volume(): """Display the volume model.""" if PF['volume'] is None: return nodes,elems = PF['volume'] F = Formex(nodes[elems],eltype='tet4') pf.message("BBOX = %s" % F.bbox()) clear() draw(F,color='random') PF['vol_model'] = F def createCube(): res = askItems([_I('name','__auto__'), ]) if res: name = res['name'] S = Cube() export({name:S}) selection.set([name]) selection.draw() def createSphere(): res = askItems([_I('name','__auto__'), _I('grade',4), ]) if res: name = res['name'] level = max(1,res['grade']) S = Sphere(level,verbose=True,filename=name+'.gts') export({name:S}) selection.set([name]) selection.draw() ################### Operations using gts library ######################## def check(): S = selection.check(single=True) if S: pf.message(S.check(verbose=True)) def split(): S = selection.check(single=True) if S: pf.message(S.split(base=selection[0],verbose=True)) def coarsen(): S = selection.check(single=True) if S: res = askItems([_I('min_edges',-1), _I('max_cost',-1.0), _I('mid_vertex',False), _I('length_cost',False), _I('max_fold',1.0), _I('volume_weight',0.5), _I('boundary_weight',0.5), _I('shape_weight',0.0), _I('progressive',False), _I('log',False), _I('verbose',False), ]) if res: selection.remember() if res['min_edges'] <= 0: res['min_edges'] = None if res['max_cost'] <= 0: res['max_cost'] = None S=S.coarsen(**res) selection.changeValues([S]) selection.drawChanges() def refine(): S = selection.check(single=True) if S: res = askItems([_I('max_edges',-1), _I('min_cost',-1.0), _I('log',False), _I('verbose',False), ]) if res: selection.remember() if res['max_edges'] <= 0: res['max_edges'] = None if res['min_cost'] <= 0: res['min_cost'] = None S=S.refine(**res) selection.changeValues([S]) selection.drawChanges() def boolean(): """Boolean operation on two surfaces. op is one of '+' : union, '-' : difference, '*' : interesection """ surfs = listAll(clas=TriSurface) if len(surfs) == 0: warning("You currently have no exported surfaces!") return ops = ['+ (Union)','- (Difference)','* (Intersection)'] res = askItems([_I('surface 1',choices=surfs), _I('surface 2',choices=surfs), _I('operation',choices=ops), _I('output intersection curve',False), _I('check self intersection',False), _I('verbose',False), ],'Boolean Operation') if res: SA = pf.PF[res['surface 1']] SB = pf.PF[res['surface 2']] SC = SA.boolean(SB,op=res['operation'].strip()[0], intersection_curve=res['output intersection curve'], check=res['check self intersection'], verbose=res['verbose']) export({'__auto__':SC}) #selection.draw() ################### menu ################# _menu = 'Surface' def create_menu(): """Create the Surface menu.""" MenuData = [ ("&Read Surface Files",readSelection), ("&Select Surface(s)",selection.ask), ("&Draw Selection",selection.draw), ("&Forget Selection",selection.forget), ("&Convert to Formex",toFormex), ("&Convert from Formex",fromFormex), ("&Convert from Mesh",fromMesh), ("&Write Surface Model",write_surface), ("---",None), ("&Create surface", [('&Cube',createCube), ('&Sphere',createSphere), ]), ("&Merge Selection",merge), ("&Fix Normals",fixNormals), ("&Reverse Normals",reverseNormals), #("&Set Property",selection.setProp), ("---",None), ("Print &Information", [('&Data Size',printSize), ('&Bounding Box',selection.printbbox), ('&Surface Type',printType), ('&Total Area',printArea), ('&Enclosed Volume',printVolume), ('&All Statistics',printStats), ]), ("&Shrink",toggle_shrink), ("Toggle &Annotations", [("&Names",selection.toggleNames,dict(checkable=True)), ("&Face Numbers",selection.toggleNumbers,dict(checkable=True)), ("&Edge Numbers",selection.toggleEdgeNumbers,dict(checkable=True)), ("&Node Numbers",selection.toggleNodeNumbers,dict(checkable=True)), ("&Normals",selection.toggleNormals,dict(checkable=True)), ("&AvgNormals",selection.toggleAvgNormals,dict(checkable=True)), ('&Toggle Bbox',selection.toggleBbox,dict(checkable=True)), ]), ("&Statistics",showStatisticsDialog), ("---",None), ("&Frontal Methods", [("&Color By Front",colorByFront), ("&Partition By Connection",partitionByConnection), ("&Partition By Angle",partitionByAngle), ]), ("&Show Border",showBorder), ("&Fill Border",fillBorders), # ("&Fill Holes",fillHoles), ("&Delete Triangles",deleteTriangles), ("---",None), ("&Transform", [("&Scale",scaleSelection), ("&Scale non-uniformly",scale3Selection), ("&Translate",translateSelection), ("&Center",centerSelection), ("&Rotate", [("&Around Global Axis",rotateGlobal), ("&Around Parallel Axis",rotateParallel), ("&Around Central Axis",rotateCentral), ("&Around General Axis",rotateGeneral), ]), ("&Roll Axes",rollAxes), ]), ("&Clip/Cut", [("&Clip",clipSelection), ("&Clip At Plane",clipAtPlane), ("&Cut With Plane",cutWithPlane), ("&Multiple Cut",cutSelectionByPlanes), ("&Intersection With Plane",intersectWithPlane), ("&Slicer",slicer), ("&Spliner",spliner), ]), ("&Smooth",smooth), ("&Undo Last Changes",selection.undoChanges), ("---",None), ('>S functions', [('&Check surface',check), ('&Split surface',split), ("&Coarsen surface",coarsen), ("&Refine surface",refine), ("&Smooth surface",smooth), ("&Boolean operation on two surfaces",boolean), ]), # ("&Show volume model",show_volume), # ("&Print Nodal Coordinates",show_nodes), # ("&Convert STL file to OFF file",convert_stl_to_off), # ("&Sanitize STL file to OFF file",sanitize_stl_to_off), # ("&Trim border",trim_surface), ("&Create volume mesh",create_volume), # ("&Read Tetgen Volume",read_tetgen_volume), ("&Export surface to Abaqus",export_surface), ("&Export volume to Abaqus",export_volume), ("---",None), ("&Reload Menu",reload_menu), ("&Close Menu",close_menu), ] return menu.Menu(_menu,items=MenuData,parent=pf.GUI.menu,before='Help') def show_menu(): """Show the menu.""" if not pf.GUI.menu.item(_menu): create_menu() def close_menu(): """Close the menu.""" pf.GUI.menu.removeItem(_menu) def reload_menu(): """Reload the menu.""" close_menu() show_menu() #################################################################### ######### What to do when the script is executed ################### if __name__ == "draw": reload_menu() # End pyformex-0.8.6/pyformex/plugins/polygon.py0000644000211500021150000001351211705104656020634 0ustar benebene00000000000000# $Id: polygon.py 2150 2012-01-16 20:33:48Z bverheg $ pyformex ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Polygonal facets. """ import pyformex as pf from coords import * from geometry import Geometry from plugins.curve import PolyLine from plugins.trisurface import fillBorder import utils ############################################################################## # class Polygon(Geometry): """A Polygon is a flat surface bounded by a closed PolyLine. The border can be specified as: - a Coords-like with shape (nvertex,3) specifying the vertex coordinates in order - an object that has a coords attribute. """ def __init__(self,border,normal=2,holes=[]): """Initialize a Polygon instance""" Geometry.__init__(self) self.prop = None if border.__class__ != Coords: try: border = border.coords except: raise ValueError,"Invalid border data" self.coords = border.reshape(-1,3) def npoints(self): """Return the number of points and edges.""" return self.coords.shape[0] def vectors(self): """Return the vectors from each point to the next one.""" x = self.coords return roll(x,-1,axis=0) - x def angles(self): """Return the angles of the line segments with the x-axis.""" v = self.vectors() return arctand2(v[:,1],v[:,0]) def externalAngles(self): """Return the angles between subsequent line segments. The returned angles are the change in direction between the segment ending at the vertex and the segment leaving. The angles are given in degrees, in the range ]-180,180]. The sum of the external angles is always (a multiple of) 360. A convex polygon has all angles of the same sign. """ a = self.angles() va = a - roll(a,1) va[va <= -180.] += 360. va[va > 180.] -= 360. return va def isConvex(self): """Check if the polygon is convex and turning anticlockwise. Returns: - +1 if the Polygon is convex and turning anticlockwise, - -1 if the Polygon is convex, but turning clockwise, - 0 if the Polygon is not convex. """ return int(sign(self.externalAngles()).sum()) / self.npoints() def internalAngles(self): """Return the internal angles. The returned angles are those between the two line segments at each vertex. The angles are given in degrees, in the range ]-180,180]. These angles are the complement of the """ return 180.-self.externalAngles() def fill(self): return def area(self,project=None): """Compute area inside a polygon. Parameters: - `project`: (3,) Coords array representing a unit direction vector. Returns: a single float value with the area inside the polygon. If a direction vector is given, the area projected in that direction is returned. Note that if the polygon is nonplanar and no direction is given, the area inside the polygon is not well defined. """ from geomtools import polygonArea return polygonArea(self.coords,project) def reducePolyline(x,e): """Create a triangle within a border. - coords: (npoints,3) Coords: the ordered vertices of the border. Elems is a (nelems,2) shaped array of integers representing the border element numbers and must be ordered. A list of two objects is returned: the new border elements and the triangle. """ if __name__ == 'draw': layout(2) for i in range(2): viewport(i) clear() smoothwire() n = 5 r = 0.7 noise = 0.0 x = randomNoise((n),r*3.,3.) y = randomNoise((n),0.,360.) y.sort() # sort #y = y[::-1] # reverse z = zeros(n) X = Coords(column_stack([x,y,z])).cylindrical().addNoise(noise) draw(X) drawNumbers(X) PG = Polygon(X) PL = PolyLine(X,closed=True) draw(PL) v = normalize(PG.vectors()) drawVectors(PG.coords,v,color=red,linewidth=2) a = PG.angles() ae = PG.externalAngles() ai = PG.internalAngles() print "Direction angles:", a print "External angles:", ae print "Internal angles:", ai print "Sum of external angles: ",ae.sum() print "The polygon is convex: %s" % PG.isConvex() # choose one of these #B = PL.coords B = PL.toMesh() #B = PL viewport(0) clear() S = fillBorder(B,method='border') draw(S) drawNumbers(S) drawText(S.check(),100,20) viewport(1) clear() S1 = fillBorder(B,method='radial') draw(S1,color=red) drawNumbers(S1) drawText(S1.check(),100,20) # End pyformex-0.8.6/pyformex/plugins/sectionize.py0000644000211500021150000001325011705104656021320 0ustar benebene00000000000000# $Id: sectionize.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """sectionize.py Create, measure and approximate cross section of a Formex. """ import pyformex as pf import simple from formex import * from gui.draw import * def connectPoints(F,close=False): """Return a Formex with straight segments connecting subsequent points. F can be a Formex or data that can be turned into a Formex (e.g. an (n,3) array of points). The result is a plex-2 Formex connecting the subsequent points of F or the first point of subsequent elements in case the plexitude of F > 1. If close=True, the last point is connected back to the first to create a closed polyline. """ if not isinstance(F,formex.Formex): F = Formex(F) return formex.connect([F,F],bias=[0,1],loop=close) def centerline(F,dir,nx=2,mode=2,th=0.2): """Compute the centerline in the direction dir. """ bb = F.bbox() x0 = F.center() x1 = F.center() x0[dir] = bb[0][dir] x1[dir] = bb[1][dir] n = array((0,0,0)) n[dir] = nx grid = simple.regularGrid(x0,x1,n).reshape((-1,3)) if mode > 0: th *= (x1[dir]-x0[dir])/nx n = zeros((3,)) n[dir] = 1.0 center = [] for P in grid: test = abs(F.distanceFromPlane(P,n)) < th if mode == 1: C = F.coords[test].mean(axis=0) elif mode == 2: test = test.sum(axis=-1) G = F.select(test==F.coords.shape[1]) print(G) C = G.center() center.append(C) grid = array(center) return Formex(connectPoints(grid)) def createSegments(F,ns=None,th=None): """Create segments along 0 axis for sectionizing the Formex F.""" bb = F.bbox() pf.message("Bounding box = %s" % bb) if ns is None or th is None: res = askItems([['number of sections',20], ['relative thickness',0.1]], 'Sectioning Parameters') if res: ns = int(res['number of sections']) th = float(res['relative thickness']) if type(ns) == int and type(th) == float: xmin,ymin,zmin = bb[0] xmax,ymax,zmax = bb[1] xgem,ygem,zgem = F.center() A = [ xmin,ygem,zgem ] B = [ xmax,ygem,zgem ] segments = Formex([[A,B]]).divide(ns) pf.message("Segments: %s" % segments) return ns,th,segments return 0,0,[] def sectionize(F,segments,th=0.1,visual=True): """Sectionize a Formex in planes perpendicular to the segments. F is any Formex. segments is a plex-2 Formex. Planes are chosen in each center of a segment, perpendicular to that segment. Then parts of the Formex F are selected in the neighbourhood of each plane. Each part is then approximated by a circle in that plane. th is the relative thickness of the selected part of the Formex. If th = 0.5, that part will be delimited by two planes in the endpoints of and perpendicular to the segments. """ sections = [] ctr = [] diam = [] if visual: clear() linewidth(1) draw(F,color='yellow') linewidth(2) for s in segments: c = 0.5 * (s[0]+s[1]) d = s[1]-s[0] l = length(d) n = d/l t = th*l test = abs(F.distanceFromPlane(c,n)) < th*l test = test.sum(axis=-1) G = F.select(test==3) if visual: draw(G,color='blue',view=None) pf.canvas.update() print(G) C = G.center() D = 2 * G.distanceFromLine(C,n).mean() pf.message("Section Center: %s; Diameter: %s" % (C,D)) sections.append(G) ctr.append(C) diam.append(D) return sections,ctr,diam def drawCircles(sections,ctr,diam): """Draw circles as approximation of Formices.""" circle = simple.circle().rotate(-90,1) cross = Formex(simple.Pattern['plus']).rotate(-90,1) circles = [] n = len(sections) for i in range(n): C = cross.translate(ctr[i]) B = circle.scale(diam[i]/2).translate(ctr[i]) S = sections[i] clear() draw(S,view='left',wait=False) draw(C,color='red',bbox=None,wait=False) draw(B,color='blue',bbox=None) circles.append(B) return circles def drawAllCircles(F,circles): clear() linewidth(1) draw(F,color='yellow',view='front') linewidth(2) for circ in circles: bb = circ.bbox() d = (bb[1] - bb[0]) * 0.5 bb[0] -= d bb[1] += d draw(circ,color='blue',bbox=bb) zoomAll() # End pyformex-0.8.6/pyformex/plugins/surface_abq.py0000644000211500021150000000463211705104656021423 0ustar benebene00000000000000# $Id: surface_abq.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Create tetraeder mesh inside .STL surface and export in Abaqus format. Usage: pyformex --nogui surface_abq SURFACE_FILES Generates input-surface.inp and input-volume.inp with the surface and volume modules in Abaqus(R) input format. """ from mesh import Mesh from plugins import fe_abq,tetgen import os def stl_to_abaqus(fn): print("Converting %s to Abaqus .INP format" % fn) tetgen.runTetgen(fn) fb = os.path.splitext(fn)[0] nodes = tetgen.readNodes(fb+'.1.node') elems = tetgen.readElems(fb+'.1.ele') faces = tetgen.readSurface(fb+'.1.smesh') print("Exporting surface model") smesh = Mesh(nodes,faces,eltype='S3') fe_abq.exportMesh(fb+'-surface.inp',smesh,"Abaqus model generated by tetgen from surface in STL file %s" % fn) print("Exporting volume model") vmesh = Mesh(nodes,elems,eltype='C3D%d' % elems.shape[1]) abq_export(fb+'-volume.inp',vmesh,"Abaqus model generated by tetgen from surface in STL file %s" % fn) # Processing starts here if __name__ == "script": import sys for f in sys.argv[2:]: if f.endswith('.stl') and os.path.exists(f): print("Processing %s" % f) stl_to_abaqus(f) else: print("Ignore argument %s" % f) # End pyformex-0.8.6/pyformex/plugins/isopar.py0000644000211500021150000001652011705104656020444 0ustar benebene00000000000000# $Id: isopar.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Isoparametric transformations""" from coords import * def evaluate(atoms,x,y=0,z=0): """Build a matrix of functions of coords. - `atoms`: a list of text strings representing a mathematical function of `x`, and possibly of `y` and `z`. - `x`, `y`, `z`: a list of x- (and optionally y-, z-) values at which the `atoms` will be evaluated. The lists should have the same length. Returns a matrix with `nvalues` rows and `natoms` colums. """ aa = zeros((len(x),len(atoms)),Float) for k,a in enumerate(atoms): aa[:,k] = eval(a) return aa class Isopar(object): """A class representing an isoparametric transformation type is one of the keys in Isopar.isodata coords and oldcoords can be either arrays, Coords or Formex instances, but should be of equal shape, and match the number of atoms in the specified transformation type The following three formulations are equivalent :: trf = Isopar(eltype,coords,oldcoords) G = F.isopar(trf) trf = Isopar(eltype,coords,oldcoords) G = trf.transform(F) G = isopar(F,eltype,coords,oldcoords) """ # REM: we should create a function to produce these data # LAGRANGIAN : 1,2,3 dim (LINE, QUAD, HEX) # TRIANGLE : 2,3 dim (TRI,TET) # SERENDIPITY : list ??? # lagrangian(nx,ny,nz) # type ndim degree+1 # 'line2': ('lagrangian',1,(1)) # 'quad9': ('lagrangian',2,(2,2)) # 'quad6': ('lagrangian',2,(3,2)) # 'quad6': 'lag-2-3-2' # 'quad8' : ('direct', 2, ('1','x','y','x*x','y*y','x*y','x*x*y','x*y*y')), # # generic name: # # 'lag-2-2-3' : ('lagrangian',2,(2,3)) # 'tri-2-2' : ('triangular',2,(2)) # 'lag-i-j-k' isodata = { 'line2' : (1, ('1','x')), 'line3' : (1, ('1','x','x*x')), 'line4' : (1, ('1','x','x**2','x**3')), 'tri3' : (2, ('1','x','y')), 'tri3' : (2, ('1','x','y')), 'tri6' : (2, ('1','x','y','x*x','y*y','x*y')), 'quad4' : (2, ('1','x','y','x*y')), 'quad8' : (2, ('1','x','y','x*x','y*y','x*y','x*x*y','x*y*y')), 'quad9' : (2, ('1','x','y','x*x','y*y','x*y','x*x*y','x*y*y', 'x*x*y*y')), 'quad13': (2, ('1','x','y','x*x','x*y','y*y', 'x*x*x','x*x*y','x*y*y','y*y*y', 'x*x*x*y','x*x*y*y','x*y*y*y')), 'quad16': (2, ('1','x','y','x*x','x*y','y*y', 'x*x*x','x*x*y','x*y*y','y*y*y', 'x*x*x*y','x*x*y*y','x*y*y*y', 'x*x*x*y*y','x*x*y*y*y','x*x*x*y*y*y')), 'tet4' : (3, ('1','x','y','z')), 'tet10' : (3, ('1','x','y','z','x*x','y*y','z*z','x*y','x*z','y*z')), 'hex8' : (3, ('1','x','y','z','x*y','x*z','y*z','x*y*z')), 'hex20' : (3, ('1','x','y','z','x*x','y*y','z*z','x*y','x*z','y*z', 'x*x*y','x*x*z','x*y*y','y*y*z','x*z*z','y*z*z','x*y*z', 'x*x*y*z','x*y*y*z','x*y*z*z')), 'hex27' : (3, ('1','x','y','z','x*x','y*y','z*z','x*y','x*z','y*z', 'x*x*y','x*x*z','x*y*y','y*y*z','x*z*z','y*z*z','x*y*z', 'x*x*y*y','x*x*z*z','y*y*z*z','x*x*y*z','x*y*y*z', 'x*y*z*z', 'x*x*y*y*z','x*x*y*z*z','x*y*y*z*z', 'x*x*y*y*z*z')), # quadratic in x,y, cubic in z 'hex36' : (3, ('1','x','y','z','x*x','y*y','z*z','x*y','x*z','y*z', 'x*x*y','x*x*z','x*y*y','y*y*z','x*z*z','y*z*z','x*y*z', 'x*x*y*y','x*x*z*z','y*y*z*z','x*x*y*z','x*y*y*z', 'x*y*z*z', 'x*x*y*y*z','x*x*y*z*z','x*y*y*z*z', 'x*x*y*y*z*z', 'z*z*z','x*z*z*z','y*z*z*z', 'x*x*z*z*z','y*y*z*z*z','x*y*z*z*z', 'x*x*y*z*z*z','x*y*y*z*z*z','x*x*y*y*z*z*z')), 'hex64': (3, ('1','x','y','z','x*x','y*y','z*z','x*y','x*z','y*z', 'x*x*y','x*x*z','x*y*y','y*y*z','x*z*z','y*z*z','x*y*z', 'x*x*x','y*y*y','z*z*z', 'x*x*y*y','x*x*z*z','y*y*z*z','x*x*y*z','x*y*y*z', 'x*y*z*z','x*x*x*y','x*x*x*z','x*y*y*y','y*y*y*z', 'x*z*z*z','y*z*z*z', 'x*x*x*y*y','x*x*x*z*z','x*x*y*y*y','y*y*y*z*z', 'z*z*z*x*x','z*z*z*y*y', 'x*x*x*y*z','x*y*y*y*z','z*z*z*x*y', 'x*x*y*y*z','x*x*y*z*z','x*y*y*z*z', 'x*x*x*y*y*y','x*x*x*z*z*z','y*y*y*z*z*z', 'x*x*x*y*y*z','x*x*x*y*z*z','y*y*y*x*x*z','y*y*y*x*z*z', 'z*z*z*x*x*y','z*z*z*x*y*y','x*x*y*y*z*z', 'x*x*x*y*y*y*z','x*x*x*y*z*z*z','y*y*y*x*z*z*z', 'x*x*x*y*y*z*z','y*y*y*x*x*z*z','z*z*z*x*x*y*y', 'x*x*x*y*y*y*z*z','x*x*x*z*z*z*y*y','y*y*y*z*z*z*x*x', 'x*x*x*y*y*y*z*z*z')), } def __init__(self,eltype,coords,oldcoords): """Create an isoparametric transformation. """ ndim,atoms = Isopar.isodata[eltype] coords = coords.view().reshape(-1,3) oldcoords = oldcoords.view().reshape(-1,3) x = oldcoords[:,0] if ndim > 1: y = oldcoords[:,1] else: y = 0 if ndim > 2: z = oldcoords[:,2] else: z = 0 aa = evaluate(atoms,x,y,z) ab = linalg.solve(aa,coords) self.eltype = eltype self.trf = ab def transform(self,X): """Apply isoparametric transform to a set of coordinates. Returns a Coords array with same shape as X """ if not isinstance(X,Coords): try: X = Coords(X) except: raise ValueError,"Expected a Coords object as argument" ndim,atoms = Isopar.isodata[self.eltype] aa = evaluate(atoms,X.x().ravel(),X.y().ravel(),X.z().ravel()) xx = dot(aa,self.trf).reshape(X.shape) if ndim < 3: xx[...,ndim:] += X[...,ndim:] return X.__class__(xx) # End pyformex-0.8.6/pyformex/plugins/tools_menu.py0000644000211500021150000004272211705104656021336 0ustar benebene00000000000000# $Id: tools_menu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """tools_menu.py Graphic Tools plugin menu for pyFormex. """ import pyformex as pf from gui import menu from geometry_menu import autoName import utils from formex import * from gui.draw import * from gui.widgets import simpleInputItem as _I from plugins import objects from plugins.tools import * from plugins.trisurface import TriSurface def editFormex(F,name): """Edit a Formex""" items = [('coords',repr(array(F.coords)),'text')] if F.prop is not None: items.append(('prop',repr(array(F.prop)),'text')) items.append(('eltype',F.eltype)) res = askItems(items) if res: x = eval(res['coords']) p = res.get('eltype',None) if type(p) is str: p = eval(p) e = res['eltype'] print(x) print(p) print(e) F.__init__(x,F.prop,e) Formex.edit = editFormex def command(): """Execute an interactive command.""" res = askItems([('command','','text')]) if res: cmd = res['command'] print("Command: %s" % cmd) exec(cmd) ##################### database tools ########################## def _init_(): global database,_drawables database = pf.GUI.database _drawables = pf.GUI.selection['geometry'] def printall(): """Print all global variable names.""" print(listAll()) def printval(): """Print selected global variables.""" database.ask() database.printval() def printbbox(): """Print selected global variables.""" database.ask() database.printbbox() def keep_(): """Forget global variables.""" database.ask() database.keep() def forget_(): """Forget global variables.""" database.ask() database.forget() def create(): """Create a new global variable with default initial contents.""" res = askItems([('name','')]) if res: name = res['name'] if name in database: warning("The variable named '%s' already exists!") else: export({name:'__initial__'}) def edit(): """Edit a global variable.""" database.ask(mode='single') F = database.check(single=True) if F is not None: name = database.names[0] if hasattr(F,'edit'): # Call specialized editor F.edit(name) else: # Use general editor res = askItems([(name,repr(F),'text')]) if res: print(res) pf.PF.update(res) def rename_(): """Rename a global variable.""" database.ask(mode='single') F = database.check(single=True) if F is not None: oldname = database.names[0] res = askItems([('Name',oldname)],caption = 'Rename variable') if res: name = res['Name'] export({name:F}) database.forget() database.set(name) def delete_all(): printall() if ack("Are you sure you want to unrecoverably delete all global variables?"): forgetAll() def dos2unix(): fn = askFilename(multi=True) if fn: for f in fn: message("Converting file to UNIX: %s" % f) utils.dos2unix(f) def unix2dos(): fn = askFilename(multi=True) if fn: for f in fn: message("Converting file to DOS: %s" % f) utils.unix2dos(f) ##################### planes ########################## planes = objects.DrawableObjects(clas=Plane) pname = autoName(Plane) def editPlane(plane,name): res = askItems([('Point',list(plane.point())), ('Normal',list(plane.normal())), ('Size',(list(plane.size()[0]),list(plane.size()[1])))], caption = 'Edit Plane') if res: plane.P = res['Point'] plane.n = res['Normal'] plane.s = res['Size'] Plane.edit = editPlane def createPlaneCoordsPointNormal(): res = askItems([('Name',pname.next()), ('Point',(0.,0.,0.)), ('Normal',(1.,0.,0.)), ('Size',((1.,1.),(1.,1.)))], caption = 'Create a new Plane') if res: name = res['Name'] p = res['Point'] n = res['Normal'] s = res['Size'] P = Plane(p,n,s) export({name:P}) draw(P) def createPlaneCoords3Points(): res = askItems([('Name',pname.next()), ('Point 1',(0.,0.,0.)), ('Point 2', (0., 1., 0.)), ('Point 3', (0., 0., 1.)), ('Size',((1.,1.),(1.,1.)))], caption = 'Create a new Plane') if res: name = res['Name'] p1 = res['Point 1'] p2 = res['Point 2'] p3 = res['Point 3'] s = res['Size'] pts=[p1, p2, p3] P = Plane(pts,size=s) export({name:P}) draw(P) def createPlaneVisual3Points(): res = askItems([('Name',pname.next()), ('Size',((1.,1.),(1.,1.)))], caption = 'Create a new Plane') if res: name = res['Name'] s = res['Size'] picked = pick('point') pts = getCollection(picked) pts = asarray(pts).reshape(-1,3) if len(pts) == 3: P = Plane(pts,size=s) export({name:P}) draw(P) else: warning("You have to pick exactly three points.") ################# Create Selection ################### selection = None def set_selection(obj_type): global selection selection = None selection = pick(obj_type) def query(mode): set_selection(mode) print(report(selection)) def pick_actors(): set_selection('actor') def pick_elements(): print _drawables.names set_selection('element') def pick_points(): set_selection('point') def pick_edges(): set_selection('edge') def query_actors(): query('actor') def query_elements(): query('element') def query_points(): query('point') def query_edges(): query('edge') def query_distances(): set_selection('point') print(reportDistances(selection)) def query_angle(): showInfo("Select two line elements.") set_selection('element') print(reportAngles(selection)) def report_selection(): if selection is None: warning("You need to pick something first.") return print(selection) print(report(selection)) def edit_point(pt): x,y,z = pt dia = None def close(): dia.close() def accept(): dia.acceptData() res = dia.results return [ res[i] for i in 'xyz' ] dia = widgets.InputDialog( items = [_I(x=x,y=y,z=z),] ) dia.show() def edit_points(K): if K.obj_type == 'point': for k in K.keys(): o = pf.canvas.actors[k].object n = _drawables.names[k] ind = K[k] print "CHANGING points %s of object %s" % (ind,n) print o[ind] def setpropCollection(K,prop): """Set the property of a collection. prop should be a single non-negative integer value or None. If None is given, the prop attribute will be removed from the objects in collection even the non-selected items. If a selected object does not have a setProp method, it is ignored. """ if K.obj_type == 'actor': obj = [ pf.canvas.actors[i] for i in K.get(-1,[]) ] for o in obj: if hasattr(o,'setProp'): o.setProp(prop) elif K.obj_type in ['element','point']: for k in K.keys(): a = pf.canvas.actors[k] o = a.object print "SETPROP ACTOR %s" % type(o) n = _drawables.names[k] print "SETPROP DRAWABLE %s" % n O = named(n) print 'From actor: %s' % id(o) print 'From name: %s' % id(O) if id(o) != id(O): raise RuntimeError,"The id of the drawn object does not match the selection" if prop is None: o.setProp(prop) elif hasattr(o,'setProp'): if not hasattr(o,'prop') or o.prop is None: o.setProp(0) o.prop[K[k]] = prop o.setProp(o.prop) a.setColor(o.prop) a.redraw(mode=pf.canvas.rendermode) def setprop_selection(): """Set the property of the current selection. A property value is asked from the user and all items in the selection that have property have their value set to it. """ if selection is None: warning("You need to pick something first.") return print(selection) res = askItems([['property',0]], caption = 'Set Property Number for Selection (negative value to remove)') if res: prop = int(res['property']) if prop < 0: prop = None setpropCollection(selection,prop) removeHighlights() def grow_selection(): if selection is None: warning("You need to pick something first.") return print(selection) res = askItems([('mode','node','radio',['node','edge']), ('nsteps',1), ], caption = 'Grow method', ) if res: growCollection(selection,**res) print(selection) highlightElements(selection) def partition_selection(): """Partition the current selection and show the result.""" if selection is None: warning("You need to pick something first.") return if not selection.obj_type in ['actor','element']: warning("You need to pick actors or elements.") return for A in pf.canvas.actors: if not A.getType() == TriSurface: warning("Currently I can only partition TriSurfaces." ) return partitionCollection(selection) highlightPartitions(selection) def get_partition(): """Select some partitions from the current selection and show the result.""" if selection is None: warning("You need to pick something first.") return if not selection.obj_type in ['partition']: warning("You need to partition the selection first.") return res = askItems([['property',[1]]], caption='Partition property') if res: prop = res['property'] getPartition(selection,prop) highlightPartitions(selection) def export_selection(): if selection is None: warning("You need to pick something first.") return sel = getCollection(selection) if len(sel) == 0: warning("Nothing to export!") return options = ['list','single item'] default = options[0] if len(sel) == 1: default = options[1] res = askItems([('Export with name',''), ('Export as',default,'radio',options), ]) if res: name = res['Export with name'] if res['Export as'] == options[0]: export({name:sel}) elif res['Export as'] == options[1]: export(dict([ (name+"-%s"%i,v) for i,v in enumerate(sel)])) def sendMail(): import sendmail sender = pf.cfg['mail/sender'] if not sender: warning("You have to configure your email settings first") return res = askItems([ _I('sender',sender,text="From:",readonly=True), _I('to','',text="To:"), _I('cc','',text="Cc:"), _I('subject','',text="Subject:"), _I('text','',itemtype='text',text="Message:"), ]) if not res: return msg = sendmail.message(**res) print msg to = res['to'].split(',') cc = res['cc'].split(',') sendmail.sendmail(message=msg,sender=res['sender'],to=to+cc) print "Mail has been sent to %s" % to if cc: print " with copy to %s" % cc ###################### images ############################# def selectImage(): """Open a dialog to read an image file. """ global image from gui.widgets import ImageView from gui.imagecolor import image2glcolor,QImage # some default values filename = getcfg('datadir')+'/butterfly.png' w,h = 200,200 image = None # the loaded image diag = None # the image dialog # construct the image previewer widget viewer = ImageView(filename) def selectImage(fn): """Helper function to load and preview image""" fn = askImageFile(fn) if fn: viewer.showImage(fn) loadImage(fn) return fn def loadImage(fn): """Helper function to load the image and set its size in the dialog""" global image image = QImage(fn) if image.isNull(): warning("Could not load image '%s'" % fn) return None w,h = image.width(),image.height() print "size = %sx%s" % (w,h) diag = currentDialog() if diag: diag.updateData({'nx':w,'ny':h}) maxsiz = 40000. if w*h > maxsiz: scale = sqrt(maxsiz/w/h) w = int(w*scale) h = int(h*scale) return w,h res = askItems([ _I('filename',filename,text='Image file',itemtype='button',func=selectImage), _I('viewer',viewer,itemtype='widget'), # the image previewing widget _I('nx',w,text='width'), _I('ny',h,text='height'), ]) if not res: return None if image is None: print "Loading image" loadImage(filename) return image def showImage(): clear() im = selectImage() if im: drawImage(im) ################### menu ################# _menu = 'Tools' def create_menu(): """Create the Tools menu.""" _init_() MenuData = [ ('Global &Variables',[ (' &List All',printall), (' &Select',database.ask), (' &Print Value',printval), (' &Print BBox',printbbox), (' &Draw',_drawables.ask), (' &Create',create), (' &Change Value',edit), (' &Rename',rename_), (' &Keep',keep_), (' &Delete',forget_), (' &Delete All',delete_all), ]), ("---",None), ('&Execute pyFormex command',command), ("&DOS to Unix",dos2unix,dict(tooltip="Convert a text file from DOS to Unix line terminators")), ("&Unix to DOS",unix2dos), ("Send &Mail",sendMail), ("---",None), ("&Create Plane",[ ("Coordinates", [("Point and normal", createPlaneCoordsPointNormal), ("Three points", createPlaneCoords3Points), ]), ("Visually", [("Three points", createPlaneVisual3Points), ]), ]), ("&Select Plane",planes.ask), ("&Draw Selection",planes.draw), ("&Forget Selection",planes.forget), ("---",None), ("Show an &Image file as Formex",showImage), ("---",None), ('&Pick',[ ("&Actors",pick_actors), ("&Elements",pick_elements), ("&Points",pick_points), ("&Edges",pick_edges), ]), ('&Edit Points',edit_points), ("&Remove Highlights",removeHighlights), ("---",None), ('&Selection',[ ('&Create Report',report_selection), ('&Set Property',setprop_selection), ('&Grow',grow_selection), ('&Partition',partition_selection), ('&Get Partition',get_partition), ('&Export',export_selection), ]), ("---",None), ('&Query',[ ('&Actors',query_actors), ('&Elements',query_elements), ('&Points',query_points), ('&Edges',query_edges), ('&Distances',query_distances), ('&Angle',query_angle), ]), ("---",None), ('&Reload',reload_menu), ("&Close",close_menu), ] return menu.Menu(_menu,items=MenuData,parent=pf.GUI.menu,before='help') def show_menu(): """Show the menu.""" if not pf.GUI.menu.item(_menu): create_menu() def close_menu(): """Close the menu.""" pf.GUI.menu.removeItem(_menu) def reload_menu(): """Reload the menu.""" close_menu() show_menu() #################################################################### ######### What to do when the script is executed ################### if __name__ == "draw": reload_menu() # End pyformex-0.8.6/pyformex/plugins/fe_menu.py0000644000211500021150000000702511705104656020565 0ustar benebene00000000000000# $Id: fe_menu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """ Finite Element Plugin Menu for pyFormex. (C) 2009 Benedict Verhegghe. """ from plugins import formex_menu,trisurface import simple from elements import Hex8 ######################### functions ############################# def readModel(fn): """Read a nodes/elems model from file. Returns an (x,e) tuple or None """ if not os.path.exists(fn): error("Node file '%s' does not exist" % fn) return None efn = fn.replace('nodes','elems') if not os.path.exists(efn): error("Corresponding element file '%s' does not exist" % efn) return None print("Importing model %s" % fn) fil = open(fn,'r') noffset = 0 #noffset = int(fil.readline().split()[1]) a = fromfile(fil,sep=" ").reshape(-1,3) print(a.shape) x = Coords(a) print(x.shape) e = fromfile(efn,sep=" ",dtype=Int).reshape(-1,3) print(e.shape) # convert to numpy offset if noffset != 0: e -= noffset return x,e def importModel(fn=None): if fn is None: fn = askFilename(".","*nodes.txt",multi=True) if not fn: return if type(fn) == str: fn = [fn] for i,f in enumerate(fn): x,e = readModel(f) modelname = os.path.basename(f).replace('nodes.txt','') F = Formex(x[e],i)#,eltype='hex8') export({modelname:F}) formex_menu.selection.append(modelname) formex_menu.selection.draw() def drawModel(): F = formex_menu.selection.check(single=True) if F: draw(F) ################################## Menu ############################# _menu = 'FeModel' def create_menu(): """Create the menu.""" MenuData = [ ("&Import Model",importModel), ("&Draw Model",drawModel), ("---",None), ("&Reload Menu",reload_menu), ("&Close Menu",close_menu), ] return menu.Menu(_menu,items=MenuData,parent=pf.GUI.menu,before='help') def show_menu(): """Show the menu.""" if not pf.GUI.menu.item(_menu): create_menu() def close_menu(): """Close the menu.""" pf.GUI.menu.removeItem(_menu) def reload_menu(): """Reload the menu.""" close_menu() show_menu() #################################################################### ######### What to do when the script is executed ################### if __name__ == "draw": reload_menu() # End pyformex-0.8.6/pyformex/plugins/curve.py0000644000211500021150000015035211705104656020275 0ustar benebene00000000000000# $Id: curve.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Definition of curves in pyFormex. This module defines classes and functions specialized for handling one-dimensional geometry in pyFormex. These may be straight lines, polylines, higher order curves and collections thereof. In general, the curves are 3D, but special cases may be created for handling plane curves. """ # I wrote this software in my free time, for my joy, not as a commissioned task. # Any copyright claims made by my employer should therefore be considered void. # Acknowledgements: Gianluca De Santis import pyformex as pf from coords import * from geometry import Geometry from formex import Formex,connect from mesh import Mesh from geomtools import triangleCircumCircle,intersectionTimesLWP,intersectionPointsSWP,anyPerpendicularVector import utils ############################################################################## # THIS IS A PROPOSAL FOR THE CURVE API! # # attributes: # coords: coordinates of points defining the curve # parts: number of parts (e.g. straight segments of a polyline) # closed: is the curve closed or not # range: [min,max] : range of the parameter: default 0..1 # methods: # subPoints(t): returns points with parameter values t # points(ndiv,extend=[0.,0.]): returns points obtained by dividing each # part in ndiv sections at equal parameter distance. # pointsOn(): the defining points placed on the curve # pointsOff(): the defining points placeded off the curve (control points) class Curve(Geometry): """Base class for curve type classes. This is a virtual class intended to be subclassed. It defines the common definitions for all curve types. The subclasses should at least define the following:: sub_points(t,j) """ N_approx = 10 def __init__(self): Geometry.__init__(self) self.prop = None def pointsOn(self): return self.coords def pointsOff(self): return Coords() def ncoords(self): return self.coords.shape[0] def npoints(self): return self.pointsOn().shape[0] def endPoints(self): """Return start and end points of the curve. Returns a Coords with two points, or None if the curve is closed. """ if self.closed: #return self.coords[[0,0]] return None else: return self.coords[[0,-1]] def sub_points(self,t,j): """Return the points at values t in part j t can be an array of parameter values, j is a single segment number. """ raise NotImplementedError def sub_points_2(self,t,j): """Return the points at values,parts given by zip(t,j) t and j can both be arrays, but should have the same length. """ raise NotImplementedError def sub_directions(self,t,j): """Return the directions at values t in part j t can be an array of parameter values, j is a single segment number. """ raise NotImplementedError def sub_directions_2(self,t,j): """Return the directions at values,parts given by zip(t,j) t and j can both be arrays, but should have the same length. """ raise NotImplementedError def lengths(self): raise NotImplementedError def pointsAt(self,t): """Return the points at parameter values t. Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment. """ # Do not use asarray here! We change it! t = array(t).ravel() ti = floor(t).clip(min=0,max=self.nparts-1) t -= ti i = ti.astype(Int) try: allX = self.sub_points_2(t,i) except: allX = concatenate([ self.sub_points(tj,ij) for tj,ij in zip(t,i)]) return Coords(allX) def directionsAt(self,t): """Return the points at parameter values t. Parameter values are floating point values. Their integer part is interpreted as the curve segment number, and the decimal part goes from 0 to 1 over the segment. """ t = array(t).ravel() ti = floor(t).clip(min=0,max=self.nparts-1) t -= ti i = ti.astype(Int) try: allX = self.sub_directions_2(t,i) except: allX = concatenate([ self.sub_directions(tj,ij) for tj,ij in zip(t,i)]) return Coords(allX) def subPoints(self,div=10,extend=[0., 0.]): """Return a sequence of points on the Curve. - `div`: int or a list of floats (usually in the range [0.,1.]) If `div` is an integer, a list of floats is constructed by dividing the range [0.,1.] into `div` equal parts. The list of floats then specifies a set of parameter values for which points at in each part are returned. The points are returned in a single Coords in order of the parts. The extend parameter allows to extend the curve beyond the endpoints. The normal parameter space of each part is [0.0 .. 1.0]. The extend parameter will add a curve with parameter space [-extend[0] .. 0.0] for the first part, and a curve with parameter space [1.0 .. 1 + extend[0]] for the last part. The parameter step in the extensions will be adjusted slightly so that the specified extension is a multiple of the step size. If the curve is closed, the extend parameter is disregarded. """ # Subspline parts (without end point) if type(div) == int: u = arange(div) / float(div) else: u = array(div).ravel() div = len(u) parts = [ self.sub_points(u,j) for j in range(self.nparts) ] if not self.closed: nstart,nend = round_(asarray(extend)*div,0).astype(Int) # Extend at start if nstart > 0: u = arange(-nstart, 0) * extend[0] / nstart parts.insert(0,self.sub_points(u,0)) # Extend at end if nend > 0: u = 1. + arange(0, nend+1) * extend[1] / nend else: # Always extend at end to include last point u = array([1.]) parts.append(self.sub_points(u,self.nparts-1)) X = concatenate(parts,axis=0) return Coords(X) def length(self): """Return the total length of the curve. This is only available for curves that implement the 'lengths' method. """ return self.lengths().sum() def approx(self,ndiv=None,ntot=None): """Return a PolyLine approximation of the curve If no `ntot` is given, the curve is approximated by `ndiv` straight segments over each part of the curve. If `ntot` is given, the curve is approximated by `ntot` straight segments over the total curve. This is based on a first approximation with ndiv segments over each part. """ if ndiv is None: ndiv = self.N_approx PL = PolyLine(self.subPoints(ndiv),closed=self.closed) if ntot is not None: at = PL.atLength(ntot) X = PL.pointsAt(at) PL = PolyLine(X,closed=PL.closed) return PL.setProp(self.prop) def toFormex(self,*args,**kargs): """Convert a curve to a Formex. This creates a polyline approximation as a plex-2 Formex. This is mainly used for drawing curves that do not implement their own drawing routines. The method can be passed the same arguments as the `approx` method. """ return self.approx(*args,**kargs).toFormex() def setProp(self,p=None): """Create or destroy the property array for the Formex. A property array is a rank-1 integer array with dimension equal to the number of elements in the Formex (first dimension of data). You can specify a single value or a list/array of integer values. If the number of passed values is less than the number of elements, they wil be repeated. If you give more, they will be ignored. If a value None is given, the properties are removed from the Formex. """ try: self.prop = int(p) except: self.prop = None return self ############################################################################## # # Curves that can be transformed by Coords Transforms # ############################################################################## # class PolyLine(Curve): """A class representing a series of straight line segments. coords is a (npts,3) shaped array of coordinates of the subsequent vertices of the polyline (or a compatible data object). If closed == True, the polyline is closed by connecting the last point to the first. This does not change the vertex data. The `control` parameter has the same meaning as `coords` and is added for symmetry with other Curve classes. If specified, it will override the `coords` argument. """ def __init__(self,coords=[],control=None,closed=False): """Initialize a PolyLine from a coordinate array.""" Curve.__init__(self) if control is not None: coords = control if isinstance(coords,Formex): if coords.nplex() == 1: coords = coords.coords elif coords.nplex() == 2: coords = Coords.concatenate([coords.coords[:,0,:],coords.coords[-1,1,:]]) else: raise ValueError,"Only Formices with plexitude 1 or 2 can be converted to PolyLine" else: coords = Coords(coords) if coords.ndim != 2 or coords.shape[1] != 3 or coords.shape[0] < 2: raise ValueError,"Expected an (npoints,3) shaped coordinate array with npoints >= 2, got shape " + str(coords.shape) self.coords = coords self.nparts = self.coords.shape[0] if not closed: self.nparts -= 1 self.closed = closed def close(self): """Close a PolyLine. If the PolyLine is already closed, it is returned unchanged. Else it is closed by adding a segment from the last to the first point (even if these are coincident). ..warning :: This method changes the PolyLine inplace. """ if not self.closed: self.closed = True self.parts += 1 def open(self): """Open a closed PolyLine. If the PolyLine is closed, it is opened by removing the last segment. Else, it is returned unchanged. ..warning :: This method changes the PolyLine inplace. Use :meth:`split` if you want to open the PolyLine without losing a segment. """ if self.closed: self.closed = False self.parts -= 1 def nelems(self): return self.nparts def toFormex(self): """Return the polyline as a Formex.""" x = self.coords F = connect([x,x],bias=[0,1],loop=self.closed) return F.setProp(self.prop) def toMesh(self): """Convert the polyLine to a plex-2 Mesh. The returned Mesh is equivalent with the PolyLine. """ e1 = arange(self.ncoords()) elems = column_stack([e1,roll(e1,-1)]) if not self.closed: elems = elems[:-1] return Mesh(self.coords,elems,eltype='line2').setProp(self.prop) def sub_points(self,t,j): """Return the points at values t in part j""" j = int(j) t = asarray(t).reshape(-1,1) n = self.coords.shape[0] X0 = self.coords[j % n] X1 = self.coords[(j+1) % n] X = (1.-t) * X0 + t * X1 return X def sub_points2(self,t,j): """Return the points at value,part pairs (t,j)""" j = int(j) t = asarray(t).reshape(-1,1) n = self.coords.shape[0] X0 = self.coords[j % n] X1 = self.coords[(j+1) % n] X = (1.-t) * X0 + t * X1 return X def sub_directions(self,t,j): """Return the unit direction vectors at values t in part j.""" j = int(j) t = asarray(t).reshape(-1,1) return self.directions()[j].reshape(len(t),3) def vectors(self): """Return the vectors of each point to the next one. The vectors are returned as a Coords object. If the curve is not closed, the number of vectors returned is one less than the number of points. """ x = self.coords if self.closed: x1 = x x2 = roll(x,-1,axis=0) # NEXT POINT else: x1 = x[:-1] x2 = x[1:] return x2-x1 def directions(self,return_doubles=False): """Returns unit vectors in the direction of the next point. This directions are returned as a Coords object with the same number of elements as the point set. If two subsequent points are identical, the first one gets the direction of the previous segment. If more than two subsequent points are equal, an invalid direction (NaN) will result. If the curve is not closed, the last direction is set equal to the penultimate. If return_doubles is True, the return value is a tuple of the direction and an index of the points that are identical with their follower. """ d = normalize(self.vectors()) w = where(isnan(d).any(axis=-1))[0] d[w] = d[w-1] if not self.closed: d = concatenate([d,d[-1:]],axis=0) if return_doubles: return d,w else: return d def avgDirections(self,return_doubles=False): """Returns the average directions at points. For each point the returned direction is the average of the direction from the preceding point to the current, and the direction from the current to the next point. If the curve is open, the first and last direction are equal to the direction of the first, resp. last segment. Where two subsequent points are identical, the average directions are set equal to those of the segment ending in the first and the segment starting from the last. """ d,w = self.directions(True) d1 = d d2 = roll(d,1,axis=0) # PREVIOUS DIRECTION w = concatenate([w,w+1]) if not self.closed: w = concatenate([[0,self.npoints()-1],w]) w = setdiff1d(arange(self.npoints()),w) d[w] = 0.5 * (d1[w]+d2[w]) if return_doubles: return d,w else: return d def lengths(self): """Return the length of the parts of the curve.""" return length(self.vectors()) def atLength(self, div): """Returns the parameter values for relative curve lengths div. ``div`` is a list of relative curve lengths (from 0.0 to 1.0). As a convenience, a single integer value may be specified, in which case the relative curve lengths are found by dividing the interval [0.0,1.0] in the specified number of subintervals. The function returns a list with the parameter values for the points at the specified relative lengths. """ lens = self.lengths().cumsum() rlen = concatenate([[0.], lens/lens[-1]]) # relative length if type(div) == int: div = arange(div+1) / float(div) z = rlen.searchsorted(div) # we need interpolation wi = where(z>0)[0] zw = z[wi] L0 = rlen[zw-1] L1 = rlen[zw] ai = zw + (div[wi] - L1) / (L1-L0) at = zeros(len(div)) at[wi] = ai return at def reverse(self): """Return the same curve with the parameter direction reversed.""" return PolyLine(reverseAxis(self.coords,axis=0),closed=self.closed) def split(self,i): """Split the curve at the point i. Returns a list of open PolyLines: one, if the PolyLine is closed or i is one of the endpoints of an open PolyLine, two in other cases. """ res = [] if self.closed: res.append(PolyLine(roll(self.coords,-i,axis=0))) else: if i > 0: res.append(PolyLine(self.coords[:i+1])) if i < len(self.coords): res.append(PolyLine(self.coords[i:])) return res def cutWithPlane(self,p,n,side=''): """Return the parts of the polyline at one or both sides of a plane. If side is '+' or '-', return a list of PolyLines with the parts at the positive or negative side of the plane. For any other value, returns a tuple of two lists of PolyLines, the first one being the parts at the positive side. p is a point specified by 3 coordinates. n is the normal vector to a plane, specified by 3 components. """ n = asarray(n) p = asarray(p) d = self.coords.distanceFromPlane(p,n) t = d > 0.0 cut = t != roll(t,-1) if not self.closed: cut = cut[:-1] w = where(cut)[0] res = [[],[]] i = 0 if t[0]: sid = 0 else: sid = 1 Q = Coords() for j in w: #print "%s -- %s" % (i,j) P = intersectionPointsSWP(self.coords[j:j+2],p,n,mode='pair')[0] #print "%s -- %s cuts at %s" % (j,j+1,P) x = Coords.concatenate([Q,self.coords[i:j+1],P]) #print "%s + %s + %s = %s" % (Q.shape[0],j-i,P.shape[0],x.shape[0]) res[sid].append(PolyLine(x)) sid = 1-sid i = j+1 Q = P x = Coords.concatenate([Q,self.coords[i:]]) #print "%s + %s = %s" % (Q.shape[0],j-i,x.shape[0]) res[sid].append(PolyLine(x)) if self.closed: if len(res[sid]) > 1: x = Coords.concatenate([res[sid][-1].coords,res[sid][0].coords]) res[sid] = res[sid][1:-1] res[sid].append(PolyLine(x)) #print [len(r) for r in res] if len(res[sid]) == 1 and len(res[1-sid]) == 0: res[sid][0].closed = True # Do not use side in '+-', because '' in '+-' returns True if side in ['+','-']: return res['+-'.index(side)] else: return res def append(self,PL,fuse=True,**kargs): """Append another PolyLine to this one. Returns the concatenation of two open PolyLines. Closed PolyLines cannot be concatenated. """ if self.closed or PL.closed: raise RuntimeError,"Closed PolyLines cannot be concatenated." X = PL.coords if fuse: x = Coords.concatenate([self.coords[-1],X[0]]) x,e = x.fuse(nodesperbox=3) # !!! YES ! > 2 !!! if e[0] == e[1]: X = X[1:] return PolyLine(Coords.concatenate([self.coords,X])) # allow syntax PL1 + PL2 __add__ = append # BV: I'm not sure what this does and if it belongs here def distanceOfPoints(self,p,n,return_points=False): """_Find the distances of points p, perpendicular to the vectors n. p is a (np,3) shaped array of points. n is a (np,3) shaped array of vectors. The return value is a tuple OKpid,OKdist,OKpoints where: - OKpid is an array with the point numbers having a distance; - OKdist is an array with the distances for these points; - OKpoints is an array with the footpoints for these points and is only returned if return_points = True. """ q = self.pointsOn() if not self.closed: q = q[:-1] m = self.vectors() t = intersectionTimesLWP(q,m,p,n) t = t.transpose((1,0)) x = q[newaxis,:] + t[:,:,newaxis] * m[newaxis,:] inside = (t >= 0.) * (t <= 1.) pid = where(inside)[0] p = p[pid] x = x[inside] dist = length(x-p) OKpid = unique(pid) OKdist = array([ dist[pid == i].min() for i in OKpid ]) if return_points: minid = array([ dist[pid == i].argmin() for i in OKpid ]) OKpoints = array([ x[pid == i][j] for i,j in zip(OKpid,minid) ]).reshape(-1,3) return OKpid,OKdist,OKpoints return OKpid,OKdist # BV: same remark: what is this distance? def distanceOfPolyLine(self,PL,ds,return_points=False): """_Find the distances of the PolyLine PL. PL is first discretised by calculating a set of points p and direction vectors n at equal distance of approximately ds along PL. The distance of PL is then calculated as the distances of the set (p,n). If return_points = True, two extra values are returned: an array with the points p and an array with the footpoints matching p. """ ntot = int(ceil(PL.length()/ds)) t = PL.atLength(ntot) p = PL.pointsAt(t) n = PL.directionsAt(t) res = self.distanceOfPoints(p,n,return_points) if return_points: return res[1],p[res[0]],res[2] return res[1] ############################################################################## # class Line(PolyLine): """A Line is a PolyLine with exactly two points. Parameters: - `coords`: compatible with (2,3) shaped float array """ def __init__(self,coords): """Initialize the Line.""" PolyLine.__init__(self,coords) if self.coords.shape[0] != 2: raise ValueError, "Expected exactly two points, got %s" % coords.shape[0] ############################################################################## # class BezierSpline(Curve): """A class representing a Bezier spline curve of degree 1, 2 or 3. A Bezier spline of degree `d` is a continuous curve consisting of `nparts` successive parts, where each part is a Bezier curve of the same degree. Currently pyFormex can model linear, quadratic and cubic BezierSplines. A linear BezierSpline is equivalent to a PolyLine, which has more specialized methods than the BezierSpline, so it might be more sensible to use a PolyLine instead of the linear BezierSpline. A Bezier curve of degree `d` is determined by `d+1` control points, of which the first and the last are on the curve, while the intermediate `d-1` points are not. Since the end point of one part is the begin point of the next part, a BezierSpline is described by `ncontrol=d*nparts+1` control points if the curve is open, or `ncontrol=d*nparts` if the curve is closed. The constructor provides different ways to initialize the full set of control points. In many cases the off-curve control points can be generated automatically. Parameters: - `coords` : array_like (npoints,3) The points that are on the curve. For an open curve, npoints=nparts+1, for a closed curve, npoints = nparts. If not specified, the on-curve points should be included in the `control` argument. - `deriv` : array_like (npoints,3) or (2,3) If specified, it gives the direction of the curve at all points or at the endpoints only for a shape (2,3) array. For points where the direction is left unspecified or where the specified direction contains a `NaN` value, the direction is calculated as the average direction of the two line segments ending in the point. This will also be used for points where the specified direction contains a value `NaN`. In the two endpoints of an open curve however, this average direction can not be calculated: the two control points in these parts are set coincident. - `curl` : float The curl parameter can be set to influence the curliness of the curve in between two subsequent points. A value curl=0.0 results in straight segments. The higher the value, the more the curve becomes curled. - `control` : array(nparts,2,3) or array(ncontrol,3) If `coords` was specified, this should be a (nparts,2,3) array with the intermediate control points, two for each part. If `coords` was not specified, this should be the full array of `ncontrol` control points for the curve. The number of points should be a multiple of 3 plus 1. If the curve is closed, the last point is equal to the first and does not need to a multiple of 3 is also allowed, in which case the first point will be appended as last. If not specified, the control points are generated automatically from the `coords`, `deriv` and `curl` arguments. If specified, they override these parameters. - `closed` : boolean If True, the curve will be continued from the last point back to the first to create a closed curve. - `degree`: int (1, 2 or 3) Specfies the degree of the curve. Default is 3. - `endzerocurv` : boolean or tuple of two booleans. Specifies the end conditions for an open curve. If True, the end curvature will be forced to zero. The default is to use maximal continuity of the curvature. The value may be set to a tuple of two values to specify different conditions for both ends. This argument is ignored for a closed curve. """ coeffs = { 1: matrix([ [ 1., 0.], [-1., 1.], ]), 2: matrix([ [ 1., 0., 0.], [-2., 2., 0.], [ 1., -2., 1.], ]), 3: matrix([ [ 1., 0., 0., 0.], [-3., 3., 0., 0.], [ 3., -6., 3., 0.], [-1., 3., -3., 1.], ]), } def __init__(self,coords=None,deriv=None,curl=1./3.,control=None,closed=False,degree=3,endzerocurv=False): """Create a BezierSpline curve.""" Curve.__init__(self) if not degree > 0: raise ValueError,"Degree of BezierSpline should be >= 0!" if degree > 3: raise ValueError,"Currently, the highest degree of BezierSpline cureves is limited to 3!" if endzerocurv in [False,True]: endzerocurv = (endzerocurv,endzerocurv) if coords is None: # All control points are given, in a single array control = Coords(control) if len(control.shape) != 2 or control.shape[-1] != 3: raise ValueError,"If no coords argument given, the control parameter should have shape (ncontrol,3), but got %s" % str(control.shape) if closed: control = Coords.concatenate([control,control[:1]]) ncontrol = control.shape[0] nextra = (ncontrol-1) % degree if nextra != 0: nextra = degree - nextra control = Coords.concatenate([control,]+[control[:1]]*nextra) ncontrol = control.shape[0] nparts = (ncontrol-1) // degree else: # Oncurve points are specified separately coords = Coords(coords) ncoords = nparts = coords.shape[0] if not closed: nparts -= 1 if control is None: if degree == 1: control = coords[:nparts] elif degree == 2: if closed: P0 = 0.5 * (roll(coords,1,axis=0) + roll(coords,-1,axis=0)) P1 = 2*coords - P0 Q0 = 0.5*(roll(coords,1,axis=0) + P1) Q1 = 0.5*(roll(coords,-1,axis=0) + P1) Q = 0.5*(roll(Q0,-1,axis=0)+Q1) control = Q else: P0 = 0.5 * (coords[:-2] + coords[2:]) P1 = 2*coords[1:-1] - P0 Q0 = 0.5*(coords[:-2] + P1) Q1 = 0.5*(coords[2:] + P1) Q = 0.5*(Q0[1:]+Q1[:-1]) control = Coords.concatenate([Q0[:1],Q,Q1[-1:]],axis=0) elif degree == 3: P = PolyLine(coords,closed=closed) ampl = P.lengths().reshape(-1,1) if deriv is None: deriv = array([[nan,nan,nan]]*ncoords) else: deriv = Coords(deriv) nderiv = deriv.shape[0] if nderiv < ncoords: if nderiv != 2: raise ValueError,"Either all or 2 directions expected (got %s)" % nderiv deriv = concatenate([ deriv[:1], [[nan,nan,nan]]*(ncoords-2), deriv[-1:]]) undefined = isnan(deriv).any(axis=-1) if undefined.any(): deriv[undefined] = P.avgDirections()[undefined] if closed: p1 = coords + deriv*curl*ampl p2 = coords - deriv*curl*roll(ampl,1,axis=0) p2 = roll(p2,-1,axis=0) else: # Fix the first and last derivs if they were autoset #print undefined #print deriv if undefined[0]: if endzerocurv[0]: # option curvature 0: deriv[0] = [nan,nan,nan] else: # option max. continuity deriv[0] = 2.*deriv[0] - deriv[1] if undefined[-1]: if endzerocurv[1]: # option curvature 0: deriv[-1] = [nan,nan,nan] else: # option max. continuity deriv[-1] = 2.*deriv[-1] - deriv[-2] #print deriv p1 = coords[:-1] + deriv[:-1]*curl*ampl p2 = coords[1:] - deriv[1:]*curl*ampl if isnan(p1[0]).any(): p1[0] = p2[0] if isnan(p2[-1]).any(): p2[-1] = p1[-1] control = concatenate([p1,p2],axis=1) if control is not None and degree > 1: try: control = asarray(control).reshape(nparts,degree-1,3) control = Coords(control) #print "COORDS",coords #print "CONTROL",control except: print("coords array has shape %s" % str(coords.shape)) print("control array has shape %s" % str(control.shape)) raise ValueError,"Invalid control points for BezierSpline" # Join the coords and controls in a single array control = Coords.concatenate([coords[:nparts,newaxis,:],control],axis=1).reshape(-1,3) if control is not None: # We now have a multiple of degree coordinates, add the last: if closed: last = 0 else: last = -1 control = Coords.concatenate([control,coords[last]]) self.degree = degree self.coeffs = BezierSpline.coeffs[degree] self.coords = control self.nparts = nparts self.closed = closed def report(self): return "Degree: %s; Parts: %s, Ncoords: %s" % (self.degree,self.nparts,self.coords.shape[0]) def pointsOn(self): """Return the points on the curve. This returns a Coords object of shape [nparts+1]. For a closed curve, the last point will be equal to the first. """ return self.coords[::self.degree] def pointsOff(self): """Return the points off the curve (the control points) This returns a Coords object of shape [nparts,ndegree-1], or an empty Coords if degree <= 1. """ if self.degree > 1: return self.coords[:-1].reshape(-1,self.degree,3)[:,1:] else: return Coords() def part(self,j): """Returns the points defining part j of the curve.""" start = self.degree * j end = start + self.degree + 1 return self.coords[start:end] def sub_points(self,t,j): """Return the points at values t in part j.""" P = self.part(j) C = self.coeffs * P U = [ t**d for d in range(0,self.degree+1) ] U = column_stack(U) X = dot(U,C) return X def sub_directions(self,t,j): """Return the unit direction vectors at values t in part j.""" P = self.part(j) C = self.coeffs * P U = [ d*(t**(d-1)) if d >= 1 else zeros_like(t) for d in range(0,self.degree+1) ] U = column_stack(U) T = dot(U,C) T = normalize(T) return T def sub_curvature(self,t,j): """Return the curvature at values t in part j.""" P = self.part(j) C = self.coeffs * P U1 = [ d*(t**(d-1)) if d >= 1 else zeros_like(t) for d in range(0,self.degree+1) ] U1 = column_stack(U1) T1 = dot(U1,C) U2 = [ d*(d-1)*(t**(d-2)) if d >=2 else zeros_like(t) for d in range(0,self.degree+1) ] U2 = column_stack(U2) T2 = dot(U2,C) K = length(cross(T1,T2))/(length(T1)**3) return K def length_intgrnd(self,t,j): """Return the arc length integrand at value t in part j.""" P = self.part(j) C = self.coeffs * P U = [ d*(t**(d-1)) if d >= 1 else 0. for d in range(0,self.degree+1) ] U = array(U) T = dot(U,C) T = array(T).reshape(3) return length(T) def lengths(self): """Return the length of the parts of the curve.""" try: from scipy.integrate import quad except: pf.warning(""".. **The **lengths** function is not available. Most likely because 'python-scipy' is not installed on your system.""") return L = [ quad(self.length_intgrnd,0.,1.,args=(j,))[0] for j in range(self.nparts) ] return array(L) def toMesh(self): """Convert the BezierSpline to a Mesh. For degrees 1 or 2, the returned Mesh is equivalent with the BezierSpline, and will have element type 'line1', resp. 'line2'. For degree 3, the returned Mesh will currently be a quadratic approximation with element type 'line2'. """ if self.degree == 1: return self.approx(ndiv=1).toMesh() else: coords = self.subPoints(2) e1 = 2*arange(len(coords)/2) elems = column_stack([e1,e1+1,e1+2]) if self.closed: elems = elems[-1][-1] = 0 return Mesh(coords,elems,eltype='line3') # This is not activated (yet) because it would be called for # drawing curves. ## def toFormex(self): ## """Convert the BezierSpline to a Formex. ## This is notational convenience for:: ## self.toMesh().toFormex() ## """ ## return self.toMesh().toFormex() # BV: This should go as a specialization in the approx() method def approx_by_subdivision(self,tol=1.e-3): """Return a PolyLine approximation of the curve. tol is a tolerance value for the flatness of the curve. The flatness of each part is calculated as the maximum orthogonal distance of its intermediate control points from the straight segment through its end points. Parts for which the distance is larger than tol are subdivided using de Casteljau's algorithm. The subdivision stops if all parts are sufficiently flat. The return value is a PolyLine connecting the end points of all parts. """ # get the control points (nparts,degree+1,3) P = array([ self.part(j) for j in range(self.nparts) ]) T = resize(True,self.nparts) while T.any(): EP = P[T][:,[0,-1]] # end points (...,2,3) IP = P[T][:,1:-1] # intermediate points (...,degree-1,3) # compute maximum orthogonal distance of IP from line EP q,m = EP[:,0].reshape(-1,1,3),(EP[:,1]-EP[:,0]).reshape(-1,1,3) t = (dotpr(IP,m) - dotpr(q,m)) / dotpr(m,m) X = q + t[:,:,newaxis] * m d = length(X-IP).max(axis=1) # subdivide parts for which distance is larger than tol T[T] *= d > tol if T.any(): # apply de Casteljau's algorithm for t = 0.5 M = [ P[T] ] for i in range(self.degree): M.append( (M[-1][:,:-1]+M[-1][:,1:])/2 ) # compute new control points Q1 = stack([ Mi[:,0] for Mi in M ],axis=1) Q2 = stack([ Mi[:,-1] for Mi in M[::-1] ],axis=1) # concatenate the parts P = P[~T] indQ = T.cumsum()-1 indP = (1-T).cumsum()-1 P = [ [Q1[i],Q2[i]] if Ti else [P[j]] for i,j,Ti in zip(indQ,indP,T) ] P = Coords(concatenate(P,axis=0)) T = [ [Ti,Ti] if Ti else [Ti] for Ti in T ] T = concatenate(T,axis=0) # create PolyLine through end points P = Coords.concatenate([P[:,0],P[-1,-1]],axis=0) PL = PolyLine(P,closed=self.closed) return PL def extend(self,extend=[1.,1.]): """Extend the curve beyond its endpoints. This function will add a Bezier curve before the first part and/or after the last part by applying de Casteljau's algorithm on this part. """ if self.closed: return if extend[0] > 0.: # get the control points P = self.part(0) # apply de Casteljau's algorithm t = -extend[0] M = [ P ] for i in range(self.degree): M.append( (1.-t)*M[-1][:-1] + t*M[-1][1:] ) # compute control points Q = stack([ Mi[0] for Mi in M[::-1] ],axis=0) self.coords = Coords.concatenate([Q[:-1],self.coords]) self.nparts += 1 if extend[1] > 0.: # get the control points P = self.part(self.nparts-1) # apply de Casteljau's algorithm t = 1.+extend[1] M = [ P ] for i in range(self.degree): M.append( (1.-t)*M[-1][:-1] + t*M[-1][1:] ) # compute control points Q = stack([ Mi[-1] for Mi in M ],axis=0) self.coords = Coords.concatenate([self.coords,Q[1:]]) self.nparts += 1 def reverse(self): """Return the same curve with the parameter direction reversed.""" control = reverseAxis(self.coords,axis=0) return BezierSpline(control=control,closed=self.closed,degree=self.degree) ############################################################################## # class CardinalSpline(BezierSpline): """A class representing a cardinal spline. Create a natural spline through the given points. The Cardinal Spline with given tension is a Bezier Spline with curl :math: `curl = ( 1 - tension) / 3` The separate class name is retained for compatibility and convenience. See CardinalSpline2 for a direct implementation (it misses the end intervals of the point set). """ def __init__(self,coords,tension=0.0,closed=False,endzerocurv=False): """Create a natural spline through the given points.""" curl = (1.-tension)/3. BezierSpline.__init__(self,coords,curl=(1.-tension)/3.,closed=closed,endzerocurv=endzerocurv) ############################################################################## # class CardinalSpline2(Curve): """A class representing a cardinal spline.""" def __init__(self,coords,tension=0.0,closed=False): """Create a natural spline through the given points. This is a direct implementation of the Cardinal Spline. For open curves, it misses the interpolation in the two end intervals of the point set. """ Curve.__init__(self) coords = Coords(coords) self.coords = coords self.nparts = self.coords.shape[0] if not closed: self.nparts -= 3 self.closed = closed self.tension = float(tension) s = (1.-self.tension)/2. self.coeffs = matrix([[-s, 2-s, s-2., s], [2*s, s-3., 3.-2*s, -s], [-s, 0., s, 0.], [0., 1., 0., 0.]])#pag.429 of open GL def sub_points(self,t,j): n = self.coords.shape[0] i = (j + arange(4)) % n P = self.coords[i] C = self.coeffs * P U = column_stack([t**3., t**2., t, ones_like(t)]) X = dot(U,C) return X ############################################################################## class NaturalSpline(Curve): """A class representing a natural spline.""" def __init__(self,coords,closed=False,endzerocurv=False): """Create a natural spline through the given points. coords specifies the coordinates of a set of points. A natural spline is constructed through this points. closed specifies whether the curve is closed or not. endzerocurv specifies the end conditions for an open curve. If True, the end curvature will forced to be zero. The default is to use maximal continuity (up to the third derivative) between the first two splines. The value may be set to a tuple of two values to specify different end conditions for both ends. This argument is ignored for a closed curve. """ Curve.__init__(self) coords = Coords(coords) if closed: coords = Coords.concatenate([coords,coords[:1]]) self.nparts = coords.shape[0] - 1 self.closed = closed if not closed: if endzerocurv in [False,True]: self.endzerocurv = (endzerocurv,endzerocurv) else: self.endzerocurv = endzerocurv self.coords = coords self.compute_coefficients() def _set_coords(self,coords): C = self.copy() C._set_coords_inplace(coords) C.compute_coefficients() return C def compute_coefficients(self): # x, y, z = self.coords.x(),self.coords.y(),self.coords.z() n = self.nparts M = zeros([4*n, 4*n]) B = zeros([4*n, 3]) # constant submatrix m = array([[0., 0., 0., 1., 0., 0., 0., 0.], [1., 1., 1., 1., 0., 0., 0., 0.], [3., 2., 1., 0., 0., 0.,-1., 0.], [6., 2., 0., 0., 0.,-2., 0., 0.]]) for i in range(n-1): f = 4*i M[f:f+4,f:f+8] = m B[f:f+2] = self.coords[i:i+2] # the last spline passes through the last 2 points f = 4*(n-1) M[f:f+2, f:f+4] = m[:2,:4] B[f:f+2] = self.coords[-2:] #add the appropriate end constrains if self.closed: # first and second derivatives at starting and last point # (that are actually the same point) are the same M[f+2, f:f+4] = m[2, :4] M[f+2, 0:4] = m[2, 4:] M[f+3, f:f+4] = m[3, :4] M[f+3, 0:4] = m[3, 4:] else: if self.endzerocurv[0]: # second derivatives at start is zero M[f+2, 0:4] = m[3, 4:] else: # third derivative is the same between the first 2 splines M[f+2, [0, 4]] = array([6.,-6.]) if self.endzerocurv[1]: # second derivatives at end is zero M[f+3, f:f+4] = m[3, :4] else: # third derivative is the same between the last 2 splines M[f+3, [f-4, f]] = array([6.,-6.]) #calculate the coefficients C = linalg.solve(M,B) self.coeffs = array(C).reshape(-1,4,3) def sub_points(self,t,j): C = self.coeffs[j] U = column_stack([t**3., t**2., t, ones_like(t)]) X = dot(U,C) return X ############################################################################## def circle(): """Create a spline approximation of a circle. The returned circle lies in the x,y plane, has its center at (0,0,0) and has a radius 1. In the current implementation it is approximated by a bezier spline with curl 0.375058 through 8 points. """ pts = Formex([1.0,0.0,0.0]).rosette(8,45.).coords.reshape(-1,3) return BezierSpline(pts,curl=0.375058,closed=True) class Arc3(Curve): """A class representing a circular arc.""" def __init__(self,coords): """Create a circular arc. The arc is specified by 3 non-colinear points. """ Curve.__init__(self) self.nparts = 1 self.closed = False self._set_coords(Coords(coords)) def _set_coords(self,coords): C = self.copy() C._set_coords_inplace(coords) if self.coords.shape != (3,3): raise ValueError,"Expected 3 points" r,C,n = triangleCircumCircle(self.coords.reshape(-1,3,3)) self.radius,self.center,self.normal = r[0],C[0],n[0] self.angles = vectorPairAngle(Coords([1.,0.,0.]),self.coords-self.center) print("Radius %s, Center %s, Normal %s" % (self.radius,self.center,self.normal)) print("ANGLES=%s" % (self.angles)) return C def sub_points(self,t,j): a = t*(self.angles[-1]-self.angles[0]) X = Coords(column_stack([cos(a),sin(a),zeros_like(a)])) X = X.scale(self.radius).rotate(self.angles[0]/Deg).translate(self.center) return X class Arc(Curve): """A class representing a circular arc.""" def __init__(self,coords=None,center=None,radius=None,angles=None,angle_spec=Deg): """Create a circular arc. The arc can be specified by 3 points (begin, center, end) or by center, radius and two angles. In the latter case, the arc lies in a plane parallel to the x,y plane. If specified by 3 colinear points, the plane of the circle will be parallel to the x,y plane if the points are in such plane, else the plane will be parallel to the z-axis. """ # Internally, we store the coords Curve.__init__(self) self.nparts = 1 self.closed = False if coords is not None: self.coords = Coords(coords) if self.coords.shape != (3,3): raise ValueError,"Expected 3 points" self.center = self.coords[1] v = self.coords-self.center self.radius = length(v[1]) try: self.normal = unitVector(cross(v[0],v[2])) except: pf.warning("The three points defining the Arc seem to be colinear: I will use a random orientation.") self.normal = anyPerpendicularVector(v[0]) self.angles = [ vectorPairAngle(Coords([1.,0.,0.]),x-self.center) for x in self.coords[[0,2]] ] else: #try: self.center = center self.radius = radius self.normal = [0.,0.,1.] self.angles = [ a * angle_spec for a in angles ] while self.angles[1] < self.angles[0]: self.angles[1] += 2*pi while self.angles[1] > self.angles[0] + 2*pi: self.angles[1] -= 2*pi begin,end = self.sub_points(array([0.0,1.0]),0) self.coords = Coords([begin,self.center,end]) #except: # raise ValueError,"Invalid data for Arc" #print("New ARC") #print(" Center %s, Radius %s, Normal %s" % (self.center,self.radius,self.normal)) #print(" Angles=%s" % (self.angles)) #print(" Start=%s; End=%s" % (self.coords[1],self.coords[2])) def sub_points(self,t,j): a = t*(self.angles[-1]-self.angles[0]) X = Coords(column_stack([cos(a),sin(a),zeros_like(a)])) X = X.scale(self.radius).rotate(self.angles[0]/Deg).translate(self.center) return X def approx(self,chordal=0.01,ndiv=None): """Return a PolyLine approximation of the Arc. Approximates the Arc by a sequence of inscribed straight line segments. If `ndiv` is specified, the arc is divided in pecisely `ndiv` segments. If `ndiv` is not given, the number of segments is determined from the chordal distance tolerance. It will guarantee that the distance of any point of the arc to the chordal approximation is less or equal than `chordal` times the radius of the arc. """ if ndiv is None: phi = 2.*arccos(1.-chordal) rng = abs(self.angles[1] - self.angles[0]) ndiv = int(ceil(rng/phi)) return Curve.approx(self,ndiv) class Spiral(Curve): """A class representing a spiral curve.""" def __init__(self,turns=2.0,nparts=100,rfunc=None): Curve.__init__(self) if rfunc == None: rfunc = lambda x:x self.coords = Coords([0.,0.,0.]).replic(npoints+1).hypercylindrical() self.nparts = nparts self.closed = False ############################################################################## # Other functions def convertFormexToCurve(self,closed=False): """Convert a Formex to a Curve. The following Formices can be converted to a Curve: - plex 2 : to PolyLine - plex 3 : to BezierSpline with degree=2 - plex 4 : to BezierSpline """ points = Coords.concatenate([self.coords[:,0,:],self.coords[-1:,-1,:]],axis=0) if self.nplex() == 2: curve = PolyLine(points,closed=closed) elif self.nplex() == 3: control = self.coords[:,1,:] curve = BezierSpline(points,control=control,closed=closed,degree=2) elif self.nplex() == 4: control = self.coords[:,1:3,:] curve = BezierSpline(points,control=control,closed=closed) else: raise ValueError,"Can not convert %s-plex Formex to a Curve" % self.nplex() return curve Formex.toCurve = convertFormexToCurve ############################################################################## # # DEPRECATED # class Polygon(PolyLine): @utils.deprecation("The curve.Polygon class is deprecated. Please use curve.Polyline(closed=True) or polygon.Polygon instead.") def __init__(self,coords=[]): PolyLine.__init__(self,coords,closed=True) def area(self,project=None): from geomtools import polygonArea return polygonArea(self.coords,project) class QuadBezierSpline(BezierSpline): @utils.deprecation("The use of the QuadBezierSpline class is deprecated. Use the BezierSpline class with parameter `degree = 2` instead.") def __init__(self,coords,**kargs): """Create a natural spline through the given points.""" kargs['degree'] = 2 BezierSpline.__init__(self,coords,**kargs) # End pyformex-0.8.6/pyformex/plugins/objects.py0000644000211500021150000003454111705104656020603 0ustar benebene00000000000000# $Id: objects.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Selection of objects from the global dictionary. This is a support module for other pyFormex plugins. """ import pyformex as pf from coords import bbox from script import named from gui.draw import drawBbox,_I import geomfile import odict from copy import deepcopy class Objects(object): """A selection of objects from the pyFormex Globals(). The class provides facilities to filter the global objects by their type and select one or more objects by their name(s). The values of these objects can be changed and the changes can be undone. """ def __init__(self,clas=None,like=None,filter=None,namelist=[]): """Create a new selection of objects. If a filter is given, only objects passing it will be accepted. The filter will be applied dynamically on the dict. If a list of names is given, the current selection will be set to those names (provided they are in the dictionary). """ self.clas = clas self.like = like self.filter = filter self.names = [] self.values = [] self.clear() if namelist: self.set(namelist) def object_type(self): """Return the type of objects in this selection.""" if self.clas: return self.clas.__name__+' ' else: return '' def set(self,names): """Set the selection to a list of names. namelist can be a single object name or a list of names. This will also store the current values of the variables. """ if type(names) == str: names = [ names ] self.names = [ s for s in names if type(s) == str ] self.values = map(named,self.names) def append(self,name,value=None): """Add a name,value to a selection. If no value is given, its current value is used. If a value is given, it is exported. """ self.names.append(name) if value is None: value = named(name) else: export({name:value}) self.values.append(value) def clear(self): """Clear the selection.""" self.set([]) def __getitem__(self,i): """Return selection item i""" return self.names[i] def listAll(self): """Return a list with all selectable objects. This lists all the global names in pyformex.PF that match the class and/or filter (if specified). """ return listAll(clas=self.clas,like=self.like,filter=self.filter) def selectAll(self): self.set(self.listAll()) def remember(self,copy=False): """Remember the current values of the variables in selection. If copy==True, the values are copied, so that the variables' current values can be changed inplace without affecting the remembered values. """ self.values = map(named,self.names) if copy: self.values = map(deepcopy,self.values) def changeValues(self,newvalues): """Replace the current values of selection by new ones. The old values are stored locally, to enable undo operations. This is only needed to change the values of objects that can not be changed inplace! """ self.remember() export2(self.names,newvalues) def undoChanges(self): """Undo the last changes of the values.""" export2(self.names,self.values) def check(self,single=False,warn=True): """Check that we have a current selection. Returns the list of Objects corresponding to the current selection. If single==True, the selection should hold exactly one Object name and a single Object instance is returned. If there is no selection, or more than one in case of single==True, an error message is displayed and None is returned """ self.names = [ n for n in self.names if n in pf.PF ] if len(self.names) == 0: if warn: warning("No %sobjects were selected" % self.object_type()) return None if single and len(self.names) > 1: if warn: warning("You should select exactly one %sobject" % self.object_type()) return None if single: return named(self.names[0]) else: return map(named,self.names) def odict(self): """Return the currently selected items as a dictionary. Returns an ODict with the currently selected objects in the order of the selection.names. """ return odict.ODict(zip(self.names,self.check(warn=False))) def ask(self,mode='multi'): """Show the names of known objects and let the user select one or more. mode can be set to'single' to select a single item. Return a list with the selected names, possibly empty (if nothing was selected by the user), or None if there is nothing to choose from. This also sets the current selection to the selected names, unless the return value is None, in which case the selection remains unchanged. """ choices = self.listAll() if not choices: return None res = widgets.ListSelection( caption='Known %sobjects' % self.object_type(), choices=self.listAll(), default=self.names, sort=True).getResult() if res is None: res = [] self.set(res) return res def ask1(self): """Select a single object from the list. Returns the object, not its name! """ if self.ask('single'): return named(self.names[0]) else: return None def forget(self): """Remove the selection from the globals.""" forget(self.names) self.clear() def keep(self): """Remove everything except the selection from the globals.""" forget([ n for n in self.listAll() if not n in self.names ]) def printval(self): """Print the selection.""" objects = self.check() if objects: for n,o in zip(self.names,objects): print("%s = %s" % (n,str(o))) def printbbox(self): """Print the bbox of the current selection.""" objects = self.check() if objects: for n,o in zip(self.names,objects): bb = o.bbox() pf.message("* %s (%s): bbox [%s, %s]" % (n,o.__class__.__name__,bb[0],bb[1])) if len(self.names) > 1: pf.message("** Overal bbox: [%s, %s]" % (bb[0],bb[1])) def writeToFile(self,filename): """Write objects to a geometry file.""" objects = self.odict() if objects: writeGeomFile(filename,objects) def readFromFile(self,filename): """Read objects from a geometry file.""" res = readGeomFile(filename) export(res) self.set(res.keys()) ###################### Drawable Objects ############################# from gui.draw import * # Default Annotations def draw_object_name(n): """Draw the name of an object at its center.""" return drawText3D(named(n).center(),n) def draw_elem_numbers(n): """Draw the numbers of an object's elements.""" return drawNumbers(named(n),color='blue') def draw_nodes(n): """Draw the nodes of an object.""" return draw(named(n).coords,nolight=True,wait=False) def draw_node_numbers(n): """Draw the numbers of an object's nodes.""" return drawNumbers(named(n).coords,color='red') def draw_free_edges(n): """Draw the feature edges of an object.""" return drawFreeEdges(named(n),color='black') def draw_bbox(n): """Draw the bbox of an object.""" return drawBbox(named(n)) class DrawableObjects(Objects): """A selection of drawable objects from the globals(). This is a subclass of Objects. The constructor has the same arguments as the Objects class, plus the following: - `annotations`: a set of functions that draw annotations of the objects. Each function should take an object name as argument, and draw the requested annotation for the named object. If the object does not have the annotation, it should be silently ignored. Default annotation functions available are: - draw_object_name - draw_elem_numbers - draw_nodes - draw_node_numbers - draw_bbox No annotation functions are activated by default. """ def __init__(self,*args,**kargs): Objects.__init__(self,*args,**kargs) self.autodraw = False self.shrink = None self.annotations = set() # Active annotations self._annotations = {} # Drawn annotations self._actors = [] def ask(self,mode='multi'): """Interactively sets the current selection.""" new = Objects.ask(self,mode) if new is not None: self.draw() return new def draw(self,**kargs): clear() pf.debug("Drawing SELECTION: %s" % self.names) self._actors = draw(self.names,clear=False,shrink=self.shrink,wait=False,**kargs) for f in self.annotations: self.drawAnnotation(f) def drawChanges(self): """Draws old and new version of a Formex with different colors. old and new can be a either Formex instances or names or lists thereof. old are drawn in yellow, new in the current color. """ self.draw() draw(self.values,color='yellow',bbox=None,clear=False,shrink=self.shrink,wait=False) def undoChanges(self): """Undo the last changes of the values.""" Objects.undoChanges(self) self.draw() def toggleAnnotation(self,f,onoff=None): """Toggle the display of an annotation On or Off. If given, onoff is True or False. If no onoff is given, this works as a toggle. """ if onoff is None: # toggle active = f not in self.annotations else: active = onoff if active: self.annotations.add(f) self.drawAnnotation(f) else: self.annotations.discard(f) self.removeAnnotation(f) def drawAnnotation(self,f): """Draw some annotation for the current selection.""" self._annotations[f] = [ f(n) for n in self.names ] def removeAnnotation(self,f): """Remove the annotation f.""" if f in self._annotations: # pf.canvas.removeAnnotations(self._annotations[f]) # Use remove, because some annotations are not canvas # annotations but actors! pf.canvas.remove(self._annotations[f]) pf.canvas.update() del self._annotations[f] def hasAnnotation(self,f): """Return the status of annotation f""" return f in self.annotations def hasNames(self): return self.hasAnnotation(draw_object_name) def hasNumbers(self): return self.hasAnnotation(draw_elem_numbers) def hasNodeNumbers(self): return self.hasAnnotation(draw_node_numbers) def hasFreeEdges(self): return self.hasAnnotation(draw_free_edges) def hasNodeMarks(self): return self.hasAnnotation(draw_nodes) def hasBbox(self): return self.hasAnnotation(draw_bbox) def toggleNames(self,onoff=None): self.toggleAnnotation(draw_object_name,onoff) def toggleNumbers(self,onoff=None): self.toggleAnnotation(draw_elem_numbers,onoff) def toggleNodeNumbers(self,onoff=None): self.toggleAnnotation(draw_node_numbers,onoff) def toggleFreeEdges(self,onoff=None): self.toggleAnnotation(draw_free_edges,onoff) def toggleNodes(self,onoff=None): self.toggleAnnotation(draw_nodes,onoff) def toggleBbox(self,onoff=None): self.toggleAnnotation(draw_bbox,onoff) def setProp(self,prop=None): """Set the property of the current selection. prop should be a single integer value or None. If None is given, a value will be asked from the user. If a negative value is given, the property is removed. If a selected object does not have a setProp method, it is ignored. """ objects = self.check() if objects: if prop is None: res = askItems([['property',0]], caption = 'Set Property Number for Selection (negative value to remove)') if res: prop = int(res['property']) if prop < 0: prop = None for o in objects: if hasattr(o,'setProp'): o.setProp(prop) self.draw() def delProp(self): """Delete the property of the current selection. This well reset the `prop` attribute of all selected objects to None. """ objects = self.check() if objects: for o in objects: if hasattr(o,'prop'): o.prop=None self.draw() if __name__ == "draw": # If executed as a pyformex script pf.debug('Reloading module %s' % __file__) elif __name__ == "__main__": print(__doc__) # End pyformex-0.8.6/pyformex/plugins/turtle.py0000644000211500021150000001116211705104656020463 0ustar benebene00000000000000# $Id: turtle.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Turtle graphics for pyFormex This module was mainly aimed at the drawing of Lindenmayer products (see :mod:`plugins.lima` and the Lima example). The idea is that a turtle can be moved in 2D from one position to another, thereby creating a line between start and endpoint or not. The current state of the turtle is defined by - ``pos``: the position as a 2D coordinate pair (x,y), - ``angle``: the moving direction as an angle (in degrees) with the x-axis, - ``step``: the speed, as a discrete step size. The start conditions are: ``pos=(0,0), step=1., angle=0.`` The followin example turtle script creates a unit square:: fd();ro(90);fd();ro(90);fd();ro(90);fd() """ import math deg = math.pi/180. def sind(arg): """Return the sine of an angle in degrees.""" return math.sin(arg*deg) def cosd(arg): """Return the cosine of an angle in degrees.""" return math.cos(arg*deg) def reset(): """Reset the turtle graphics engine to start conditions. This resets the turtle's state to the starting conditions ``pos=(0,0), step=1., angle=0.``, removes everything from the state save stack and empties the resulting path. """ global pos,step,angle,list,save pos = [0.,0.] step = 1. angle = 0. list=[] save=[] def push(): """Save the current state of the turtle. The turtle state includes its position, step and angle. """ global save save.append([pos,step,angle]) def pop(): """Restore the turtle state to the last saved state.""" global pos,step,angle,list,save pos,step,angle = save.pop(-1) def fd(d=None,connect=True): """Move forward over a step `d`, with or without drawing. The direction is the current direction. If `d` is not given, the step size is the current step. By default, the new position is connected to the previous with a straight line segment. """ global pos,step,angle,list if d: step = d p = [ pos[0] + step * cosd(angle), pos[1] + step * sind(angle) ] if connect: list.append([pos,p]) pos = p def mv(d=None): """Move over step `d` without drawing.""" fd(d,False) def ro(a): """Rotate over angle `a`. The new direction is incremented with `a`""" global pos,step,angle,list angle += a def go(p): """Go to position `p` (without drawing). While the `mv` method performs a relative move, this is an absolute move. `p` is a tuple of (x,y) values. """ global pos,step,angle,list pos = p def st(d): """Set the step size.""" global pos,step,angle,list step = d def an(a): """Set the angle""" global pos,step,angle,list angle = a def play(scr,glob=None): """Play all the commands in the script `scr` The script is a string of turtle commands, where each command is ended with a semicolon (';'). If a dict `glob` is specified, it will be update with the turtle module's globals() after each turtle command. """ import string for line in string.split(scr,";"): if line: if glob: glob.update(globals()) eval(line,glob) else: eval(line) return list reset() if __name__ == "__main__": def test(txt): l = play(txt) print("%s line segments" % len(l)) print(l) test("fd();ro(90);fd();ro(90);fd();ro(90);fd()") test("fd();ro(90);fd();ro(90);fd();ro(90);fd()") test("reset();fd();ro(90);fd();ro(90);fd();ro(90);fd()") # End pyformex-0.8.6/pyformex/plugins/flavia.py0000644000211500021150000001561711705104656020417 0ustar benebene00000000000000# $Id: flavia.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """ Interface with flavia FE result files. (C) 2010 Benedict Verhegghe. """ from arraytools import * from mesh import Mesh,mergeMeshes from plugins.fe_post import FeResult import shlex element_type_translation = { 'quadrilateral': { 4: 'quad4', 8: 'quad8', 9: 'quad9', }, } element_results_count = { 'vector': { 2: 2, 3: 3, }, 'matrix': { 2: 3, 3: 6, }, } ######################### functions ############################# def readMesh(fn): """Read a flavia mesh file. Returns a list of Meshes if succesful. """ fil = open(fn,'r') meshes = [] for line in fil: if line.startswith('#'): continue elif line.startswith('Mesh'): s = shlex.split(line.lower()) s = dict(zip(s[0::2],s[1::2])) print s ndim = int(s['dimension']) nplex = int(s['nnode']) eltype = element_type_translation[s['elemtype']][nplex] print "eltype = %s, ndim = %s" % (eltype,ndim) elif line.startswith('Coordinates'): coords = readCoords(fil,ndim) print "Coords %s" % str(coords.shape) elif line.startswith('Elements'): elems,props = readElems(fil,nplex) print "Elements %s %s" % (elems.shape,props.shape) meshes.append((elems,props)) else: print line elems,props = [m[0] for m in meshes],[m[1] for m in meshes] maxnod = max([ e.max() for e in elems]) coords = coords[:maxnod+1] return coords,elems,props,ndim def readCoords(fil,ndim): """Read a set of coordinates from a flavia file""" ncoords = 100 coords = zeros((ncoords,3),dtype=Float) for line in fil: if line.startswith('End Coordinates'): break else: s = line.split() i = int(s[0]) x = map(float,s[1:]) while i >= ncoords: coords = growAxis(coords,ncoords,axis=0,fill=0.0) ncoords = coords.shape[0] coords[i-1,:ndim] = x return coords def readElems(fil,nplex): """Read a set of coordinates from a flavia file""" nelems = 100 elems = zeros((nelems,nplex),dtype=Int) props = zeros((nelems),dtype=Int) for line in fil: if line.startswith('End Elements'): break else: s = line.split() i = int(s[0]) e = map(int,s[1:nplex+1]) p = int(s[nplex+1]) while i >= nelems: elems = growAxis(elems,nelems,axis=0,fill=0) props = growAxis(props,nelems,axis=0,fill=0) nelems = elems.shape[0] elems[i-1] = e props[i-1] = p defined = elems.sum(axis=1) > 0 return elems[defined]-1,props[defined] def readResults(fn,nnodes,ndim): """Read a flavia results file for an ndim mesh. """ fil = open(fn,'r') results = {} for line in fil: if line.startswith('#'): continue elif line.startswith('Result'): s = shlex.split(line.lower()) name = s[1] restype = s[4] domain = s[5] print domain if domain != 'onnodes': print "Currently only results on nodes can be read" print "Skipping %s %s" % (name,domain) nres = 0 continue nres = element_results_count[restype][ndim] elif line.startswith('Values'): if nres > 0: result = readResult(fil,nnodes,nres) print name results[name] = result else: print line return results def readResult(fil,nvalues,nres): """Read a set of results from a flavia file""" values = zeros((nvalues,nres),dtype=Float) for line in fil: if line.startswith('End Values'): break else: s = line.split() i = int(s[0]) x = map(float,s[1:]) values[i-1] = x return values from plugins.fe import Model from plugins.fe_post import FeResult def createFeResult(model,results): """Create an FeResult from meshes and results""" #model = Model(*mergeMeshes(meshes,fuse=False)) DB = FeResult() DB.nodes = model.coords DB.nnodes = model.coords.shape[0] DB.nodid = arange(DB.nnodes) DB.elems = dict(enumerate(model.elems)) DB.nelems = model.celems[-1] DB.Finalize() ndisp = results['displacement'].shape[1] nstrs = results['stress'].shape[1] DB.datasize['U'] = ndisp DB.datasize['S'] = nstrs for lc in range(1): # currently only 1 step DB.Increment(lc,0) DB.R['U'] = results['displacement'] DB.R['S'] = results['stress'] return DB def readFlavia(meshfile,resfile): """Read flavia results files Currently we only read matching pairs of meshfile,resfile files. """ if meshfile: coords,elems,props,ndim = readMesh(meshfile) if resfile: R = readResults(resfile,coords.shape[0],ndim) M = Model(coords,elems) DB = createFeResult(M,R) DB.printSteps() return DB if __name__ == "draw": chdir('/home/bene/prj/pyformex') name = 'FeResult-001' meshfile = name+'.flavia.msh' resfile = utils.changeExt(meshfile,'res') M = readMesh(meshfile) print M.coords.shape,M.elems.shape print M.coords,M.elems draw(M) R = readResults(resfile,M) DB = createFeResult(M,R) DB.printSteps() print DB.R print DB.datasize DB1 = FeResult() print DB1.datasize for key in [ 'U0','U1','U2','U3']: v = DB.getres(key) if v is not None: print "%s: %s" % (key,v.shape) # End pyformex-0.8.6/pyformex/plugins/dxf.py0000644000211500021150000002037311705104656017731 0ustar benebene00000000000000# $Id: dxf.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Read/write geometry in DXF format. This module allows to import and export some simple geometrical items in DXF format. """ from formex import * from plugins import curve def importDXF(filename): """Import (parts of) a DXF file into pyFormex. This function scans a DXF file for recognized entities and imports those entities as pyFormex objects. It is only a very partial importer, but has proven to be already very valuable for many users. `filename`: name of a DXF file. The return value is a list of pyFormex objects. Importing a DXF file is done in two steps: - First the DXF file is scanned and the recognized entities are formatted into a text with standard function calling syntax. See :func:`readDXF`. - Then the created text is executed as a Python script, producing equivalent pyFormex objects. See :func:`convertDXF`. """ text = readDXF(filename) if text: return convertDXF(text) else: return [] def readDXF(filename): """Read a DXF file and extract the recognized entities. `filename`: name of a .DXF file. Returns a multiline string with one line for each recognized entity, in a format that can directly be used by :func:`convertDXF`. This function requires the external program `dxfparser` which comes with the pyFormex distribution. It currently recognizes entities of type 'Arc', 'Line', 'Polyline', 'Vertex'. """ import utils if utils.hasExternal('dxfparser'): sta,out = utils.runCommand('dxfparser %s 2>/dev/null' % filename) if sta==0: return out else: return '' else: utils.warn('warn_no_dxfparser') return '' Entities = [] Vertices = [] def convertDXF(text): """Convert a textual representation of a DXF format to pyFormex objects. `text` : a multiline text representation of the contents of a DXF file. This text representation can e.g. be obtained by the function :func:`readDXF`. It contains lines defining DXF entities. A small example:: Arc(0.0,0.0,0.0,1.0,-90.,90.) Arc(0.0,0.0,0.0,3.0,-90.,90.) Line(0.0,-1.0,0.0,0.0,1.0,0.0) Polyline(0) Vertex(0.0,3.0,0.0) Vertex(-2.0,3.0,0.0) Vertex(-2.0,-7.0,0.0) Vertex(0.0,-7.0,0.0) Vertex(0.0,-3.0,0.0) Each line of the text defines a single entity or starts a multiple component entity. The text should be well aligned to constitute a proper Python script. Currently, the only defined entities are 'Arc', 'Line', 'Polyline', 'Vertex'. Returns a list of pyFormex objects corresponding to the text. The returned objects are of the following type: ============= ================================ function name object ============= ================================ Arc :class:`plugins.curve.Arc` Line :class:`plugins.curve.Line` Polyline :class:`plugins.curve.PolyLine` ============= ================================ No object is returned for the `Vertex` function: they define the vertices of a PolyLine. """ import types global Entities,Vertices Entities = [] Vertices = [] # Simple type (Line, Arc) create the object and append it to the # Entities list # Compound types (like Polyline) append the corresponding # EndEntity function to the Entities list def EndEntity(): global Entities,Vertices if Entities: f = Entities[-1] if type(f) is types.FunctionType: f() def Arc(x0,y0,z0,r,a0,a1): global Entities,Vertices count = len(Entities) part = curve.Arc(center=[x0,y0,z0],radius=r,angles=[a0,a1]).setProp(count) part.dxftype = 'Arc' Entities.append(part) def Line(x0,y0,z0,x1,y1,z1): global Entities,Vertices count = len(Entities) part = curve.Line([[x0,y0,z0],[x1,y1,z1]]).setProp(count) part.dxftype = 'Line' Entities.append(part) def Polyline(n): global Entities,Vertices EndEntity() Entities.append(EndPolyline) Vertices = [] def EndPolyline(): global Entities,Vertices count = len(Entities) - 1 part = curve.PolyLine(Vertices).setProp(count) part.dxftype = 'Polyline' Entities[-1] = part Vertices = [] def Vertex(x,y,z): global Entities,Vertices Vertices.append([x,y,z]) l = {'Line':Line, 'Arc':Arc, 'Polyline':Polyline, 'EndPolyline':EndPolyline, 'Vertex':Vertex} exec text in l EndEntity() return Entities class DxfExporter(object): """Export geometry in DXF format. While we certainly do not want to promote proprietary software, some of our users occasionally needed to export some model in DXF format. This class provides a minimum of functionality. """ def __init__(self,filename,terminator='\n'): """Open a file for export in DXF format. No check is done that the file has a '.dxf' extension. The file will by default be written in UNIX line termination mode. An existing file will be overwritten without warning! """ self.filename = filename self.fil = open(self.filename,'w') self.term = terminator def write(self,s): """Write a string to the dxf file. The string does not include the line terminator. """ self.fil.write(s+self.term) def out(self,code,data): """Output a string data item to the dxf file. code is the group code, data holds the data """ self.write('%3s' % code) self.write('%s' % data) def close(self): """Finalize and close the DXF file""" self.out(0,'EOF') self.fil.close() def section(self,name): """Start a new section""" self.out(0,'SECTION') self.out(2,name) def endSection(self): """End the current section""" self.out(0,'ENDSEC') def entities(self): """Start the ENTITIES section""" self.section('ENTITIES') def layer(self,layer): """Export the layer""" self.out(8,layer) def line(self,x,layer=0): """Export a line. x is a (2,3) shaped array """ self.out(0,'LINE') self.out(8,layer) for j in range(2): for i in range(3): self.out(10*(i+1)+j,x[j][i]) def exportDXF(filename,F): """Export a Formex to a DXF file Currently, only plex-2 Formices can be exported to DXF. """ if F.nplex() != 2: raise ValueError,"Can only export plex-2 Formices to DXF" dxf = DxfExporter(filename) dxf.entities() for i in F: dxf.line(i) dxf.endSection() dxf.close() # An example if __name__ == 'draw': #chdir(__file__) from simple import circle c = circle(360./20.,360./20.,360.) draw(c) exportDXF('circle1.dxf',c) #End pyformex-0.8.6/pyformex/plugins/section2d.py0000644000211500021150000001330211705104656021034 0ustar benebene00000000000000# $Id: section2d.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """Some functions operating on 2D structures. This is a plugin for pyFormex. (C) 2002 Benedict Verhegghe See the Section2D example for an example of its use. """ from plugins import sectionize from numpy import * ## initalization should be with a closed PolyLine, after checking it is plane class PlaneSection(object): """A class describing a general 2D section. The 2D section is the area inside a closed curve in the (x,y) plane. The curve is decribed by a finite number of points and by straight segments connecting them. """ def __init__(self,F): """Initialize a plane section. Initialization can be done either by a list of points or a set of line segments. By Points: Each point is connected to the following one, and (unless they are very close) the last one back to the first. Traversing the resulting path should rotate positively around the z axis to yield a positive surface. By Segments: It is the responsibilty of the user to ensure that the segments form a closed curve. If not, the calculated section data will be rather meaningless. """ if F.nplex() == 1: self.F = sectionize.connectPoints(F,close=True) elif F.nplex() == 2: self.F = F else: raise ValueError,"Expected a plex-1 or plex-2 Formex" def sectionChar(self): return sectionChar(self.F) def sectionChar(F): """Compute characteristics of plane sections. The plane sections are described by their circumference, consisting of a sequence of straight segments. The segment end point data are gathered in a plex-2 Formex. The segments should form a closed curve. The z-value of the coordinates does not have to be specified, and will be ignored if it is. The resulting path through the points should rotate positively around the z axis to yield a positive surface. The return value is a dict with the following characteristics: - `L` : circumference, - `A` : enclosed surface, - `Sx` : first area moment around global x-axis - `Sy` : first area moment around global y-axis - `Ixx` : second area moment around global x-axis - `Iyy` : second area moment around global y-axis - `Ixy` : product moment of area around global x,y-axes """ if F.nplex() != 2: raise ValueError, "Expected a plex-2 Formex!" #pf.debug("The circumference has %d segments" % F.nelems()) x = F.x() y = F.y() x0 = x[:,0] y0 = y[:,0] x1 = x[:,1] y1 = y[:,1] a = (x0*y1 - x1*y0) / 2 return { 'L' : sqrt((x1-x0)**2 + (y1-y0)**2).sum(), 'A' : a.sum(), 'Sx' : (a*(y0+y1)).sum()/3, 'Sy' : (a*(x0+x1)).sum()/3, 'Ixx' : (a*(y0*y0+y0*y1+y1*y1)).sum()/6, 'Iyy' : (a*(x0*x0+x0*x1+x1*x1)).sum()/6, 'Ixy' :-(a*(x0*y0+x1*y1+(x0*y1+x1*y0)/2)).sum()/6, } def extendedSectionChar(S): """Computes extended section characteristics for the given section. S is a dict with section basic section characteristics as returned by sectionChar(). This function computes and returns a dict with the following: - `xG`, `yG` : coordinates of the center of gravity G of the plane section - `IGxx`, `IGyy`, `IGxy` : second area moments and product around axes through G and parallel with the global x,y-axes - `alpha` : angle(in radians) between the global x,y axes and the principal axes (X,Y) of the section (X and Y always pass through G) - `IXX`, `IYY` : principal second area moments around X,Y respectively. (The second area product is always zero.) """ xG = S['Sy']/S['A'] yG = S['Sx']/S['A'] IGxx = S['Ixx'] - S['A'] * yG**2 IGyy = S['Iyy'] - S['A'] * xG**2 IGxy = S['Ixy'] + S['A'] * xG*yG alpha,IXX,IYY = princTensor2D(IGxx,IGyy,IGxy) return { 'xG' : xG, 'yG' : yG, 'IGxx' : IGxx, 'IGyy' : IGyy, 'IGxy' : IGxy, 'alpha': alpha, 'IXX' : IXX, 'IYY' : IYY, } def princTensor2D(Ixx,Iyy,Ixy): """Compute the principal values and directions of a 2D tensor. Returns a tuple with three values: - `alpha` : angle (in radians) from x-axis to principal X-axis - `IXX,IYY` : principal values of the tensor """ from math import sqrt,atan2 C = (Ixx+Iyy) * 0.5 D = (Ixx-Iyy) * 0.5 R = sqrt(D**2 + Ixy**2) IXX = C+R IYY = C-R alpha = atan2(Ixy,D) * 0.5 return alpha,IXX,IYY # End pyformex-0.8.6/pyformex/plugins/calpy_itf.py0000644000211500021150000001040711705104656021117 0ustar benebene00000000000000# $Id: calpy_itf.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Calpy interface for pyFormex. Currently this is only used to detect the installation of calpy and add the path of the calpy module to sys.path. Importing this module will automatically check the availabilty of calpy and set the sys.path accordingly. """ import pyformex as pf import utils import sys,os calpy_path = None # never tried to detect def detect(trypaths=None): """Check if we have calpy and if so, add its path to sys.path.""" global calpy_path calpy = utils.checkExternal('calpy') if not calpy: return pf.message("You have calpy version %s" % calpy) path = '' calpy = calpy.split('-')[0] # trim the version trailer if utils.checkVersion('calpy','0.3.4-rev3',external=True) >= 0: sta,out = utils.runCommand('calpy --whereami') if not sta: path = out pf.debug("I found calpy in %s" % path) if not path: if trypaths is None: trypaths = [ '/usr/local/lib', '/usr/local' ] for p in trypaths: path = '%s/calpy-%s' % (p,calpy) if os.path.exists(path): pf.debug('path exists: %s' % path) break else: pf.debug('path does not exist: %s' % path) path = '' if path: #path += '/calpy' pf.message("I found calpy in '%s'" % path) sys.path.append(path) calpy_path = path def check(trypaths=None): """Warn the user that calpy was not found.""" if calpy_path is None: detect(trypaths) try: import calpy except ImportError: pass if utils.hasModule('calpy',check=True): return True else: pf.warning("sys.path=%s\nSorry, I can not run this example, because you do not have calpy installed (at least not in a place where I can find it)." % sys.path) return False if __name__ == "__main__": print(__doc__) else: pf.debug("Loading plugin %s" % __file__) check() if check(): from calpy import plane class QuadInterpolator(plane.Quad): """A class to interface with calpy's Quad class. We want to use the calpy interpolation facilities without having to set up a full model for calpy processing. This class just sets the necessary data to make the interpolation mehtods (GP2NOdes, NodalAcc, NodalAvg) work. Parameters: - `nelems`: number of elements - `nplex`: plexitude of the elements (supported is 4 to 9) - `gprule`: gauss integration rule """ class Model: """A dummy class to keep calpy happy.""" option = 'dummy' tempfilename = 'dummy' def __init__(self,nelems,nplex,gprule): from numpy import array plane.Quad.__init__(self,'myQuad',gprule,self.Model) self.nnod = nplex self.nelems = nelems self.natCoords = array([1,1,-1,1,-1,-1,1,-1,0,1,-1,0,0,-1,1,0,0,0],dtype=float).reshape((9,2))[:self.nnod,:] ### End pyformex-0.8.6/pyformex/plugins/project.py0000644000211500021150000003446011705103030020601 0ustar benebene00000000000000# $Id: project.py 2138 2011-12-29 21:34:45Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """project.py Functions for managing a project in pyFormex. """ import pyformex as pf from pyformex import utils import os,sys import cPickle import gzip _signature_ = 'pyFormex 0.8.6' module_relocations = { 'plugins.mesh' : 'mesh', 'plugins.surface' : 'plugins.trisurface', } def find_global(module,name): """Override the import path of some classes""" pf.debug("I want to import %s from %s" % (name,module)) if module in module_relocations: module = module_relocations[module] pf.debug(" I will try module %s instead" % module) __import__(module) mod = sys.modules[module] clas = getattr(mod, name) return clas def pickle_load(f,try_resolve=True): """Load data from pickle file f.""" pi = cPickle.Unpickler(f) if try_resolve: pi.find_global = find_global return pi.load() highest_format = 3 class Project(dict): """Project: a persistent storage of pyFormex data. A pyFormex Project is a regular Python dict that can contain named data of any kind, and can be saved to a file to create persistence over different pyFormex sessions. The :class:`Project` class is used by pyFormex for the ``pyformex.PF`` global variable that collects variables exported from pyFormex scripts. While projects are mostly handled through the pyFormex GUI, notably the *File* menu, the user may also create and handle his own Project objects from a script. Because of the way pyFormex Projects are written to file, there may be problems when trying to read a project file that was created with another pyFormex version. Problems may occur if the project contains data of a class whose implementation has changed, or whose definition has been relocated. Our policy is to provide backwards compatibility: newer versions of pyFormex will normally read the older project formats. Saving is always done in the newest format, and these can generally not be read back by older program versions (unless you are prepared to do some hacking). .. warning:: Compatibility issues. Occasionally you may run into problems when reading back an old project file, especially when it was created by an unreleased (development) version of pyFormex. Because pyFormex is evolving fast, we can not test the full compatibility with every revision You can file a support request on the pyFormex `support tracker`_. and we will try to add the required conversion code to pyFormex. The project files are mainly intended as a means to easily save lots of data of any kind and to restore them in the same session or a later session, to pass them to another user (with the same or later pyFormex version), to store them over a medium time period. Occasionally opening and saving back your project files with newer pyFormex versions may help to avoid read-back problems over longer time. For a problemless long time storage of Geometry type objects you may consider to write them to a pyFormex Geometry file (.pgf) instead, since this uses a stable ascii based format. It can (currently) not deal with other data types however. Parameters: - `filename`: the name of the file where the Project data will be saved. If the file exists (and `access` is not `w`), it should be a previously saved Project and an attempt will be made to load the data from this file into the Project. If this fails, an error is raised. If the file exists and `access` is `w`, it will be overwritten, destroying any previous contents. If no filename is specified, a temporary file will be created when the Project is saved for the first time. The file with not be automatically deleted. The generated name can be retrieved from the filename attribute. - `access`: One of 'wr' (default), 'rw', 'w' or 'r'. If the string contains an 'r' the data from an existing file will be read into the dict. If the string starts with an 'r', the file should exist. If the string contains a 'w', the data can be written back to the file. The 'r' access mode is a read-only mode. ====== =============== ============ =================== access File must exist File is read File can be written ====== =============== ============ =================== r yes yes no rw yes yes yes wr no if it exists yes w no no yes ====== =============== ============ =================== - `convert`: if True (default), and the file is opened for reading, an attempt is made to open old projects in a compatibility mode, doing the necessary conversions to new data formats. If convert is set False, only the latest format can be read and older formats will generate an error. - `signature`: A text that will be written in the header record of the file. This can e.g. be used to record format version info. - `compression`: An integer from 0 to 9: compression level. For large data sets, compression leads to much smaller files. 0 is no compression, 9 is maximal compression. The default is 4. - `binary`: if False and no compression is used, storage is done in an ASCII format, allowing to edit the file. Otherwise, storage uses a binary format. Using binary=False is deprecated. - `data`: a dict-like object to initialize the Project contents. These data may override values read from the file. """ def __init__(self,filename=None,access='wr',convert=True,signature=_signature_,compression=5,binary=True,data={},**kargs): """Create a new project.""" if 'create' in kargs: utils.warn("The create=True argument should be replaced with access='w'") if 'legacy' in kargs: utils.warn("The legacy=True argument has become superfluous") self.filename = filename self.access = access self.signature = str(signature) self.gzip = compression if compression in range(1,10) else 0 self.mode = 'b' if binary or compression > 0 else '' dict.__init__(self) if filename and os.path.exists(filename) and 'r' in self.access: # read existing contents self.load(convert) self.update(data) if filename and access=='w': # destroy existing contents self.save() def header_data(self): """Construct the data to be saved in the header.""" store_attr = ['signature','gzip','mode','autofile','_autoscript_'] store_vals = [getattr(self,k,None) for k in store_attr] return dict([(k,v) for k,v in zip(store_attr,store_vals) if v is not None]) def save(self): """Save the project to file.""" if 'w' not in self.access: #print "NOT saving to readonly Project file access" return if self.filename is None: import tempfile fd,fn = tempfile.mkstemp(prefix='pyformex_',suffix='.pyf') self.filename = fn else: print "Saving project %s with mode %s and compression %s" % (self.filename,self.mode,self.gzip) #print(" Contents: %s" % self.keys()) f = open(self.filename,'w'+self.mode) # write header f.write("%s\n" % self.header_data()) f.flush() if self.mode == 'b': # When using binary, can as well use highest protocol protocol = cPickle.HIGHEST_PROTOCOL else: protocol = 0 if self.gzip: pyf = gzip.GzipFile(mode='w'+self.mode,compresslevel=self.gzip,fileobj=f) cPickle.dump(self,pyf,protocol) pyf.close() else: cPickle.dump(self,f,protocol) f.close() def readHeader(self): """Read the header from a project file. Tries to read the header from different legacy formats, and if succesfull, adjusts the project attributes according to the values in the header. Returns the open file if succesfull. """ self.format = -1 print("Reading project file: %s" % self.filename) f = open(self.filename,'rb') fpos = f.tell() s = f.readline() # Try subsequent formats try: # newest format has header in text format header = eval(s) self.__dict__.update(header) self.format = 3 except: # try OLD new format: the first pickle contains attributes try: p = pickle_load(f) self.__dict__.update(p) self.format = 2 except: s = s.strip() print "Header = '%s'" % s if s=='gzip' or s=='' or 'pyFormex' in s: # transitional format self.gzip = 5 self.format = 1 # NOT SURE IF THIS IS OK, NEED EXAMPLE FILE f.seek(fpos) else: # headerless format f.seek(0) self.gzip = 0 self.format = 0 return f def load(self,try_resolve=False): """Load a project from file. The loaded definitions will update the current project. """ f = self.readHeader() if self.format < highest_format: print "Format looks like %s" % self.format utils.warn('warn_old_project') with f: try: print "Unpickling gzip" pyf = gzip.GzipFile(fileobj=f,mode='rb') p = pickle_load(pyf,try_resolve) pyf.close() except: print "Unpickling clear" p = pickle_load(f,try_resolve) self.update(p) def convert(self,filename=None): """Convert an old format project file. The project file is read, and if successfull, is immediately saved. By default, this will overwrite the original file. If a filename is specified, the converted data are saved to that file. In both cases, access is set to 'wr', so the tha saved data can be read back immediately. """ self.load(True) print "GOT KEYS %s" % self.keys() if filename is not None: self.filename = filename self.access = 'w' print "Will now save to %s" % self.filename self.save() def uncompress(self): """Uncompress a compressed project file. The project file is read, and if successfull, is written back in uncompressed format. This allows to make conversions of the data inside. """ f = self.readHeader() print self.format,self.gzip if f: if self.gzip: try: pyf = gzip.GzipFile(self.filename,'r',self.gzip,f) except: self.gzip = 0 if self.gzip: fn = self.filename.replace('.pyf','_uncompressed.pyf') fu = open(fn,'w'+self.mode) h = self.header_data() h['gzip'] = 0 fu.write("%s\n" % h) while True: x = pyf.read() if x: fu.write(x) else: break fu.close() print "Uncompressed %s to %s" % (self.filename,fn) else: utils.warn("The contents of the file does not appear to be compressed.") f.close() def delete(self): """Unrecoverably delete the project file.""" os.remove(self.filename) # Test if __name__ == '__main__': d = dict(a=1,b=2,c=3,d=[1,2,3],e={'f':4,'g':5}) from numpy import random d['r'] = random.randint(0,100,(3,3)) print('DATA',d) P = Project('testa.pyf') P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) P = Project('testb.pyf',access='w') P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) P = Project() P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) for i in [0,1,3,5,7,9]: P = Project('testc%s.pyf'%i,access='w',compression=i) P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) P = Project('testl.pyf',access='w',legacy=True) P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) P = Project('testr.pyf',access='r') P.update(d) print('SAVE',P) P.save() P.clear() print('CLEAR',P) P.load() print('LOAD',P) # # End pyformex-0.8.6/pyformex/plugins/formian.py0000644000211500021150000001457111705104656020606 0ustar benebene00000000000000# $Id: formian.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Formian compatibility functions This module defines some Formex methods which perform the same functionality as the corresponding functions in `Formian`. The author of formex/formian had an incredible preference for newspeak: for every concept or function, a new name was invented. While this may give Formian the aspect of a sophisticated scientific background, it works rather distracting and ennoying for people that are already familiar with the basic ideas of 3D geometry, and prefer to use the standardized terms. Also, Formian has many different names for minor variation of the same concept. pyFormex implements these by using extra arguments, often optional. Finally, Formian starts numbering at one, while in pyFormex all numbering is zero-based. That means that the y-axis is 1 in pyFormex, but 2 in Formian. The functions defined in this module use the Formian names and conventions, thus facilitating the transscription of Formian scripts to pyFormex. Just import this module to make them available. For original pyFormex scripts, the use of this module is discouraged. Translate formian code to python No change : + - * / sign (defined further) abs sqrt,sin,cos,tan,asin,acos,atan,exp (from math) ln -> log (from math) ric -> int(round()) tic -> int() floc -> float() m^n -> pow(m,n) of m**n f|x -> f(x) tran(i,j)|F -> F.translate(i-1,j) ref(i,j)|F -> F.reflect(i-1,j) """ from formex import Formex # methods that have equivalent in Formex Formex.order = Formex.nelems Formex.plexitude = Formex.nplex Formex.grade = Formex.ndim Formex.cantle = Formex.element Formex.signet = Formex.point Formex.uniple = Formex.coord Formex.cop = Formex.remove Formex.cantle2str = Formex.element2str Formex.signet2str = Formex.point2str Formex.pex = Formex.unique # methods that can be emulated in Formex def formex_method(f): setattr(Formex,f.__name__,f) return f @formex_method def give(self): print(self.toFormian()) @formex_method def tran(self,dir,dist): return self.translate(dir-1,dist) @formex_method def ref(self,dir,dist): return self.reflect(dir-1,dist) @formex_method def rindle(self,n,dir,step): return self.replic(n,step,dir) @formex_method def rin(self,dir,n,dist): return self.replic(n,dist,dir-1) @formex_method def lam(self,dir,dist): return self+self.reflect(dir-1,dist) @formex_method def ros(self,i,j,x,y,n,angle): if (i,j) == (1,2): return self.rosette(n,angle,2,[x,y,0]) elif (i,j) == (2,3): return self.rosette(n,angle,0,[0,x,y]) elif (i,j) == (1,3): return self.rosette(n,-angle,1,[x,0,y]) @formex_method def tranic(self,*args,**kargs): n = len(args)/2 d = [ i-1 for i in args[:n] ] return self.translatem(*zip(d,args[n:])) @formex_method def tranid(self,t1,t2): return self.translate([t1,t2,0]) @formex_method def tranis(self,t1,t2): return self.translate([t1,0,t2]) @formex_method def tranit(self,t1,t2): return self.translate([0,t1,t2]) @formex_method def tranix(self,t1,t2,t3): return self.translate([t1,t2,t3]) @formex_method def tranad(self,a1,a2,b1,b2,t=None): return self.translate([b1-a1,b2-a2,0.],t) @formex_method def tranas(self,a1,a2,b1,b2,t=None): return self.translate([b1-a1,0.,b2-a2],t) @formex_method def tranat(self,a1,a2,b1,b2,t=None): return self.translate([0.,b1-a1,b2-a2],t) @formex_method def tranax(self,a1,a2,a3,b1,b2,b3,t=None): return self.translate([b1-a1,b2-a2,b3-a3],t) @formex_method def rinic(self,*args,**kargs): n = len(args)/3 F = self for d,m,t in zip(args[:n],args[n:2*n],args[2*n:]): F = F.rin(d,m,t) return F @formex_method def rinid(self,n1,n2,t1,t2): return self.rin(1,n1,t1).rin(2,n2,t2) @formex_method def rinis(self,n1,n2,t1,t2): return self.rin(1,n1,t1).rin(3,n2,t2) @formex_method def rinit(self,n1,n2,t1,t2): return self.rin(2,n1,t1).rin(3,n2,t2) @formex_method def lamic(self,*args,**kargs): n = len(args)/2 F = self for d,p in zip(args[:n],args[n:]): F = F.lam(d,p) return F @formex_method def lamid(self,t1,t2): return self.lam(1,t1).lam(2,t2) @formex_method def lamis(self,t1,t2): return self.lam(1,t1).lam(3,t2) @formex_method def lamit(self,t1,t2): return self.lam(2,t1).lam(2,t2) @formex_method def rosad(self,a,b,n=4,angle=90): return self.rosette(n,angle,2,[a,b,0]) @formex_method def rosas(self,a,b,n=4,angle=90): return self.rosette(n,angle,1,[a,0,b]) @formex_method def rosat(self,a,b,n=4,angle=90): return self.rosette(n,angle,0,[0,a,b]) @formex_method def genid(self,n1,n2,t1,t2,bias=0,taper=0): return self.replic2(n1,n2,t1,t2,0,1,bias,taper) @formex_method def genis(self,n1,n2,t1,t2,bias=0,taper=0): return self.replic2(n1,n2,t1,t2,0,2,bias,taper) @formex_method def genit(self,n1,n2,t1,t2,bias=0,taper=0): return self.replic2(n1,n2,t1,t2,1,2,bias,taper) @formex_method def bb(self,b1,b2): return self.scale([b1,b2,1.]) @formex_method def bc(self,b1,b2,b3): return self.cylindrical(scale=[b1,b2,b3]) @formex_method def bp(self,b1,b2): return self.cylindrical(scale=[b1,b2,1.]) @formex_method def bs(self,b1,b2,b3): return self.spherical(scale=[b1,b2,b3],colat=True) # Some functions def tic(f): return int(f) def ric(f): return int(round(f)) # End pyformex-0.8.6/pyformex/plugins/postproc.py0000644000211500021150000000466511705104656021027 0ustar benebene00000000000000# $Id: postproc.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Postprocessing functions Postprocessing means collecting a geometrical model and computed values from a numerical simulation, and render the values on the domain. """ from arraytools import * # Some functions to calculate a scalar value from a vector def norm2(A): return sqrt(square(asarray(A)).sum(axis=-1)) def norm(A,x): return power(power(asarray(A),x).sum(axis=-1),1./x) def max(A): return asarray(A).max(axis=-1) def min(A): return asarray(A).min(axis=-1) def frameScale(nframes=10,cycle='up',shape='linear'): """Return a sequence of scale values between -1 and +1. ``nframes`` : the number of steps between 0 and -1/+1 values. ``cycle``: determines how subsequent cycles occur: ``'up'``: ramping up ``'updown'``: ramping up and down ``'revert'``: ramping up and down then reverse up and down ``shape``: determines the shape of the amplitude curve: ``'linear'``: linear scaling ``'sine'``: sinusoidal scaling """ s = arange(nframes+1) if cycle in [ 'updown', 'revert' ]: s = concatenate([s, fliplr(s[:-1].reshape((1,-1)))[0]]) if cycle in [ 'revert' ]: s = concatenate([s, -fliplr(s[:-1].reshape((1,-1)))[0]]) return s.astype(float)/nframes # End pyformex-0.8.6/pyformex/plugins/fe_abq.py0000644000211500021150000022053511705104656020367 0ustar benebene00000000000000# $Id: fe_abq.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Exporting finite element models in Abaqus\ |trade| input file format. This module provides functions and classes to export finite element models from pyFormex in the Abaqus\ |trade| input format (.inp). The exporter handles the mesh geometry as well as model, node and element properties gathered in a :class:`PropertyDB` database (see module :mod:`properties`). While this module provides only a small part of the Abaqus input file format, it suffices for most standard jobs. While we continue to expand the interface, depending on our own necessities or when asked by third parties, we do not intend to make this into a full implementation of the Abaqus input specification. If you urgently need some missing function, there is always the possibility to edit the resulting text file or to import it into the Abaqus environment for further processing. The module provides two levels of functionality: on the lowest level, there are functions that just generate a part of an Abaqus input file, conforming to the Abaqus\ |trade| Keywords manual. Then there are higher level functions that read data from the property module and write them to the Abaqus input file and some data classes to organize all the data involved with the finite element model. """ from plugins.properties import * from plugins.fe import * from mydict import Dict,CDict import pyformex as pf from datetime import datetime import os,sys from utils import deprecation,removeDict ################################################## ## Some Abaqus .inp format output routines ################################################## def abqInputNames(job): """Returns corresponding Abq jobname and input filename. job can be either a jobname or input file name, with or without directory part, with or without extension (.inp) The Abq jobname is the basename without the extension. The abq filename is the abspath of the job with extension '.inp' """ jobname = os.path.basename(job) if jobname.endswith('.inp'): jobname = jobname[:-4] filename = os.path.abspath(job) if not filename.endswith('.inp'): filename += '.inp' return jobname,filename def nsetName(p): """Determine the name for writing a node set property.""" if p.name is None: return 'Nall' else: return p.name def esetName(p): """Determine the name for writing an element set property.""" if p.name is None: return 'Eall' else: return p.name ########################################################### ## Output Formatting Following Abaqus Keywords Manual ## ########################################################### # # !! This is only a very partial implementation # of the Abaqus keyword specs. # ## The following output functions return the formatted output ## and should be written to file by the caller. ############################################### def fmtCmd(cmd='*'): """Format a command.""" return '*'+cmd+'\n' def fmtData1D(data,npl=8,sep=', ',linesep='\n'): """Format numerical data in lines with maximum npl items. data is a numeric array. The array is flattened and then the data are formatted in lines with maximum npl items, separated by sep. Lines are separated by linesep. """ data = asarray(data) data = data.flat return linesep.join([ sep.join(map(str,data[i:i+npl])) for i in range(0,len(data),npl) ]) def fmtData(data,npl=8,sep=', ',linesep='\n'): """Format numerical data in lines with maximum npl items. data is a numeric array, which is coerced to be a 2D array, either by adding a first axis or by collapsing the first ndim-1 axies. Then the data are formatted in lines with maximum npl items, separated by sep. Lines are separated by linesep. """ data = asarray(data) data = data.reshape(-1,data.shape[-1]) return linesep.join([fmtData1D(row,npl,sep,linesep) for row in data])+linesep def fmtOptions(options): """Format the options of an Abaqus command line. - `options`: a dict with ABAQUS command keywords and values. If the keyword does not take any value, the value in the dict should be an empty string. Returns a comma-separated string of 'keyword' or 'keyword=value' fields. The string includes an initial comma. """ s = '' for k in options: s += ", %s" % k.upper() if options[k] != '': s += "=%s" % options[k] return s def fmtHeading(text=''): """Format the heading of the Abaqus input file.""" out = """** Abaqus input file created by %s (%s) ** *HEADING %s """ % (pf.Version,pf.Url,text) return out def fmtPart(name='Part-1'): """Start a new Part.""" out = """** Abaqus input file created by %s (%s) ** *PART """ % (name) return out materialswritten=[] #FI few lines of documentation about plastic and the damping needed def fmtMaterial(mat): """Write a material section. `mat` is the property dict of the material. The following keys are recognized and output accordingly: - `name`: if specified, and a material with this name has already been written, this function does nothing. - `elasticity`: one of 'LINEAR', 'HYPERELASTIC', 'ANISOTROPIC HYPERELASTIC', 'USER'. Default is 'LINEAR'. Defines the elastic behavior class of the material. The requirements for the other keys depend on this type. The fields labeled (opt) are optional. - 'LINEAR': - young_modulus - shear_modulus - (opt) poisson_ratio: it is calculated if None - 'HYERELASTIC': required: - model: one of 'ogden', 'polynomial' or 'reduced polynomial' - constants: list of all parameter required for the model (see Abaqus documentation) optional: - order: order of the model. If blank will be automatically calculated from the len of the constants list example:: intimaMat = { 'name': 'intima', 'density': 0.1, # Not Used, but Abaqus does not like a material without 'elasticity':'hyperelastic', 'type':'reduced polynomial', 'constants': [6.79E-03, 5.40E-01, -1.11, 10.65, -7.27, 1.63, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] } - 'ANISOTROPIC HYPERELASTIC': - 'USER': """ ## ======================== ## ANISOTROPIC HYPERELASTIC ## elasticity=anisotropic hyperelastic ## model= holzapfel ## constants= ist of int sorted abaqus parameter ## ======================== ## USER MATERIAL ## depvar = nr of dependand variables (24 for the nitinol umat) ## elasticity=user ## constants= list of int sorted abaqus parameter ## ============================ ## Additional parametrer ## plastic: list([yield stress, yield strain]) ## """ if mat.name is None or mat.name in materialswritten: return "" out ="*MATERIAL, NAME=%s\n" % mat.name materialswritten.append(mat.name) print materialswritten if mat.elasticity is None or mat.elasticity == 'linear': if mat.poisson_ratio is None and mat.shear_modulus is not None: mat.poisson_ratio = 0.5 * mat.young_modulus / mat.shear_modulus - 1.0 out += '*ELASTIC\n%s,%s\n' % (float(mat.young_modulus), float(mat.poisson_ratio)) elif mat.elasticity.lower() == 'hyperelastic': out += "*HYPERELASTIC, %s" % mat.type.upper() if mat.type.lower() == 'ogden': if mat.has_key('order'): order=mat.order else: order=len(mat.constants)/3. if len(order)%3!=0 : raise ValueError,"Wrong number of parameters" if mat.type.lower() == 'polynomial': ord=(-5. + (25.+8.*len(mat.constants))**0.5)/2. # Nparameters = ((N+1)*(N+2))/2 + N-1 --> Inverse to find order N if mat.has_key('order'): order=mat.order else: order=ord if int(ord)!=order: raise ValueError,"Wrong number of parameters" if mat.type.lower() == 'reduced polynomial': if mat.has_key('order'): order=mat.order else: order=len(mat.constants)/2. if len(mat.constants)%2!=0: raise ValueError,"Wrong number of parameters" out += ", N=%i\n" %order out += fmtData(mat.constants) elif mat.elasticity.lower() == 'anisotropic hyperelastic': out += "*ANISOTROPIC HYPERELASTIC, HOLZAPFEL\n" #TO DO: add possibility to define local orientations!!!" elif mat.elasticity.lower() == 'user': if mat.depvar is not None: out += "*DEPVAR\n%s\n" % mat.depvar out += "*USER MATERIAL, CONSTANTS=%s\n" % len(mat.constants) out += fmtData(mat.constants) if mat.density is not None: out += "*DENSITY\n%s\n" % float(mat.density) if mat.plastic is not None: out += "*PLASTIC\n" mat.plastic = asarray(mat.plastic) if mat.plastic.ndim != 2: raise ValueError,"Plastic data should be 2-dim array" ## if mat.plastic.shape[1] > 8: ## raise ValueError,"Plastic data array should have max. 8 columns" out += fmtData(mat.plastic) if mat.damping is not None: if mat.damping == 'yes': out += "*DAMPING" if mat.alpha is not None: out +=", ALPHA = %s" %mat.alpha if mat.beta is not None: out +=", BETA = %s" %mat.beta out += '\n' return out def fmtTransform(setname,csys): """Write transform command for the given set. - `setname` is the name of a node set - `csys` is a CoordSystem. """ out = "*TRANSFORM, NSET=%s, TYPE=%s\n" % (setname,csys.sys) out += fmtData(csys.data.reshape(-1)) return out def fmtFrameSection(el,setname): """Write a frame section for the named element set. Recognized data fields in the property record: - sectiontype GENERAL: - cross_section - moment_inertia_11 - moment_inertia_12 - moment_inertia_22 - torsional_constant - sectiontype CIRC: - radius - sectiontype RECT: - width - height - all sectiontypes: - young_modulus - shear_modulus - optional: - density: density of the material - yield_stress: yield stress of the material - orientation: a vector specifying the direction cosines of the 1 axis """ out = "" extra = '' if el.density: extra += ', DENSITY=%s' % float(el.density) if el.yield_stress: extra += ', PLASTIC DEFAULTS, YIELD STRESS=%s' % float(el.yield_stress) if el.shear_modulus is None and el.poisson_ratio is not None: el.shear_modulus = el.young_modulus / 2. / (1.+float(el.poisson_ratio)) sectiontype = el.sectiontype.upper() out += "*FRAME SECTION, ELSET=%s, SECTION=%s%s\n" % (setname,sectiontype,extra) if sectiontype == 'GENERAL': out += "%s, %s, %s, %s, %s \n" % (float(el.cross_section),float(el.moment_inertia_11),float(el.moment_inertia_12),float(el.moment_inertia_22),float(el.torsional_constant)) elif sectiontype == 'CIRC': out += "%s \n" % float(el.radius) elif sectiontype == 'RECT': out += "%s, %s\n" % (float(el.width),float(el.height)) if el.orientation != None: out += fmtData(el.orientation) else: out += '\n' out += fmtData([float(el.young_modulus),float(el.shear_modulus)]) return out def fmtGeneralBeamSection(el,setname): """Write a general beam section for the named element set. To specify a beam section when numerical integration over the section is not required. Recognized data fields in the property record: - sectiontype GENERAL: - cross_section - moment_inertia_11 - moment_inertia_12 - moment_inertia_22 - torsional_constant - sectiontype CIRC: - radius - sectiontype RECT: - width, height - all sectiontypes: - young_modulus - shear_modulus or poisson_ration - optional: - density: density of the material (required in Abaqus/Explicit) """ out = "" extra = '' if el.density: extra += ', DENSITY=%s' % float(el.density) if el.shear_modulus is None and el.poisson_ratio is not None: el.shear_modulus = el.young_modulus / 2. / (1.+float(el.poisson_ratio)) sectiontype = el.sectiontype.upper() out += "*BEAM GENERAL SECTION, ELSET=%s, SECTION=%s%s\n" % (setname,sectiontype,extra) if sectiontype == 'GENERAL': out += "%s, %s, %s, %s, %s \n" % (float(el.cross_section),float(el.moment_inertia_11),float(el.moment_inertia_12),float(el.moment_inertia_22),float(el.torsional_constant)) elif sectiontype == 'CIRC': out += "%s \n" % float(el.radius) elif sectiontype == 'RECT': out += "%s, %s\n" % (float(el.width),float(el.height)) if el.orientation != None: out += "%s,%s,%s\n" % tuple(el.orientation) else: out += '\n' out += "%s, %s \n" % (float(el.young_modulus),float(el.shear_modulus)) return out def fmtBeamSection(el,setname): """Write a beam section for the named element set. To specify a beam section when numerical integration over the section is required. Recognized data fields in the property record: - all sectiontypes: material - sectiontype GENERAL: - cross_section - moment_inertia_11 - moment_inertia_12 - moment_inertia_22 - torsional_constant - sectiontype CIRC: - radius - intpoints1 (number of integration points in the first direction) optional - intpoints2 (number of integration points in the second direction) optional - sectiontype RECT: - width, height - intpoints1 (number of integration points in the first direction) optional - intpoints2 (number of integration points in the second direction) optional """ out = "" sectiontype = el.sectiontype.upper() out += "*BEAM SECTION, ELSET=%s, MATERIAL=%s, SECTION=%s\n" % (setname,el.material.name,sectiontype) if sectiontype == 'GENERAL': out += "%s, %s, %s, %s, %s \n" % (float(el.cross_section),float(el.moment_inertia_11),float(el.moment_inertia_12),float(el.moment_inertia_22),float(el.torsional_constant)) elif sectiontype == 'CIRC': out += "%s \n" % float(el.radius) elif sectiontype == 'RECT': out += "%s, %s\n" % (float(el.width),float(el.height)) if el.orientation != None: out += "%s,%s,%s\n" % tuple(el.orientation) else: out += '\n' if el.intpoints1 != None: out += "%s" % el.intpoints1 if el.intpoints2 != None: out += ", %s" % el.intpoints2 out += "\n" return out def fmtConnectorSection(el,setname): """Write a connector section. Optional data: - `behavior` : connector behavior name - `orient` : connector orientation """ out = "" if el.sectiontype.upper() != 'GENERAL': out += '*CONNECTOR SECTION, ELSET=%s' % setname if el.behavior: out += ', BEHAVIOR=%s' % el.behavior out += '\n%s\n' % el.sectiontype.upper() if el.orient: out += '%s\n' % el.orient return out def fmtConnectorBehavior(prop): """ Write a connector behavior. Implemented: Elasticity, Stop Examples: Elasticity P.Prop(name='connbehavior1',ConnectorBehavior='',Elasticity=dict(component=[1,2,3,4,5,6],value=[1,1,1,1,1,1])) Stop: P.Prop(name='connbehavior3',ConnectorBehavior='',Stop=dict(component=[1,2,3,4,5,6],lowerlimit=[1,1,1,1,1,1], upperlimit=[2, 2, 2, 2,2,2])) """ out = '' for p in prop: out += '*CONNECTOR BEHAVIOR, NAME=%s\n' % p.name if p.Elasticity: for j in range(len(p.Elasticity['component'])): out += '*CONNECTOR ELASTICITY, COMPONENT=%s\n' % p.Elasticity['component'][j] out += '%s ,\n' % p.Elasticity['value'][j] if p.Stop: for j in range(len(p.Stop['component'])): out += '*CONNECTOR STOP, COMPONENT=%s\n' % p.Stop['component'][j] out += '%s , %s\n'% (p.Stop['lowerlimit'][j], p.Stop['upperlimit'][j]) return out #~ FI need more documentation and examples # this sholud be extended to all the element types that has the property SOLID SECTION. now it is only for 3dsolid def fmtSolidSection(el,setname): """_ THis docstring is very badly structured! REQUIRED - material/composite (mutually exclusive) - elset - refnode (REQUIRED only for plane stress elements and acoustic infinite elements) - orientation (REQUIRED only for anisotropic materials, OPTIONAL otherwise) el.seccontrol is a Dict REQUIRED el.seccontrol.name = name of the section control OPTIONAL el.seccontrol.data = list or array of optional line for hourglass control and bulk viscosity All other keys have name equal to the ABAQUS keywords and value equal to parameter setting if an ABAQUS keyword does not have a value to be the Dict value must be an empty string el.material see fmtMaterial documentation EXAMPLE control=Dict({'name':'StentControl','hourglass':'enhanced',}) P.elemProp(set='STENT',eltype='C3D8R',section=ElemSection(section=stentSec,material=steel,seccontrol=control)) """ out = '' if el.sectiontype.upper() == '3DSOLID': if mat is not None: out += "*SOLID SECTION, ELSET=%s, MATERIAL=%s" % (setname,el.material.name) if el.seccontrol is not None: out += ", CONTROLS=%s" %el.seccontrol.name if el.orientation is not None: out += ", ORIENTATION=%s" % (el.orientation.name) out += '\n' if el.thickness is not None: out += '%s\n'%float(el.thickness) if el.seccontrol is not None: if el.seccontrol.name is not None: # add the only required parameter out += "*SECTION CONTROLS, NAME=%s" %el.seccontrol.name out += fmtOptions(removeDict(el.seccontrol,['name','data'])) out += '\n' # add data line for if el.seccontrol.data is not None: out += fmtData(el.seccontrol.data) else: raise ValueError,"A section control name need to be specified" return out def fmtShellSection(el,setname,matname): out = '' if el.sectiontype.upper() == 'SHELL': if matname is not None: out += """*SHELL SECTION, ELSET=%s, MATERIAL=%s %s \n""" % (setname,matname,float(el.thickness)) return out def fmtSurface(prop): """Format the surface definitions. Required: - set: the elements/nodes in the surface, either numbers or a set name. - name: the surface name - surftype: 'ELEMENT' or 'NODE' - label: face or edge identifier (only required for surftype = 'ELEMENT') This label can be a string, or a list of strings. This allows to use different identifiers for the different elements in the surface. Thus:: Prop(name='mysurf',set=[0,1,2,6],surftype='element',label=['S1','S2','S1','S3') will get exported to Abaqus as:: *SURFACE, NAME=mysurf, TYPE=element 1, S1 2, S2, 1, S1 7, S3 """ out = '' for p in prop: out += "*SURFACE, NAME=%s, TYPE=%s\n" % (p.name,p.surftype) for i,e in enumerate(p.set): if e.dtype.kind != 'S': e += 1 if p.label is None: out += "%s\n" % e elif isinstance(p.label, str): out += "%s, %s\n" % (e,p.label) else: out += "%s, %s\n" % (e,p.label[i]) return out def fmtAnalyticalSurface(prop): """Format the analytical surface rigid body. Required: - nodeset: refnode. - name: the surface name - surftype: 'ELEMENT' or 'NODE' - label: face or edge identifier (only required for surftype = 'NODE') Example: >>> P.Prop(name='AnalySurf', nodeset = 'REFNOD', analyticalsurface='') """ out = '' for p in prop: out += "*RIGID BODY, ANALYTICAL SURFACE = %s, REFNOD=%s\n" % (p.name,p.nodeset) return out def fmtSurfaceInteraction(prop): """Format the interactions. Required: -name Optional: - cross_section (for node based interaction) - friction : friction coeff or 'rough' - surface behavior: no separation - surface behavior: pressureoverclosure """ out = '' for p in prop: out += "*Surface Interaction, name=%s\n" % (p.name) if p.cross_section is not None: out += "%s\n" % p.cross_section if p.friction is not None: if p.friction == 'rough': out += "*FRICTION, ROUGH\n" else: out += "*FRICTION\n%s\n" % float(p.friction) if p.surfacebehavior: out += "*Surface Behavior" print "writing Surface Behavior" if p.noseparation == True: out += ", no separation" if p.pressureoverclosure: if p.pressureoverclosure[0] == 'soft': out += ", pressure-overclosure=%s\n" % p.pressureoverclosure[1] out += "%s" % fmtData(p.pressureoverclosure[2:]) elif p.pressureoverclosure[0] == 'hard': out += ", penalty=%s\n" % p.pressureoverclosure[1] out += "%s" % fmtData(p.pressureoverclosure[2:]) else: out += "\n" return out def fmtGeneralContact(prop): """Format the general contact. Only implemented on model level Required: - interaction: interaction properties : name or Dict Optional: - Exclusions (exl) Example: >>> P.Prop(generalinteraction=Interaction(name ='contactprop1'),exl =[['surf11', 'surf12'],['surf21',surf22]]) """ out = '' for p in prop: if type(p.generalinteraction) is str: intername = p.generalinteraction else: intername = p.generalinteraction.name out += fmtSurfaceInteraction([p.generalinteraction]) out += "*Contact\n" out += "*Contact Inclusions, ALL EXTERIOR\n" if p.exl: out += "*Contact Exclusions\n" for ex in p.exl: out += "%s, %s\n" % (ex[0], ex[1]) out += "*Contact property assignment\n" out += ", , %s\n" % intername return out def fmtContactPair(prop): """Format the contact pair. Required: - master: master surface - slave: slave surface - interaction: interaction properties : name or Dict Example: >>> P.Prop(name='contact0',interaction=Interaction(name ='contactprop', surfacebehavior=True, pressureoverclosure=['hard','linear',0.0, 0.0, 0.001]), master ='quadtubeINTSURF1', slave='hexstentEXTSURF', contacttype='NODE TO SURFACE') """ out = '' for p in prop: if type(p.interaction) is str: intername = p.interaction else: intername = p.interaction.name out += fmtSurfaceInteraction([p.interaction]) out += "*Contact Pair, interaction=%s" % intername if p.contacttype is not None: out += ", type=%s\n" % p.contacttype else: out+="\n" out += "%s, %s\n" % (p.slave,p.master) return out def fmtConstraint(prop): """Format Tie constraint Required: -name -adjust (yes or no) -slave -master Optional: -type (surf2surf, node2surf) -no rotation Example: >>> P.Prop(constraint='1', name = 'constr1', adjust = 'no', master = 'hexstentbarSURF', slave = 'hexstentEXTSURF',type='NODE TO SURFACE') """ for p in prop: out ='' out +="*Tie, name=%s, adjust=%s" % (p.name, p.adjust) if p.type is not None: out+=",type = %s" % p.type if p.norotation == True: out+=", NO ROTATION" out +="\n" out +="%s, %s\n" % (p.slave, p.master) return out def fmtInitialConditions(prop): """Format initial conditions Required: -type -nodes -data Example:: P.Prop(initialcondition='', nodes ='Nall', type = 'TEMPERATURE', data = 37.) """ for p in prop: out ="*Initial Conditions, type = %s\n" % p.type out +="%s,%.2f\n" % (p.nodes, p.data) return out def fmtOrientation(prop): """Format the orientation. Optional: - definition - system: coordinate system - a: a first point - b: a second point """ out = '' for p in prop: out += "*ORIENTATION, NAME=%s" % (p.name) if p.definition is not None: out += ", definition=%s" % p.definition if p.system is not None: out += ", SYSTEM=%s" % p.system out += "\n" if p.a is not None: data = tuple(p.a) if p.b is not None: data += tuple(p.b) out += fmtData(data) else: raise ValueError,"Orientation needs at least point a" return out def fmtEquation(prop): """Format multi-point constraint using an equation Required: - equation Equation should be a list, which contains the different terms of the equation. Each term is again a list with three values: - First value: node number - Second value: degree of freedom - Third value: coefficient Example:: P.nodeProp(equation=[[209,1,1],[32,1,-1]]) This forces the displacement in Y-direction of nodes 209 and 32 to be equal. """ out = '' nofs = 1 for p in prop: out += "*EQUATION\n" out += "%s\n" % asarray(p.equation).shape[0] for i in p.equation: dof = i[1]+1 out += "%s, %s, %s\n" % (i[0]+nofs,dof,i[2]) return out ## The following output sections with possibly large data ## are written directly to file. ########################################################## def writeNodes(fil,nodes,name='Nall',nofs=1): """Write nodal coordinates. The nodes are added to the named node set. If a name different from 'Nall' is specified, the nodes will also be added to a set named 'Nall'. The nofs specifies an offset for the node numbers. The default is 1, because Abaqus numbering starts at 1. """ fil.write('*NODE, NSET=%s\n' % name) for i,n in enumerate(nodes): fil.write("%d, %14.6e, %14.6e, %14.6e\n" % ((i+nofs,)+tuple(n))) if name != 'Nall': fil.write('*NSET, NSET=Nall\n%s\n' % name) def writeElems(fil,elems,type,name='Eall',eid=None,eofs=1,nofs=1): """Write element group of given type. elems is the list with the element node numbers. The elements are added to the named element set. If a name different from 'Eall' is specified, the elements will also be added to a set named 'Eall'. The eofs and nofs specify offsets for element and node numbers. The default is 1, because Abaqus numbering starts at 1. If eid is specified, it contains the element numbers increased with eofs. """ fil.write('*ELEMENT, TYPE=%s, ELSET=%s\n' % (type.upper(),name)) nn = elems.shape[1] fmt = '%d' + nn*', %d' + '\n' if eid is None: eid = arange(elems.shape[0]) else: eid = asarray(eid) for i,e in zip(eid+eofs,elems+nofs): fil.write(fmt % ((i,)+tuple(e))) writeSet(fil,'ELSET','Eall',[name]) def writeSet(fil,type,name,set,ofs=1): """Write a named set of nodes or elements (type=NSET|ELSET) `set` : an ndarray. `set` can be a list of node/element numbers, in which case the `ofs` value will be added to them, or a list of names the name of another already defined set. """ fil.write("*%s,%s=%s\n" % (type,type,name)) set = asarray(set) if set.dtype.kind == 'S': # we have set names for i in set: fil.write('%s\n' % i) else: for i in set+ofs: fil.write("%d,\n" % i) connector_elems = ['CONN3D2','CONN2D2'] frame_elems = ['FRAME3D','FRAME2D'] truss_elems = [ 'T2D2','T2D2H','T2D3','T2D3H', 'T3D2','T3D2H','T3D3','T3D3H'] beam_elems = [ 'B21', 'B21H','B22','B22H','B23','B23H', 'B31', 'B31H','B32','B32H','B33','B33H'] membrane_elems = [ 'M3D3', 'M3D4','M3D4R', 'M3D6','M3D8', 'M3D8R', 'M3D9','M3D9R'] plane_stress_elems = [ 'CPS3', 'CPS4','CPS4I','CPS4R', 'CPS6','CPS6M', 'CPS8','CPS8M'] plane_strain_elems = [ 'CPE3','CPE3H', 'CPE4','CPE4H','CPE4I','CPE4IH','CPE4R','CPE4RH', 'CPE6','CPE6H','CPE6M','CPE6MH', 'CPE8','CPE8H','CPE8R','CPE8RH'] generalized_plane_strain_elems = [ 'CPEG3','CPEG3H', 'CPEG4','CPEG4H','CPEG4I','CPEG4IH','CPEG4R','CPEG4RH', 'CPEG6','CPEG6H','CPEG6M','CPEG6MH', 'CPEG8','CPEG8H','CPEG8R','CPEG8RH'] solid2d_elems = plane_stress_elems + \ plane_strain_elems + \ generalized_plane_strain_elems shell_elems = [ 'S3','S3R', 'S3RS', 'S4','S4R', 'S4RS','S4RSW','S4R5', 'S8R','S8R5', 'S9R5', 'STRI3', 'STRI65'] surface_elems = [ 'SFM3D3', 'SFM3D4','SFM3D4R', 'SFM3D6', 'SFM3D8','SFM3D8R'] solid3d_elems = [ 'C3D4','C3D4H', 'C3D6','C3D6H', 'C3D8','C3D8H','C3D8R','C3D8RH','C3D10', 'C3D10H','C3D10M','C3D10MH', 'C3D15','C3D15H', 'C3D20','C3D20H','C3D20R','C3D20RH',] def writeSection(fil,prop): """Write an element section. prop is a an element property record with a section and eltype attribute """ out = "" setname = esetName(prop) el = prop.section eltype = prop.eltype.upper() mat = el.material if mat is not None: fil.write(fmtMaterial(mat)) if eltype in connector_elems: fil.write(fmtConnectorSection(el,setname)) elif eltype in frame_elems: fil.write(fmtFrameSection(el,setname)) elif eltype in truss_elems: if el.sectiontype.upper() == 'GENERAL': fil.write("""*SOLID SECTION, ELSET=%s, MATERIAL=%s %s """ %(setname,el.material.name, float(el.cross_section))) elif el.sectiontype.upper() == 'CIRC': fil.write("""*SOLID SECTION, ELSET=%s, MATERIAL=%s %s """ %(setname,el.material.name, float(el.radius)**2*pi)) ############ ##BEAM elements ########################## elif eltype in beam_elems: if el.integrate: fil.write(fmtBeamSection(el,setname)) else: fil.write(fmtGeneralBeamSection(el,setname)) ############ ## SHELL elements ########################## elif eltype in shell_elems: fil.write(fmtShellSection(el,setname,mat.name)) ############ ## SURFACE elements ########################## elif eltype in surface_elems: if el.sectiontype.upper() == 'SURFACE': if el.density: fil.write("""*SURFACE SECTION, ELSET=%s, DENSITY=%f \n""" % (setname, el.density)) else: fil.write("""*SURFACE SECTION, ELSET=%s \n""" % setname) ############ ## MEMBRANE elements ########################## elif eltype in membrane_elems: if el.sectiontype.upper() == 'MEMBRANE': if mat is not None: fil.write("""*MEMBRANE SECTION, ELSET=%s, MATERIAL=%s %s \n""" % (setname,mat.name,float(el.thickness))) ############ ## 3DSOLID elements ########################## #Section controls: add enhanced hourglassing #first elementset: set control=' ' #other elementsets: set control=True (or something else) elif eltype in solid3d_elems: if el.sectiontype.upper() == '3DSOLID': fil.write(fmtSolidSection(el,setname)) ############ ## 2D SOLID elements ########################## elif eltype in solid2d_elems: if el.sectiontype.upper() == 'SOLID': if mat is not None: fil.write("""*SOLID SECTION, ELSET=%s, MATERIAL=%s %s \n""" % (setname,mat.name,float(el.thickness))) ############ ## RIGID elements ########################## elif eltype in ['R2D2','RB2D2','RB3D2','RAX2','R3D3','R3D4']: if el.sectiontype.upper() == 'RIGID': fil.write("*RIGID BODY,REFNODE=%s,density=%s, ELSET=%s\n" % (el.nodeset,el.density,setname)) ############ ## UNSUPPORTED elements ########################## else: pf.warning('Sorry, elementtype %s is not yet supported' % eltype) #~ FI writeDisplacements has been included in writeBoundaries # the previous one didnt allow to add option like 'USER' for disp subroutine # in this way the function is more general as it allows also other kind of boundary conditions # i. e type= velocity or type = acceleration to be added in the extra Dict #~ the op option has been removed it needs to be included in extra. # the previous default parameter (OP= MOD) is also deflaut to abaqus and # does not need to be specified #~ the key ampl can be also icluded in extra but has not been removed # I will suggest to remove writeDisplacements or set this function equal to writeBoundaries def writeBoundaries(fil,prop): """_ BAD STRUCTURE! Write nodal boundary conditions. prop is a list of node property records that should be scanned for bound attributes to write. prop contains REQUIRED -bound : a string (for prescribed conditions) : a list of 6 integer (of values 0 or 1). where 1 the boundary is written : a list of tuple ( ) OPTIONAL -ampl : an amplitude name -extra : Dict type.It has keys name equal to the ABAQUS keywords and value equal to parameter setting if an ABAQUS keyword does not have a value to be the Dict value must be an empty string By default, the boundary conditions are applied as a modification of the existing boundary conditions, i.e. initial conditions and conditions from previous steps remain in effect. EXAMPLES P.nodeProp(tag='init',set=arange(100),name='catheter',bound='pinned') P.nodeProp(tag='init',set=arange(100),name='catheter',bound=[0,1,1,0,0,0]) !!!!This works like writeDisplacements ampname='amp' times = [0,1];values = [0,1] amp = Amplitude(data=column_stack([times,values])) P.Prop(amplitude=amp,name=ampname) P.nodeProp(tag='step1',set='catheter',bound=[(0,5.3),(1,3.5)],ampl=ampname) extra= Dict({'user':''}) P.nodeProp(tag='step1',set='catheter',bound=[(0,5.4),(1,3.5)],extra=extra) """ for p in prop: setname = nsetName(p) fil.write("*BOUNDARY") if p.ampl is not None: fil.write(", AMPLITUDE=%s" % p.ampl) if p.op is not None: fil.write(", OP=%s" % p.op) if p.extra is not None: fil.write(fmtOptions(p.extra)) fil.write("\n") if isinstance(p.bound,str): fil.write("%s, %s\n" % (setname,p.bound)) elif isinstance(p.bound[0],int): for b in range(6): if p.bound[b]==1: fil.write("%s, %s\n" % (setname,b+1)) elif isinstance(p.bound[0],tuple): for b in p.bound: dof = b[0]+1 fil.write(fmtData(setname,dof,dof,b[1])) #~ FI see writeBoundaries comments def writeDisplacements(fil,prop,dtype='DISPLACEMENT'): """Write boundary conditions of type BOUNDARY, TYPE=DISPLACEMENT prop is a list of node property records that should be scanned for displ attributes to write. By default, the boundary conditions are applied as a modification of the existing boundary conditions, i.e. initial conditions and conditions from previous steps remain in effect. The user can set op='NEW' to remove the previous conditions. This will also remove initial conditions! """ for p in prop: setname = nsetName(p) fil.write("*BOUNDARY, TYPE=%s" % dtype) if p.op is not None: fil.write(", OP=%s" % p.op) if p.ampl is not None: fil.write(", AMPLITUDE=%s" % p.ampl) fil.write("\n") for v in p.displ: dof = v[0]+1 fil.write("%s, %s, %s, %s\n" % (setname,dof,dof,v[1])) def writeCloads(fil,prop,op='NEW'): """Write cloads. prop is a list of node property records that should be scanned for displ attributes to write. By default, the loads are applied as new values in the current step. The user can set op='MOD' to add the loads to already existing ones. """ for p in prop: setname = nsetName(p) fil.write("*CLOAD, OP=%s" % op) if p.ampl is not None: fil.write(", AMPLITUDE=%s" % p.ampl) fil.write("\n") for v in p.cload: dof = v[0]+1 fil.write("%s, %s, %s\n" % (setname,dof,v[1])) def writeDloads(fil,prop,op='NEW'): """Write Dloads. prop is a list property records having an attribute dload By default, the loads are applied as new values in the current step. The user can set op='MOD' to add the loads to already existing ones. """ for p in prop: setname = esetname(p) fil.write("*DLOAD, OP=%s" % op) if p.ampl is not None: fil.write(", AMPLITUDE=%s" % p.ampl) fil.write("\n") if p.dload.label == 'GRAV': fil.write("%s, GRAV, 9.81, 0, 0 ,-1\n" % setname) else: fil.write("%s, %s, %s\n" % (setname,p.dload.label,p.dload.value)) def writeDsloads(fil,prop,op='NEW'): """Write Dsloads. prop is a list property records having an attribute dsload By default, the loads are applied as new values in the current step. The user can set op='MOD' to add the loads to already existing ones. """ for p in prop: fil.write("*DSLOAD, OP=%s" % op) if p.ampl is not None: fil.write(", AMPLITUDE=%s" % p.ampl) fil.write("\n") fil.write("%s, %s, %s\n" % (p.dsload.surface,p.dsload.label,p.dsload.value)) ####################################################### # General model data # def writeAmplitude(fil,prop): for p in prop: fil.write("*AMPLITUDE, NAME=%s, DEFINITION=%s\n" % (p.name,p.amplitude.type)) for i,v in enumerate(p.amplitude.data): fil.write("%s, %s," % tuple(v)) if i % 4 == 3: fil.write("\n") if i % 4 != 3: fil.write("\n") ### Output requests ################################### # # Output: goes to the .odb file (for postprocessing with Abaqus/CAE) # Result: goes to the .fil file (for postprocessing with other means) ####################################################### def writeNodeOutput(fil,kind,keys,set='Nall'): """ Write a request for nodal result output to the .odb file. - `keys`: a list of NODE output identifiers - `set`: a single item or a list of items, where each item is either a property number or a node set name for which the results should be written """ output = 'OUTPUT' if type(set) == str or type(set) == int: set = [ set ] for i in set: if type(i) == int: setname = Nset(str(i)) else: setname = i s = "*NODE %s, NSET=%s" % (output,setname) fil.write("%s\n" % s) for key in keys: fil.write("%s\n" % key) def writeNodeResult(fil,kind,keys,set='Nall',output='FILE',freq=1, globalaxes=False,lastmode=None, summary=False,total=False): """ Write a request for nodal result output to the .fil or .dat file. - `keys`: a list of NODE output identifiers - `set`: a single item or a list of items, where each item is either a property number or a node set name for which the results should be written - `output` is either ``FILE`` (for .fil output) or ``PRINT`` (for .dat output)(Abaqus/Standard only) - `freq` is the output frequency in increments (0 = no output) Extra arguments: - `globalaxes`: If 'YES', the requested output is returned in the global axes. Default is to use the local axes wherever defined. Extra arguments for output=``PRINT``: - `summary`: if True, a summary with minimum and maximum is written - `total`: if True, sums the values for each key Remark: the `kind` argument is not used, but is included so that we can easily call it with a `Results` dict as arguments """ if type(set) == str or type(set) == int: set = [ set ] for i in set: if type(i) == int: setname = Nset(str(i)) else: setname = i s = "*NODE %s, NSET=%s" % (output,setname) if freq != 1: s += ", FREQUENCY=%s" % freq if globalaxes: s += ", GLOBAL=YES" if lastmode is not None: s += ", LAST MODE=%s" % lastmode if output=='PRINT': if summary: s += ", SUMMARY=YES" if total: s += ", TOTAL=YES" fil.write("%s\n" % s) for key in keys: fil.write("%s\n" % key) def writeElemOutput(fil,kind,keys,set='Eall'): """ Write a request for element output to the .odb file. - `keys`: a list of ELEMENT output identifiers - `set`: a single item or a list of items, where each item is either a property number or an element set name for which the results should be written """ output = 'OUTPUT' if type(set) == str or type(set) == int: set = [ set ] for i in set: if type(i) == int: setname = Eset(str(i)) else: setname = i s = "*ELEMENT %s, ELSET=%s" % (output,setname) fil.write("%s\n" % s) for key in keys: fil.write("%s\n" % key) def writeElemResult(fil,kind,keys,set='Eall',output='FILE',freq=1, pos=None, summary=False,total=False): """ Write a request for element result output to the .fil or .dat file. - `keys`: a list of ELEMENT output identifiers - `set`: a single item or a list of items, where each item is either a property number or an element set name for which the results should be written - `output` is either ``FILE`` (for .fil output) or ``PRINT`` (for .dat output)(Abaqus/Standard only) - `freq` is the output frequency in increments (0 = no output) Extra arguments: - `pos`: Position of the points in the elements at which the results are written. Should be one of: - 'INTEGRATION POINTS' (default) - 'CENTROIDAL' - 'NODES' - 'AVERAGED AT NODES' Non-default values are only available for ABAQUS/Standard. Extra arguments for output='PRINT': - `summary`: if True, a summary with minimum and maximum is written - `total`: if True, sums the values for each key Remark: the ``kind`` argument is not used, but is included so that we can easily call it with a Results dict as arguments """ if type(set) == str or type(set) == int: set = [ set ] for i in set: if type(i) == int: setname = Eset(str(i)) else: setname = i s = "*EL %s, ELSET=%s" % (output,setname) if freq != 1: s += ", FREQUENCY=%s" % freq if pos: s += ", POSITION=%s" % pos if output=='PRINT': if summary: s += ", SUMMARY=YES" if total: s += ", TOTAL=YES" fil.write("%s\n" % s) for key in keys: fil.write("%s\n" % key) def writeFileOutput(fil,resfreq=1,timemarks=False): """Write the FILE OUTPUT command for Abaqus/Explicit""" fil.write("*FILE OUTPUT, NUMBER INTERVAL=%s" % resfreq) if timemarks: fil.write(", TIME MARKS=YES") fil.write("\n") #~ Fi this function works like the previous one(if extra is a str) # but now extra can be also a list of Dict .This is more convenient if more addictional lines # need to be written at the step level for type history. # This function is very similar to writeStepExtra maybe can be merged def writeModelProps(fil,prop): """_ BAD STRUCTURE Write model props for this step extra : str : list of Dict. each Dict is a new line.Only 2 keys are dedicated REQUIRED -keyword = abaqus keyword name OPTIONAL -data = list or array of numerical data for any additional data line All other keys have name equal to the ABAQUS keywords and value equal to parameter setting if an ABAQUS keyword does not have a value to be the Dict value must be an empty string EXAMPLE P.Prop(tag='step1',extra='*CONTACT CONTROLS , AUTOMATIC TOLERANCES\n') P.Prop(tag='step1',extra=[Dict({'keyword':'CONTACT CONTROLS','AUTOMATIC TOLERANCES':''})]) """ for p in prop: if p.extra: if isinstance(p.extra,str): fil.write(p.extra) elif isinstance(p.extra,list): cmd='' for l in p.extra: l=CDict(l) # to avoid keyerrors if l.data is not a key cmd+='*%s'%l['keyword'] cmd+=fmtOptions(removeDict(l,['keyword','data'])) cmd+='\n' if l.data is not None: cmd+=fmtData(l.data) fil.write(cmd.upper()) #~ FI see comments for writeModelProps def writeStepExtra(fil,extra): if isinstance(extra,str): fil.write(extra) elif isinstance(extra,list): cmd='' for l in extra: l=CDict(l) # to avoid keyerrors if l.data is not a key cmd+='*%s'%l['keyword'] cmd+=fmtOptions(removeDict(l,['keyword','data'])) cmd+='\n' if l.data is not None: cmd+=fmtData(l.data) fil.write(cmd.upper()) ################################################## ## Some classes to store all the required information ################################################## #FI- SBD The Step Class has been changed. most of the keywords have been removed. # we kept the analysis values has they were before, but we added three new kewords # in which to store all the parameter (see Example).The default values for riks buckle have been removed # but we left infos in the documentation. #At the moment we didn t find any exception at least for what we used so far #but maybe the stepOption=Dict(),analysisOption=Dict(),extra=str added keywords # can be all tuple (Dict, list/array) for any additional line of values to be added. # lines # if res and self.analysis == 'EXPLICIT': # writeFileOutput(fil,resfreq,timemarks) # should be removed and change also the OUTPUT class (see comments) class Step(Dict): """_Badly structured docstring The basic logical unit in the simulation history. In Abaqus, a step is the smallest logical entity in the simulation history. It is typically a time step in a dynamic simulation, but it can also describe different loading states in a (quasi-)static simulation. Our Step class holds all the data describing the global step parameters. It combines the Abaqus 'STEP', 'STATIC', 'DYNAMIC' and 'BUCKLE' keyword commands (and even some more global parameter setting commands). Parameters: - `analysis`: the analysis type, one of: 'STATIC', 'DYNAMIC', 'EXPLICIT', 'PERTURBATION', 'BUCKLE', 'RIKS' - `time`: either - a single float value specifying the step time, - a list of 2 values (special cases with analysis=EXPLICIT) - a list of 4 values: time inc, step time, min. time inc, max. time inc (all the other cases) - for LANCZOS: a list of 5 values - for RIKS: a list of 8 values In most cases, only the step time should be specified. - `nlgeom`: 'YES' ot 'NO' (default) If 'YES', the analysis will be geometrically non-linear. For Analysis type 'RIKS' set `nlgeom` to 'YES', 'BUCKLE' set it to 'NO', 'PERTURBATION' ignores `nlgeom`. - `tags`: a list of property tags to include in this step. If specified, only the property records having one of the listed values as their `tag` attribute will be included in this step. - `out` and `res`: specific output/result records for this step. They come in addition to the global ones. - stepOption is a Dict of optional parameters to be added at a step level at the FIRST line. it is placed after the *STEP keyword i.e *STEP, NLGEOM=YES,INC=10000,UNSYMM = YES It has keys name equal to the ABAQUS keywords and value equal to parameter setting if an ABAQUS keyword does not have a value to be the Dict value must be an empty string (see example below) - subheading is a string printed as an additionanal subheading (not important normally) - analysisOption is a Dict of optional parameters to be added at a step level at the SECOND line. it is placed after the analysis keyword i.e *STATIC, STABILIZE=0.0002,CONTINUE=NO with keys name equal to the ABAQUS keywords and value equal to parameter setting if an ABAQUS keyword does not have a value to be the Dict value must be an empty string (see example below) -extra : str for any extra keyword to be added at a step level after the time line : list of Dict for any extra keyword to be added at a step level after the time line. Each Dict is a separate line (see example below) Only 2 keys are dedicated: REQUIRED -keyword = abaqus keyword name OPTIONAL -data = list or array of numerical data for any additional data line All the other keys are optional and must have name equal to the ABAQUS keywords and value equal to parameter setting if an ABAQUS keyword does not have a value to be the Dict value must be an empty string (see example below) Examples on stepOption , analysisOption, extra stepOption standard analysis, not needed for explicit Dict({'UNSYMM':'YES'/'NO','CONVERT SDI':'YES'/'NO','AMPLITUDE':'STEP'/'RAMP','INC':100}) analysisOption: static, riks Dict({'STABILIZE':0.0002,'CONTINUE':'NO'/'YES','ALLSDTOL':0.05,'DIRECT':'NO STOP','FACTOR':1.,'INCR':0.1 (for riks)}) dynamic (implicit) Dict({'APPLICATION':'QUASI-STATIC'/'MODERATE DISSIPATION'/'TRANSIENT FIDELITY'}) dynamic explicit Dict({'DIRECT USER CONTROL':'' / 'ELEMENT BY ELEMENT':'' / 'FIXED TIME INCREMENTATION':'',\ 'IMPROVED DT METHOD'='NO'/'YES','SCALE FACTOR':1.}) buckle Dict({'EIGENSOLVER':'SUBSPACE'}) extra: extra='*BULK VISCOSITY\n0.12, 0.06\n' extra=[Dict({'keyword':'BULK VISCOSITY','data':[0.12, 0.06]})] """ analysis_types = [ 'STATIC', 'DYNAMIC', 'EXPLICIT', \ 'PERTURBATION', 'BUCKLE', 'RIKS' ] def __init__(self,analysis='STATIC',time=[0.,0.,0.,0.],nlgeom='NO',subheading=None,\ tags=None,name=None,out=None,res=None,stepOptions=None,analysisOptions=None,extra=None): """Create a new analysis step.""" self.analysis = analysis.upper() self.name = name if not self.analysis in Step.analysis_types: raise ValueError,'analysis should be one of %s' % analysis_types if type(time) == float: time = [ 0., time, 0., 0. ] self.time = time self.nlgeom = nlgeom self.tags = tags self.out = out self.res = res self.stepOptions=stepOptions self.analysisOptions=analysisOptions self.subheading=subheading self.extra=extra def write(self,fil,propDB,out=[],res=[],resfreq=1,timemarks=False): """Write a load step. propDB is the properties database to use. Except for the step data itself, this will also write the passed output and result requests. out is a list of Output-instances. res is a list of Result-instances. resfreq and timemarks are global values only used by Explicit """ cmd = '*STEP' if self.name: cmd += ',NAME = %s' % self.name if self.analysis == 'PERTURBATION': cmd += ', PERTURBATION' cmd += ', NLGEOM=%s' % self.nlgeom if self.stepOptions is not None: cmd+=fmtOptions(self.stepOptions) cmd += '\n' fil.write(cmd) if self.subheading is not None: fil.write(self.subheading+'\n') if self.analysis =='STATIC': fil.write("*STATIC") elif self.analysis == 'EXPLICIT': fil.write("*DYNAMIC, EXPLICIT") elif self.analysis == 'DYNAMIC': fil.write("*DYNAMIC") elif self.analysis == 'BUCKLE': fil.write("*BUCKLE") elif self.analysis == 'PERTURBATION': fil.write("*STATIC") elif self.analysis == 'RIKS': fil.write("*STATIC, RIKS") cmd='' if self.analysisOptions is not None: cmd+=fmtOptions(self.analysisOptions) cmd+='\n' fil.write(cmd) #~ fil.write(("%s"+",%s"*(len(self.time)-1)+'\n') % tuple(self.time)) fil.write(fmtData(self.time)) if self.extra is not None: writeStepExtra(fil,self.extra) prop = propDB.getProp('n',tag=self.tags,attr=['bound']) if prop: pf.message(" Writing step boundary conditions") writeBoundaries(fil,prop) for pname,aname in [ ('displ','DISPLACEMENT'), ('veloc','VELOCITY'), ('accel','ACCELERATION') ]: prop = propDB.getProp('n',tag=self.tags,attr=[pname]) if prop: pf.message(" Writing step %s" % aname.lower()) writeDisplacements(fil,prop,dtype=aname) prop = propDB.getProp('n',tag=self.tags,attr=['cload']) if prop: pf.message(" Writing step cloads") writeCloads(fil,prop) prop = propDB.getProp('e',tag=self.tags,attr=['dload']) if prop: pf.message(" Writing step dloads") writeDloads(fil,prop) prop = propDB.getProp('',tag=self.tags,attr=['dsload']) if prop: pf.message(" Writing step dsloads") writeDsloads(fil,prop) prop = propDB.getProp('',tag=self.tags) if prop: pf.message(" Writing step model props") writeModelProps(fil,prop) if self.out: out += self.out for i in out: if i.kind is None: fil.write(i.fmt()) if i.kind == 'N': writeNodeOutput(fil,**i) elif i.kind == 'E': writeElemOutput(fil,**i) if self.res: res += self.res if res and self.analysis == 'EXPLICIT': writeFileOutput(fil,resfreq,timemarks) for i in res: if i.kind == 'N': writeNodeResult(fil,**i) elif i.kind == 'E': writeElemResult(fil,**i) fil.write("*END STEP\n") #FI-SDB Remove **options the OUTPUT class # should be used only extra but examples are needed class Output(Dict): """A request for output to .odb and history. Parameters: - `type`: 'FIELD' or 'HISTORY' - `kind`: None, 'NODE', or 'ELEMENT' (first character suffices) - `extra`: an extra string to be added to the command line. This allows to add Abaqus options not handled by this constructor. The string will be appended to the command line preceded by a comma. For kind=='': - `variable`: 'ALL', 'PRESELECT' or '' For kind=='NODE' or 'ELEMENT': - `keys`: a list of output identifiers (compatible with kind type) - `set`: a single item or a list of items, where each item is either a property number or a node/element set name for which the results should be written. If no set is specified, the default is 'Nall' for kind=='NODE' and 'Eall' for kind='ELEMENT' """ def __init__(self,kind=None,keys=None,set=None,type='FIELD',variable='PRESELECT',extra='',**options): """ Create a new output request.""" if 'history' in options: pf.warning("The `history` argument in an output request is deprecated.\nPlease use `type='history'` instead.") if 'numberinterval' in options: pf.warning("The `numberinterval` argument in an output request is deprecated.\nPlease use the `extra` argument instead.") if kind: kind = kind[0].upper() if set is None: set = "%sall" % kind Dict.__init__(self,{'kind':kind}) if kind is None: self.update({'type':type,'variable':variable,'extra':extra}) else: self.update({'keys':keys,'set':set}) def fmt(self): """Format an output request. Return a string with the formatted output command. """ out = ['*OUTPUT',self.type.upper()] if self.variable: out.append('VARIABLE=%s' % self.variable.upper()) if self.extra: out.append(self.extra) return ', '.join(out)+'\n' class Result(Dict): """A request for output of results on nodes or elements. Parameters: - `kind`: 'NODE' or 'ELEMENT' (first character suffices) - `keys`: a list of output identifiers (compatible with kind type) - `set`: a single item or a list of items, where each item is either a property number or a node/element set name for which the results should be written. If no set is specified, the default is 'Nall' for kind=='NODE' and 'Eall' for kind='ELEMENT' - `output` is either ``FILE`` (for .fil output) or ``PRINT`` (for .dat output)(Abaqus/Standard only) - `freq` is the output frequency in increments (0 = no output) Extra keyword arguments are available: see the `writeNodeResults` and `writeElemResults` methods for details. """ # The following values can be changed to set the output frequency # for Abaqus/Explicit nintervals = 1 timemarks = False def __init__(self,kind,keys,set=None,output='FILE',freq=1,time=False, **kargs): """Create new result request.""" kind = kind[0].upper() if set is None: set = "%sall" % kind Dict.__init__(self,{'keys':keys,'kind':kind,'set':set,'output':output, 'freq':freq}) self.update(dict(**kargs)) class Interaction(Dict): """A Dict for setting surface interactions pressureoverclosure is an array = ['hard'/'soft','linear'/'nonlinear'/'exponential'/'tabular'/.., value1,value2,value3,... ] Leave empty for default hard contact 'hard' will set penalty contact, either 'linear' or 'nonlinear' 'soft' will set soft pressure-overclosure, combine with 'linear'/'exponential'/'tabular'/'scale factor' for needed values on dataline: see abaqus keyword manual """ def __init__(self, name=None, cross_section=1, friction=0.0, surfacebehavior = None, noseparation = False, pressureoverclosure = None): self.name = name self.cross_section = cross_section self.friction =friction self.surfacebehavior = surfacebehavior self.noseparation = noseparation self.pressureoverclosure = pressureoverclosure ############################################################ AbqData class AbqData(object): """Contains all data required to write the Abaqus input file. - `model` : a :class:`Model` instance. - `prop` : the `Property` database. - `steps` : a list of `Step` instances. - `res` : a list of `Result` instances. - `out` : a list of `Output` instances. - `bound` : a tag or alist of the initial boundary conditions. The default is to apply ALL boundary conditions initially. Specify a (possibly non-existing) tag to override the default. """ def __init__(self,model,prop,nprop=None,eprop=None,steps=[],res=[],out=[],bound=None): """Create new AbqData.""" if not isinstance(model,Model) or not isinstance(prop,PropertyDB): raise ValueError,"Invalid arguments: expected Model and PropertyDB, got %s and %s" % (type(model),type(prop)) self.model = model self.prop = prop self.nprop = nprop self.eprop = eprop self.bound = bound self.steps = steps self.res = res self.out = out def write(self,jobname=None,group_by_eset=True,group_by_group=False,header='',create_part=False): """Write an Abaqus input file. - `jobname` : the name of the inputfile, with or without '.inp' extension. If None is specified, the output is written to sys.stdout An extra header text may be specified. - `create_part` : if True, the model will be created as an Abaqus Part, followed by and assembly of that part. """ global materialswritten materialswritten = [] # Create the Abaqus input file if jobname is None: jobname,filename = 'Test',None fil = sys.stdout else: jobname,filename = abqInputNames(jobname) fil = open(filename,'w') pf.message("Writing to file %s" % (filename)) fil.write(fmtHeading("""Model: %s Date: %s Created by pyFormex Script: %s %s """ % (jobname, datetime.now(), pf.scriptName, header))) if create_part: fil.write("*PART, name=Part-0\n") nnod = self.model.nnodes() pf.message("Writing %s nodes" % nnod) writeNodes(fil,self.model.coords) pf.message("Writing node sets") for p in self.prop.getProp('n',attr=['set']): if p.set is not None: # set is directly specified set = p.set elif p.prop is not None: # set is specified by nprop nrs if self.nprop is None: print(p) raise ValueError,"nodeProp has a 'prop' field but no 'nprop'was specified" set = where(self.nprop == p.prop)[0] else: # default is all nodes set = range(self.model.nnodes()) setname = nsetName(p) writeSet(fil,'NSET',setname,set) pf.message("Writing coordinate transforms") for p in self.prop.getProp('n',attr=['csys']): fil.write(fmtTransform(p.name,p.csys)) pf.message("Writing element sets") telems = self.model.celems[-1] nelems = 0 for p in self.prop.getProp('e'): if p.set is not None: # element set is directly specified set = p.set elif p.prop is not None: # element set is specified by eprop nrs if self.eprop is None: print(p) raise ValueError,"elemProp has a 'prop' field but no 'eprop'was specified" set = where(self.eprop == p.prop)[0] else: # default is all elements set = range(telems) if p.has_key('eltype'): print('Elements of type %s: %s' % (p.eltype,set)) setname = esetName(p) gl,gr = self.model.splitElems(set) elems = self.model.getElems(gr) for i,elnrs,els in zip(range(len(gl)),gl,elems): grpname = Eset('grp',i) subsetname = Eset(p.nr,'grp',i,) nels = len(els) if nels > 0: pf.message("Writing %s elements from group %s" % (nels,i)) writeElems(fil,els,p.eltype,name=subsetname,eid=elnrs) nelems += nels if group_by_eset: writeSet(fil,'ELSET',setname,[subsetname]) if group_by_group: writeSet(fil,'ELSET',grpname,[subsetname]) else: writeSet(fil,'ELSET',p.name,p.set) pf.message("Total number of elements: %s" % telems) if nelems != telems: pf.message("!! Number of elements written: %s !!" % nelems) ## # Now process the sets without eltype ## for p in self.prop.getProp('e',noattr=['eltype']): ## setname = esetName(p) ## writeSet(fil,'ELSET',setname,p.set) pf.message("Writing element sections") for p in self.prop.getProp('e',attr=['section','eltype']): writeSection(fil,p) if create_part: fil.write("*END PART\n") fil.write("*ASSEMBLY, name=Assembly\n") fil.write("*INSTANCE, name=Part-0-0, part=Part-0\n") fil.write("*END INSTANCE\n") fil.write("*END ASSEMBLY\n") pf.message("Writing global model properties") prop = self.prop.getProp('',attr=['amplitude']) if prop: pf.message("Writing amplitudes") writeAmplitude(fil,prop) prop = self.prop.getProp('',attr=['orientation']) if prop: pf.message("Writing orientations") fil.write(fmtOrientation(prop)) prop = self.prop.getProp('',attr=['ConnectorBehavior']) if prop: pf.message("Writing Connector Behavior") fil.write(fmtConnectorBehavior(prop)) prop = self.prop.getProp('n',attr=['equation']) if prop: pf.message("Writing constraint equations") fil.write(fmtEquation(prop)) prop = self.prop.getProp('',attr=['surftype']) if prop: pf.message("Writing surfaces") fil.write(fmtSurface(prop)) prop = self.prop.getProp('',attr=['analyticalsurface']) if prop: pf.message("Writing analytical surfaces") fil.write(fmtAnalyticalSurface(prop)) prop = self.prop.getProp('',attr=['interaction']) if prop: pf.message("Writing contact pairs") fil.write(fmtContactPair(prop)) prop = self.prop.getProp('',attr=['generalinteraction']) if prop: pf.message("Writing general contact") fil.write(fmtGeneralContact(prop)) prop = self.prop.getProp('',attr=['constraint']) if prop: pf.message("Writing constraints") fil.write(fmtConstraint(prop)) prop = self.prop.getProp('',attr=['initialcondition']) if prop: pf.message("Writing initial conditions") fil.write(fmtInitialConditions(prop)) prop = self.prop.getProp('n',tag=self.bound,attr=['bound']) if prop: pf.message("Writing initial boundary conditions") writeBoundaries(fil,prop) pf.message("Writing steps") for step in self.steps: step.write(fil,self.prop,self.out,self.res,resfreq=Result.nintervals,timemarks=Result.timemarks) if filename is not None: fil.close() pf.message("Wrote Abaqus input file %s" % filename) ################################################## ## Some convenience functions ################################################## def exportMesh(filename,mesh,eltype=None,header=''): """Export a finite element mesh in Abaqus .inp format. This is a convenience function to quickly export a mesh to Abaqus without having to go through the whole setup of a complete finite element model. This just writes the nodes and elements specified in the mesh to the file with the specified name. The resulting file can then be imported in Abaqus/CAE or manual be edited to create a full model. If an eltype is specified, it will oerride the value stored in the mesh. This should be used to set a correct Abaqus element type matchin the mesh. """ fil = open(filename,'w') fil.write(fmtHeading(header)) if eltype is None: eltype = mesh.eltype writeNodes(fil,mesh.coords) writeElems(fil,mesh.elems,eltype,nofs=1) fil.close() pf.message("Abaqus file %s written." % filename) ################################################## ## Test ################################################## if __name__ == "script" or __name__ == "draw": def TestwriteFormatLines(): a = arange(27) print fmtData1D(a) print fmtData1D(a,5) print fmtData1D(a,12) a = a.reshape(3,9) print fmtData(a) print fmtData(a,5) print fmtData(a,12) TestwriteFormatLines() exit() print("The data hereafter are incorrect and inconsistent.") print("See the FeAbq example for a comprehensive example.") # Create the geometry (4 quads) F = Formex('4:0123').replic2(2,2) # Create Finite Element model nodes,elems = F.feModel() if pf.GUI: draw(F) drawNumbers(F) drawNumbers(Formex(nodes),color=red) # Create property database P = PropertyDB() #install example materials and section databases # either like this Mat = MaterialDB(getcfg('datadir')+'/materials.db') P.setMaterialDB(Mat) # or like this P.setSectionDB(SectionDB(getcfg('datadir')+'/sections.db')) exit() # creating some property data S1 = ElemSection('IPEA100', 'steel') S2 = ElemSection({'name':'circle','radius':10,'sectiontype':'circ'},'steel','CIRC') S3 = ElemSection(sectiontype='join') BL1 = ElemLoad(label='PZ',value=0.5) BL2 = ElemLoad('Grav') S2.cross_section=572 CYL = CoordSystem('cylindrical',[0,0,0,0,0,1]) # populate the property database P.nodeProp(tag='d1',set=[0,1],cload=[2,6,4,0,0,0],displ=[(3,5.4)],csys=CYL) p = P.nodeProp(tag='b0',set=[1,2],cload=[9,2,5,3,0,4],bound='pinned') P.nodeProp(tag='d2',setname=p.name,bound=[1,1,1,0,0,1],displ=[(2,6),(4,8.)]) bottom = P.elemProp(12,section=S2,dload=[BL1],eltype='T2D3') top = P.elemProp(2,section=S2,dload=[BL2],eltype='FRAME2D') diag = P.elemProp(8,section=S3,eltype='conn3d2') # create the model nodes,elems = F.feModel() model = Model(nodes,elems) # create the steps step1 = Step(tags=['d1']) step2 = Step(nlgeom='yes',tags=['d2']) #create the output requests out = [ Output(type='history'), Output(type='field'), Output(type='field',kind='element',set=Eset(bottom.nr),keys=['SF']), ] res = [ Result(kind='NODE',keys=['U']), Result(kind='ELEMENT',keys=['SF'],set=Eset(top.nr)), ] all = AbqData(model,P,[step1,step2],res,out,bound=['b0']) all.write('testing') # End pyformex-0.8.6/pyformex/plugins/fe_ast.py0000644000211500021150000004207311705103030020373 0ustar benebene00000000000000# $Id$ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Exporting finite element models in code Aster file formats (.mail and .comm). """ from plugins.fe_abq import fmtData from plugins.properties import * from plugins.fe import * from mydict import Dict,CDict import pyformex as pf from datetime import datetime import os,sys def astInputNames(job,extension='mail'): """Returns corresponding Code Aster input filename. job should be a jobname, with or without directory part, but without extension The extension can be mail or comm. The jobname is the basename without the extension and the directory part. The filename is the abspath of the job with extension. """ jobname = os.path.basename(job) filename = os.path.abspath(job) if extension in ['mail','comm']: filename += '.%s' % extension else: raise ValueError,"Extension should be mail or comm" return jobname,filename def nsetName(p): """Determine the name for writing a node set property.""" if p.name is None: return 'Nall' else: return p.name def esetName(p): """Determine the name for writing an element set property.""" if p.name is None: return 'Eall' else: return p.name def writeNodes(fil,nodes,type,name=None): """Write nodal coordinates. Type can be 2D or 3D. """ if not type in ['2D','3D']: raise ValueError,"Type should be 2D or 3D" out = 'COOR_%s' % type if name is not None: out += ' nom = %s' % name fil.write('%s\n'% out) if type == '2D': nodes = nodes[:,:2] nn = nodes.shape[1] fmt = 'N%d' + nn*' %14.6e' + '\n' for i,n in enumerate(nodes): fil.write(fmt % ((i,)+tuple(n))) fil.write('FINSF\n') fil.write('%\n') def writeElems(fil,elems,type,name=None,eid=None,eofs=0,nofs=0): """Write element group of given type. elems is the list with the element node numbers. The elements are added to the named element set. The eofs and nofs specify offsets for element and node numbers. If eid is specified, it contains the element numbers increased with eofs. """ out = type if name is not None: out += ' nom = %s' % name fil.write('%s\n'% out) nn = elems.shape[1] if nn < 5: fmt = 'M%d' + nn*' N%d' + '\n' else: fl = nn/4 fmt = 'M%d' + fl*(4*' N%d' + '\n') if nn%4 != 0: fmt += (nn%4)*' N%d' + '\n' if eid is None: eid = arange(elems.shape[0]) else: eid = asarray(eid) for i,e in zip(eid+eofs,elems+nofs): fil.write(fmt % ((i,)+tuple(e))) fil.write('FINSF\n') fil.write('%\n') def writeSet(fil,type,name,set): """Write a named set of nodes or elements (type=NSET|ELSET) `set` is a list of node/element numbers, in which case the `ofs` value will be added to them. """ if type == 'NSET': fil.write('GROUP_NO nom = %s\n' % name) cap = 'N' elif type == 'ELSET': fil.write('GROUP_MA nom = %s\n' % name) cap = 'M' else: raise ValueError,"Type should be NSET or ELSET" for i in set: fil.write('%s%d\n' % (cap,i)) fil.write('FINSF\n') fil.write('%\n') def fmtHeadingMesh(text=''): """Format the heading of the Code Aster mesh file (.mail).""" out = """TITRE Code Aster mail file created by %s (%s) %s FINSF """ % (pf.Version,pf.Url,text) return out def fmtHeadingComm(text=''): """Format the heading of the Code Aster command file (.comm).""" out = """# # Code Aster command file created by %s (%s) # %s """ % (pf.Version,pf.Url,text) return out def fmtEquation(prop): """Format multi-point constraint using an equation Required: - name - equation Optional: - coefficient Equation should be a list, which contains the different terms of the equation. Each term is again a list with three values: - First value: node number - Second value: degree of freedom - Third value: multiplication coefficient of the term The sum of the different terms should be equal to the coefficient. If this coefficient is not specified, the sum of the terms should be equal to zero. Example: P.nodeProp(equation=[[209,1,1],[32,1,-1]]) In this case, the displacement in Y-direction of node 209 and 32 should be equal. """ dof = ['DX','DY','DZ'] out = 'link = AFFE_CHAR_MECA(\n' out += ' MODELE=Model,\n' out += ' LIAISON_DDL=(\n' for i,p in enumerate(prop): l1 = ' _F(NOEUD=(' l2 = ' DDL=(' l3 = ' COEF_MULT=(' for j in p.equation: l1 += '\'N%s\',' % j[0] l2 += '\'%s\',' % dof[j[1]] l3 += '%s,' % j[2] out += l1 + '),\n' + l2 + '),\n' + l3 + '),\n' coef = 0 if p.coefficient is not None: coef = p.coefficient out += ' COEF_IMPO=%s,),\n' % coef out += ' ),\n' out += ' );\n\n' return out def fmtDisplacements(prop): """Format nodal boundary conditions Required: - set - name - displ Displ should be a list of tuples (dofid,value) Set can be a list of node numbers, or a set name (string). Example 1: P.nodeProp(set='bottom',bound=[(0,0),(1,0),(2,0)]) Example 2: P.nodeProp(name='rot',set=[2],bound=[(3,30)]) In the first example, the displacements of the nodes in the set 'bottom' are zero. In the second example, a rotation is imposed around the X-axis on node number 2. """ dof = ['DX','DY','DZ','DRX','DRY','DRZ'] out = '' for i,p in enumerate(prop): out += 'displ%s = AFFE_CHAR_MECA(\n' % i out += ' MODELE=Model,\n' out += ' DDL_IMPO=\n' out += ' _F(GROUP_NO=(\'%s\'),\n' % p.name.upper() for j in p.displ: out += ' %s=%s,\n' % (dof[j[0]],j[1]) out += ' ),\n' out += ' );\n\n' return out def fmtLocalDisplacements(prop): """Format nodal boundary conditions in a local coordinate system Required: - name - displ - local Displ should be a list of tuples (dofid,value) Local is an angle, specified in degrees (SHOULD BE EXTENDED TO THREE ANGLES!!!) The local cartesian coordinate system is obtained by rotating the global coordinate system around the Z-axis over the specified angle. Set can be a list of node numbers, or a set name (string). """ dof = ['DX','DY','DZ','DRX','DRY','DRZ'] out = 'locDispl = AFFE_CHAR_MECA(\n' out += ' MODELE=Model,\n' out += ' LIAISON_OBLIQUE=(\n' for i,p in enumerate(prop): for j in p.displ: out += ' _F(GROUP_NO=(\'%s\'),\n' % p.name.upper() out += ' ANGL_NAUT=%s,\n' % p.local out += ' %s=%s),\n' % (dof[j[0]],j[1]) out += ' ),\n' out += ' );\n\n' return out materialswritten=[] def fmtMaterial(mat): """Write a material section. """ if mat.name is None or mat.name in materialswritten: return "" out = '%s = DEFI_MATERIAU(\n' % mat.name materialswritten.append(mat.name) print materialswritten if mat.elasticity is None or mat.elasticity == 'linear': if mat.poisson_ratio is None and mat.shear_modulus is not None: mat.poisson_ratio = 0.5 * mat.young_modulus / mat.shear_modulus - 1.0 out += ' ELAS=_F(E=%s,NU=%s),\n' % (float(mat.young_modulus),float(mat.poisson_ratio)) if mat.plastic is not None: mat.plastic = asarray(mat.plastic) if mat.plastic.ndim != 2: raise ValueError,"Plastic data should be 2-dim array" out1 = 'SIGMF=DEFI_FONCTION(\n' out1 += ' NOM_PARA=\'EPSI\',\n' out1 += ' VALE=(\n' for i in mat.plastic: out1 += ' %s,%s,\n' % (i[0],i[1]) out1 += ' ),\n' out1 += ' );\n\n' out += ' TRACTION=_F(SIGM=SIGMF,),\n' out = out1 + out out += ' );\n\n' return out solid3d_elems = [ 'HEXA8',] def fmtSections(prop): """Write element sections. prop is a an element property record with a section and eltype attribute """ out1 = 'Model=AFFE_MODELE(\n' out1 += ' MAILLAGE=Mesh,\n' out1 += ' AFFE=(\n' out2 = '' out3 = 'Mat=AFFE_MATERIAU(\n' out3 += ' MODELE=Model,\n' out3 += ' MAILLAGE=Mesh,\n' out3 += ' AFFE=(\n' for p in prop: setname = esetName(p) el = p.section eltype = p.eltype.upper() mat = el.material out1 += ' _F(GROUP_MA=\'%s\',\n' % setname.upper() out1 += ' PHENOMENE=\'MECANIQUE\',\n' out3 += ' _F(GROUP_MA=\'%s\',\n' % setname.upper() if mat is not None: out2 += fmtMaterial(mat) ############ ## 3DSOLID elements ########################## if eltype in solid3d_elems: if el.sectiontype.upper() == '3DSOLID': out1 += ' MODELISATION=\'3D\'),\n' out3 += ' MATER=%s),\n' % mat.name out1 += ' ),\n' out1 += ' );\n\n' out3 += ' ),\n' out3 += ' );\n\n' return out1 + out2 + out3 class AstData(object): """Contains all data required to write the Code Aster mesh (.mail) and command (.comm) files. - `model` : a :class:`Model` instance. - `prop` : the `Property` database. - `steps` : a list of `Step` instances. - `res` : a list of `Result` instances. - `out` : a list of `Output` instances. - `bound` : a tag or alist of the initial boundary conditions. The default is to apply ALL boundary conditions initially. Specify a (possibly non-existing) tag to override the default. """ def __init__(self,model,prop,nprop=None,eprop=None,steps=[],res=[],out=[],bound=None,type='3D'): """Create new AstData.""" if not isinstance(model,Model) or not isinstance(prop,PropertyDB): raise ValueError,"Invalid arguments: expected Model and PropertyDB, got %s and %s" % (type(model),type(prop)) self.model = model self.prop = prop self.nprop = nprop self.eprop = eprop self.bound = bound self.steps = steps self.res = res self.out = out self.type = type def writeMesh(self,jobname=None,header=''): """Write a Code Aster mesh file (.mail). """ # Create the Code Aster mesh file if jobname is None: jobname,filename = 'Test',None fil = sys.stdout else: jobname,filename = astInputNames(jobname,extension='mail') fil = open(filename,'w') pf.message("Writing mesh to file %s" % (filename)) fil.write(fmtHeadingMesh("""Model: %s Date: %s Created by pyFormex Script: %s %s """ % (jobname, datetime.now(), pf.scriptName, header))) # write coords nnod = self.model.nnodes() pf.message("Writing %s nodes" % nnod) writeNodes(fil,self.model.coords,self.type) # write elements pf.message("Writing elements and element sets") telems = self.model.celems[-1] nelems = 0 for p in self.prop.getProp('e'): if p.set is not None: # element set is directly specified set = p.set elif p.prop is not None: # element set is specified by eprop nrs if self.eprop is None: raise ValueError,"elemProp has a 'prop' field but no 'eprop' was specified" set = where(self.eprop == p.prop)[0] else: # default is all elements set = range(telems) setname = esetName(p) if p.has_key('eltype'): print('Writing elements of type %s: %s' % (p.eltype,set)) gl,gr = self.model.splitElems(set) elems = self.model.getElems(gr) elnrs = array([]).astype(int) els = array([]).astype(int) for i in elems: nels = len(i) if nels > 0: els = append(els,i).reshape(-1,i.shape[1]) nelems += nels writeElems(fil,els,p.eltype,name=setname,eid=set) pf.message("Writing element sets") writeSet(fil,'ELSET',setname,set) pf.message("Total number of elements: %s" % telems) if nelems != telems: pf.message("!! Number of elements written: %s !!" % nelems) # write node sets pf.message("Writing node sets") for p in self.prop.getProp('n',attr=['set']): if p.set is not None: # set is directly specified set = p.set elif p.prop is not None: # set is specified by nprop nrs if self.nprop is None: raise ValueError,"nodeProp has a 'prop' field but no 'nprop' was specified" set = where(self.nprop == p.prop)[0] else: # default is all nodes set = range(self.model.nnodes()) setname = nsetName(p) writeSet(fil,'NSET',setname,set) ## # write element sets ## pf.message("Writing element sets") ## for p in self.prop.getProp('e',noattr=['eltype']): ## if p.set is not None: ## # element set is directly specified ## set = p.set ## elif p.prop is not None: ## # element set is specified by eprop nrs ## if self.eprop is None: ## raise ValueError,"elemProp has a 'prop' field but no 'eprop' was specified" ## set = where(self.eprop == p.prop)[0] ## else: ## # default is all elements ## set = range(telems) ## setname = esetName(p) ## writeSet(fil,'ELSET',setname,set) fil.write('FIN') if filename is not None: fil.close() pf.message("Wrote Code Aster mesh file (.mail) %s" % filename) def writeComm(self,jobname=None,header=''): global materialswritten materialswritten = [] # Create the Code Aster command file if jobname is None: jobname,filename = 'Test',None fil = sys.stdout else: jobname,filename = astInputNames(jobname,extension='comm') fil = open(filename,'w') pf.message("Writing command to file %s" % (filename)) fil.write(fmtHeadingComm("""Model: %s Date: %s Created by pyFormex # Script: %s # %s # """ % (jobname, datetime.now(), pf.scriptName, header))) fil.write('DEBUT();\n\n') fil.write('Mesh=LIRE_MAILLAGE(INFO=2,);\n\n') prop = self.prop.getProp('e',attr=['section','eltype']) if prop: pf.message("Writing element sections") fil.write(fmtSections(prop)) prop = self.prop.getProp('n',attr=['displ'],noattr=['local']) if prop: pf.message("Writing displacement boundary conditions") fil.write(fmtDisplacements(prop)) prop = self.prop.getProp('n',attr=['local']) if prop: pf.message("Writing local displacement boundary conditions") fil.write(fmtLocalDisplacements(prop)) prop = self.prop.getProp('n',attr=['equation']) if prop: pf.message("Writing constraint equations") fil.write(fmtEquation(prop)) fil.write('FIN();\n') if filename is not None: fil.close() pf.message("Wrote Code Aster command file (.comm) %s" % filename) # End pyformex-0.8.6/pyformex/plugins/nurbs_menu.py0000644000211500021150000001530511705104656021324 0ustar benebene00000000000000# $Id: nurbs_menu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Nurbs menu """ from draw2d import * from plugins.objects import * from plugins.tools_menu import _drawables from plugins.geometry_menu import autoname from gui import menu # We subclass the DrawableObject to change its toggleAnnotation method class NurbsObjects(DrawableObjects): def __init__(self): DrawableObjects.__init__(self,clas=NurbsCurve) def toggleAnnotation(self,i=0,onoff=None): """Toggle mesh annotations on/off. This functions is like DrawableObjects.toggleAnnotation but also updates the geometry_menu when changes are made. """ DrawableObjects.toggleAnnotation(self,i,onoff) geometry_menu = pf.GUI.menu.item(_menu) toggle_menu = geometry_menu.item("toggle annotations") # This relies on the menu having the same items as the annotation list action = toggle_menu.actions()[i] action.setChecked(selection.hasAnnotation(i)) selection = NurbsObjects() selection_PL = objects.DrawableObjects(clas=PolyLine) selection_BS = objects.DrawableObjects(clas=BezierSpline) ntoggles = len(selection.annotations) def toggleEdgeNumbers(): selection.toggleAnnotation(0+ntoggles) def toggleNodeNumbers(): print "SURFACE_MENU: %s" % selection selection.toggleAnnotation(1+ntoggles) def toggleNormals(): selection.toggleAnnotation(2+ntoggles) def toggleAvgNormals(): selection.toggleAnnotation(3+ntoggles) ## selection.annotations.extend([[draw_edge_numbers,False], ## [draw_node_numbers,False], ## [draw_normals,False], ## [draw_avg_normals,False], ## ]) class _options(object): color = 'blue'; ctrl = True; ctrl_numbers = False; knots = True; knotsize = 6; knot_numbers = False; knot_values = False; def drawNurbs(N): draw(N,color=_options.color,nolight=True) if _options.ctrl: draw(N.coords,nolight=True) if _options.ctrl_numbers: drawNumbers(N.coords,nolight=True) if _options.knots: draw(N.knotPoints(),color=_options.color,marksize=_options.knotsize,nolight=True) if _options.knot_numbers: drawNumbers(N.knotPoints(),nolight=True) # Override some functions for nurbs def createNurbsCurve(N,name=None): """Create a new Nurbs curve in the database. This will ask for a name if none is specified. The curve is exported under that name, drawn and selected. If no name is returned, the curve is not stored. """ an = autoname['nurbscurve'] drawNurbs(N) if name is None: name = an.peek() res = askItems([ ('name',name,{'text':'Name for storing the object'}), ]) if not res: return None name = res['name'] if name == an.peek(): an.next() print name print pf.PF export({name:N}) print pf.PF selection.set([name]) return name def createInteractive(): mode='nurbs' res = askItems([('degree',3),('closed',False)]) obj_params.update(res) print "z value = %s" % the_zvalue points = drawPoints2D(mode,npoints=-1,zvalue=the_zvalue) if points is None: return print "POINTS %s" % points N = drawnObject(points,mode=mode) pf.canvas.removeHighlights() if N: createNurbsCurve(N,name=None) def fromControlPolygon(): if not selection_PL.check(single=True): selection_PL.ask(mode='single') if selection_PL.check(single=True): n = selection_PL.names[0] C = named(n) res = askItems([('degree',3)]) N = NurbsCurve(C.coords,degree=res['degree']) createNurbsCurve(N,name=None) draw(C) def fromPolyLine(): if not selection_PL.check(single=True): selection_PL.ask(mode='single') if selection_PL.check(single=True): n = selection_PL.names[0] C = named(n) N = NurbsCurve(C.coords,degree=1,blended=False) createNurbsCurve(N,name=None) ################################## Menu ############################# _menu = 'Nurbs' def create_menu(before='help'): """Create the menu.""" MenuData = [ ("&Select drawable",_drawables.ask), ("&Set grid",create_grid), ("&Remove grid",remove_grid), ("---",None), ("&Toggle Preview",toggle_preview,{'checkable':True}), ("---",None), ("&Create Nurbs Curve ",[ ("Interactive",createInteractive), ("From Control Polygon",fromControlPolygon), ("Convert PolyLine",fromPolyLine), # ("Convert BezierSpline",fromBezierSpline), ]), ("---",None), ("&Reload Menu",reload_menu), ("&Close Menu",close_menu), ("Test menu",test_menu), ] w = menu.Menu(_menu,items=MenuData,parent=pf.GUI.menu,before=before) return w def show_menu(before='help'): """Show the menu.""" if not pf.GUI.menu.action(_menu): create_menu(before=before) def close_menu(): """Close the menu.""" pf.GUI.menu.removeItem(_menu) def reload_menu(): """Reload the menu.""" before = pf.GUI.menu.nextitem(_menu) print "Menu %s was before %s" % (_menu,before) close_menu() import plugins plugins.refresh('draw2d') show_menu(before=before) setDrawOptions({'bbox':'last'}) print pf.GUI.menu.actionList() def test_menu(): print "TEST2" #################################################################### if __name__ == "draw": # If executed as a pyformex script reload_menu() # End pyformex-0.8.6/pyformex/plugins/wrl.py0000644000211500021150000000367411705104656017761 0ustar benebene00000000000000# $Id: wrl.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## from numpy import * from gui.draw import * _prop_ = 0 _name_ = "_dummy_" def name(s): global _name_ _name_ = str(s) def position(*args): pass def IndexedFaceSet(coords,faces=None): global _prop_ _prop_ += 1 coords = asarray(coords).reshape(-1,3) print(coords.shape,_prop_) F = Formex(coords,_prop_) print(F.prop) draw(F) export({"%s-%s" % (_name_,'coords'):F}) if faces is None: return def IndexedLineSet(coords,lines): coords = asarray(coords).reshape(-1,3) print(coords.shape) F = Formex(coords,_prop_) draw(F) export({"%s-%s" % (_name_,'coords'):F}) lines = column_stack([lines[:-1],lines[1:]]) print(lines.shape) G = Formex(coords[lines],_prop_) export({_name_:G}) draw(G) # End pyformex-0.8.6/pyformex/plugins/draw2d_menu.py0000644000211500021150000000606011705104656021354 0ustar benebene00000000000000# $Id: draw2d_menu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """2D drawing menu This pyFormex plugin menu provides some interactive 2D drawing functions. While the drawing operations themselves are in 2D, they can be performed on a plane with any orientation in space. The constructed geometry always has 3D coordinates in the global cartesian coordinate system. """ from draw2d import * from geometry_menu import * ################################## Menu ############################# _menu = 'Draw' def create_menu(before='help'): """Create the menu.""" MenuData = [ ("&Select drawable",drawable.ask), ("&Set grid",create_grid), ("&Remove grid",remove_grid), ("---",None), ("&Toggle Preview",toggle_preview,{'checkable':True}), ("---",None), ("&Draw Points",draw_points), ("&Draw Polyline",draw_polyline), ("&Draw Curve",draw_curve), ("&Draw Nurbs",draw_nurbs), ("&Draw Circle",draw_circle), ("---",None), ("&Split Curve",split_curve), ("---",None), ("&Reload Menu",reload_menu), ("&Close Menu",close_menu), ("Test menu",test_menu), ] w = menu.Menu(_menu,items=MenuData,parent=pf.GUI.menu,before=before) return w def show_menu(before='help'): """Show the menu.""" if not pf.GUI.menu.action(_menu): create_menu(before=before) def close_menu(): """Close the menu.""" pf.GUI.menu.removeItem(_menu) def reload_menu(): """Reload the menu.""" before = pf.GUI.menu.nextitem(_menu) print "Menu %s was before %s" % (_menu,before) close_menu() import plugins plugins.refresh('draw2d') show_menu(before=before) setDrawOptions({'bbox':'last'}) print pf.GUI.menu.actionList() def test_menu(): print "TEST2" #################################################################### if __name__ == "draw": # If executed as a pyformex script reload_menu() # End pyformex-0.8.6/pyformex/plugins/tools.py0000644000211500021150000002130711705104656020306 0ustar benebene00000000000000# $Id: tools.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """tools.py Graphic Tools for pyFormex. """ import pyformex as pf from coords import * from collection import Collection from gui.actors import GeomActor class Plane(object): def __init__(self,points,normal=None,size=((1.0,1.0),(1.0,1.0))): pts = Coords(points) if pts.shape == (3,) and normal is not None: P = pts n = Coords(normal) if n.shape != (3,): raise ValueError,"normal does not have correct shape" elif pts.shape == (3,3,): P = pts.centroid() n = cross(pts[1]-pts[0],pts[2]-pts[0]) else: raise ValueError,"point(s) does not have correct shape" size = asarray(size) s = Coords([insert(size[0],0,0.,-1),insert(size[1],0,0.,-1)]) self.P = P self.n = n self.s = s def point(self): return self.P def normal(self): return self.n def size(self): return self.s def bbox(self): return self.P.bbox() def __str__(self): return 'P:%s n:%s s:%s' % (list(self.P),list(self.n), (list(self.s[0]),list(self.s[1]))) def actor(self,**kargs): from gui import actors actor = actors.PlaneActor(size=self.s,**kargs) actor.list = actor.create_list(mode=pf.canvas.rendermode) actor = actors.RotatedActor(actor,self.n,**kargs) actor.list = actor.create_list(mode=pf.canvas.rendermode) actor = actors.TranslatedActor(actor,self.P,**kargs) return actor ################# Report information about picked objects ################ def report(K): if K is not None and hasattr(K,'obj_type'): print(K.obj_type) if K.obj_type == 'actor': return reportActors(K) elif K.obj_type == 'element': return reportElements(K) elif K.obj_type == 'point': return reportPoints(K) elif K.obj_type == 'edge': return reportEdges(K) elif K.obj_type == 'partition': return reportPartitions(K) return '' def reportActors(K): s = "Actor report\n" v = K.get(-1,[]) s += "Actors %s\n" % v for k in v: A = pf.canvas.actors[k] t = A.getType() s += " Actor %s (type %s)\n" % (k,t) return s def reportElements(K): s = "Element report\n" for k in K.keys(): v = K[k] A = pf.canvas.actors[k] t = A.getType() s += "Actor %s (type %s); Elements %s\n" % (k,t,v) if t == 'Formex': e = A elif t == 'TriSurface': e = A.getElems() for p in v: s += " Element %s: %s\n" % (p,e[p]) return s def reportPoints(K): s = "Point report\n" for k in K.keys(): v = K[k] A = pf.canvas.actors[k] s += "Actor %s (type %s); Points %s\n" % (k,A.getType(),v) x = A.vertices() for p in v: s += " Point %s: %s\n" % (p,x[p]) return s def reportEdges(K): s = "Edge report\n" for k in K.keys(): v = K[k] A = pf.canvas.actors[k] s += "Actor %s (type %s); Edges %s\n" % (k,A.getType(),v) e = A.edges() for p in v: s += " Edge %s: %s\n" % (p,e[p]) def reportPartitions(K): s = "Partition report\n" for k in K.keys(): P = K[k][0] A = pf.canvas.actors[k] t = A.getType() for l in P.keys(): v = P[l] s += "Actor %s (type %s); Partition %s; Elements %s\n" % (k,t,l,v) if t == 'Formex': e = A elif t == 'TriSurface': e = A.getElems() for p in v: s += " Element %s: %s\n" % (p,e[p]) return s def reportDistances(K): if K is None or not hasattr(K,'obj_type') or K.obj_type != 'point': return '' s = "Distance report\n" x = Coords.concatenate(getCollection(K)) s += "First point: %s %s\n" % (0,x[0]) d = x.distanceFromPoint(x[0]) for i,p in enumerate(zip(x,d)): s += "Distance from point: %s %s: %s\n" % (i,p[0],p[1]) return s def reportAngles(K): if K is None or not hasattr(K,'obj_type') or K.obj_type != 'element': return '' s = "Angle report:\n" for F in getCollection(K): if isinstance(F,GeomActor): x = F.coords #print(x) if F.elems is None: #print(x.shape) v = x[:,1,:] - x[:,0,:] v = normalize(v) cosa = dotpr(v[0],v[1]) #print(cosa) a = arccosd(cosa) s += " a = %s" % a return s def getObjectItems(obj,items,mode): """Get the specified items from object.""" if mode == 'actor': return [ obj[i].object for i in items if hasattr(obj[i],'object') ] elif mode in ['element','partition']: if hasattr(obj,'object') and hasattr(obj.object,'select'): return obj.object.select(items) elif mode == 'point': if hasattr(obj,'vertices'): return obj.vertices()[items] return None def getCollection(K): """Returns a collection.""" if K.obj_type == 'actor': return [ pf.canvas.actors[int(i)].object for i in K.get(-1,[]) if hasattr(pf.canvas.actors[int(i)],'object') ] elif K.obj_type in ['element','point']: return [ getObjectItems(pf.canvas.actors[k],K[k],K.obj_type) for k in K.keys() ] elif K.obj_type == 'partition': return [getObjectItems(pf.canvas.actors[k],K[k][0][prop],K.obj_type) for k in K.keys() for prop in K[k][0].keys()] else: return None def growCollection(K,**kargs): """Grow the collection with n frontal rings. K should be a collection of elements. This currently only works on surfaces. Objects that do not have a nodeFront() generator function are """ if K.obj_type == 'element': for k in K.keys(): o = pf.canvas.actors[k] if hasattr(o,'growSelection'): K[k] = o.growSelection(K[k],**kargs) def partitionCollection(K): """Partition the collection according to node adjacency. The actor numbers will be connected to a collection of property numbers, e.g. 0 [1 [4,12] 2 [6,20]], where 0 is the actor number, 1 and 2 are the property numbers and 4, 12, 6 and 20 are the element numbers. """ sel = getCollection(K) if len(sel) == 0: print("Nothing to partition!") return if K.obj_type == 'actor': actor_numbers = K.get(-1,[]) K.clear() for i in actor_numbers: K.add(range(sel[int(i)].nelems()),i) prop = 1 j = 0 for i in K.keys(): p = sel[j].partitionByConnection() + prop print("Actor %s partitioned in %s parts" % (i,p.max()-p.min()+1)) C = Collection() C.set(transpose(asarray([p,K[i]]))) K[i] = C prop += p.max()-p.min()+1 j += 1 K.setType('partition') def getPartition(K,prop): """ Remove all partitions with property not in prop.""" for k in K.keys(): for p in K[k][0].keys(): if not p in prop: K[k][0].remove(K[k][0][p],p) def exportObjects(obj,name,single=False): """Export a list of objects under the given name. If obj is a list, and single=True, each element of the list is exported as a single item. The items will be given the names name-0, name-1, etc. Else, the obj is exported as is under the name. """ if single and type(obj) == list: export(dict([ ("name-%s"%i,v) for i,v in enumerate(obj)])) else: export({name:obj}) # End pyformex-0.8.6/pyformex/plugins/partition.py0000644000211500021150000001331411705104656021156 0ustar benebene00000000000000# $Id: partition.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## import pyformex as pf from gui import decors,colors from gui.camera import inverse from gui.draw import * from formex import * import geomtools VA=None def prepare(V): """Prepare the surface for slicing operation.""" global VA V = V.translate(-V.center()) P = V.center() print("Initial P = %s" % P) VA = draw(V,bbox=None,color='black') area,norm = geomtools.areaNormals(V.coords) N = norm[0] return V,P,N def testview(F,V,P): global VA,waiting p = array(pf.canvas.camera.getCenter()) waiting = True while waiting: pf.app.processEvents() p -= array(pf.canvas.camera.getCenter()) print("TRANSLATE: %s" % p) m = pf.canvas.camera.getRot() P += p print("TOTAL TRANSLATE: %s" % P) V = V.affine(inverse(m[0:3,0:3])).translate(-P) print(V.center()) print(F.center()) undraw(VA) VA = draw(V) area,norm = geomtools.areaNormals(V.coords) N = norm[0] return P,N def colorCut(F,P,N,prop): """Color a Formex in two by a plane (P,N)""" print(F.bbox()) print(P) print(N) print(prop) dist = F.distanceFromPlane(P,N) print(dist) right = any(dist>0.0,axis=1) print(right) F.prop[right] = prop nright = right.sum() nleft = F.nelems() - nright print("Left part has %s elements, right part has %s elements" % (nleft,nright)) return F def splitProp(F,name): """Partition a Formex according to its prop values. Returns a dict with the partitions, named like name-prop and exports these named Formex instances. It the Formex has no props, the whole Formex is given the name. """ if F.prop is None: d = { name:F } else: d = dict([['%s-%s' % (name,p),F.withProp(p)] for p in F.propSet()]) export(d) return d waiting = True def wakeup(): global waiting waiting = False def partition(Fin,prop=0): """Interactively partition a Formex. By default, the parts will get properties 0,1,... If prop >= 0, the parts will get incremental props starting from prop. Returns the cutplanes in an array with shape (ncuts,2,3), where (i,0,:) is a point in the plane i and (i,1,:) is the normal vector on the plane i . As a side effect, the properties of the input Formex will be changed to flag the parts between successive cut planes by incrementing property values. If you wish to restore the original properties, you should copy them (or the input Formex) before calling this function. """ global FA,VA,waiting # start color keepprops = prop if prop is None or prop < 0: prop = 0 # Make sure the inital Formex is centered initial_trl = -Fin.center() F = Fin.translate(initial_trl) # Store the inital properties and make all properties equal to start value initial_prop = F.prop F.setProp(prop) # draw it linewidth(1) perspective(False) clear() FA = draw(F,view='front') # create a centered cross plane, large enough bb = F.bbox() siz = F.sizes().max() V = Formex([[[0.,0.,0.],[1.,0.,0.],[1.,1.,0.]], [[1.,1.,0.],[0.,1.,0.],[0.,0.,0.]]], 0) V = V.translate(-V.center()).rotate(90,1).scale(siz) cut_planes = [] QtCore.QObject.connect(pf.canvas,QtCore.SIGNAL("Wakeup"),wakeup) linewidth(2) w,h = pf.canvas.width(),pf.canvas.height() fgcolor('magenta') SD = decors.Line(w/2,0,w/2,h) decorate(SD) fgcolor(colors.black) V,P,N = prepare(V) while True: res = ask("",["Adjust Cut","Keep Cut", "Finish"]) if res == "Adjust Cut": P,N = testview(F,V,P) print("Plane: point %s, normal %s" % (P,N)) elif res == "Keep Cut": undraw(FA) #undraw(VA) cut_planes.append((P,N)) prop += 1 F = colorCut(F,-P,N,prop) FA = draw(F) undraw(VA) V,P,N = prepare(V) else: break QtCore.QObject.disconnect(pf.canvas,QtCore.SIGNAL("Wakeup"),wakeup) clear() draw(F) Fin.setProp(F.prop) return array(cut_planes) def savePartitions(F): print("Current dir is %s" % os.getcwd()) if ack("Save the partitioned Formex?"): writeFormex(F,'part.fmx') clear() if ack("Reread/draw the partitioned Formex?"): F = readFormex('part.fmx') draw(F) d = splitProp(F,'part') export(d) if ack("Save the partitions separately?"): for (k,v) in d.iteritems(): writeFormex(v,"%s.fmx"%k) # End pyformex-0.8.6/pyformex/plugins/cameratools.py0000644000211500021150000000726511705104656021466 0ustar benebene00000000000000# $Id: cameratools.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """Camera tools Some extra tools to handle the camera. """ import pyformex as pf from gui.widgets import simpleInputItem as _I,InputDialog as Dialog from utils import stuur dialog = None def getCameraSettings(cam): return dict([ (k,getattr(cam,k)) for k in ['ctr','dist','rot','fovy','aspect','area','near','far']]) def apply(): global dialog dialog.acceptData() settings = dialog.results #print settings cam = pf.canvas.camera cam.setClip(settings['near'],settings['far']) pf.canvas.update() def close(): global dialog if dialog: dialog.close() dialog = None def updateSettings(cam): global dialog settings = getCameraSettings(cam) dialog.updateData(settings) def setNear(fld): val = fld.value()/100. cam = pf.canvas.camera res = stuur(val,[0.,0.5,1.0],[0.01*cam.dist,cam.dist,100.*cam.dist]) #print "%s = %s" % (val,res) cam.setClip(res,cam.far) pf.canvas.update() def setFar(fld): val = fld.value()/100. cam = pf.canvas.camera res = stuur(val,[0.,0.5,1.0],[0.01*cam.dist,cam.dist,100.*cam.dist]) #print "%s = %s" % (val,res) cam.setClip(cam.near,res) pf.canvas.update() def showCameraTool(): global dialog cam = pf.canvas.camera settings = getCameraSettings(cam) settings['near'] = cam.near/cam.dist settings['far'] = cam.far/cam.dist dialog = Dialog(store=settings, items=[ _I('ctr',text='Center',itemtype='point',tooltip='The center of the scene where the camera is looking at.'), _I('dist',text='Distance',tooltip='The distance of the camera to the center of the scene.'), _I('fovy',text='Field of View',tooltip='The vertical opening angle of the camera lens.'), _I('aspect',text='Aspect ratio',tooltip='The ratio of the vertical over the horizontal lens opening angles.'), # _I('area',text='Visible area',tooltip='Relative part of the camera area that is visible in the viewport.'), _I('near',text='Near clipping plane',itemtype='fslider',func=setNear,tooltip='Distance of the near clipping plane to the camera.'), _I('far',text='Far clipping plane',itemtype='fslider',func=setFar,tooltip='Distance of the far clipping plane to the camera.'), ],actions = [('Close',close), # ('Apply',apply), ], default='Close', ) dialog.show() cam.modelview_callback = updateSettings if __name__ == 'draw': showCameraTool() dialog.timeout = close # End pyformex-0.8.6/pyformex/plugins/properties.py0000644000211500021150000006603611705104656021352 0ustar benebene00000000000000# $Id: properties.py 2150 2012-01-16 20:33:48Z bverheg $ *** pyformex *** ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """General framework for attributing properties to geometrical elements. Properties can really be just about any Python object. Properties can be attributed to a set of geometrical elements. """ from flatkeydb import FlatDB from mydict import Dict,CDict from arraytools import * ################################################################# # This first part still needs to be changed. # It should probably be moved to a separate module class Database(Dict): """A class for storing properties in a database.""" def __init__(self,data={}): """Initialize a database. The database can be initialized with a dict. """ Dict.__init__(self,data) def readDatabase(self,filename,*args,**kargs): """Import all records from a database file. For now, it can only read databases using flatkeydb. args and kargs can be used to specify arguments for the FlatDB constructor. """ mat = FlatDB(*args,**kargs) mat.readFile(filename) for k,v in mat.iteritems(): self[k] = Dict(v) class MaterialDB(Database): """A class for storing material properties.""" def __init__(self,data={}): """Initialize a materials database. If data is a dict, it contains the database. If data is a string, it specifies a filename where the database can be read. """ Database.__init__(self,{}) if type(data) == str: self.readDatabase(data,['name'],beginrec='material',endrec='endmaterial') elif type(data) == dict: self.update(data) else: raise ValueError,"Expected a filename or a dict." class SectionDB(Database): """A class for storing section properties.""" def __init__(self,data={}): """Initialize a section database. If data is a dict, it contains the database. If data is a string, it specifies a filename where the database can be read. """ Database.__init__(self,{}) if type(data) == str: self.readDatabase(data,['name'],beginrec='section',endrec='endsection') elif type(data) == dict: self.update(data) else: raise ValueError,"Expected a filename or a dict." class ElemSection(CDict): """Properties related to the section of an element. An element section property can hold the following sub-properties: section the geometric properties of the section. This can be a dict or a string. If it is a string, its value is looked up in the global section database. The required data in the dict depend on the sectiontype. Currently the following keys are used by fe_abq.py: sectiontype the type of section: should be one of following: - 'solid': a solid 2D or 3D section, - 'circ' : a plain circular section, - 'rect' : a plain rectangular section, - 'pipe' : a hollow circular section, - 'box' : a hollow rectangular section, - 'I' : an I-beam, - 'general' : anything else (automatically set if not specified). !! Currently only 'solid' and 'general' are allowed. - the cross section characteristics : cross_section, moment_inertia_11, moment_inertia_12, moment_inertia_22, torsional_constant - for sectiontype 'circ': radius material the element material. This can be a dict or a string. Currently known keys to fe_abq.py are: young_modulus, shear_modulus, density, poisson_ratio . (see fmtMaterial in fe_abq) orientation - a Dict, or - a list of 3 direction cosines of the first beam section axis. behavior the behavior of a connector """ # All the instances use the same databases matDB = None #MaterialDB() secDB = None #SectionDB() def __init__(self,section=None,material=None,orientation=None,behavior=None,**kargs): ### sectiontype is now an attribute of section ### """Create a new element section property. Empty by default.""" self._class_init() CDict.__init__(self,kargs) self.addMaterial(material) self.addSection(section) ## if sectiontype is not None: ## self.sectiontype = sectiontype if orientation is not None: self.orientation = orientation ## self.behavior = behavior @classmethod def _class_init(clas): if clas.matDB is None: clas.matDB = MaterialDB() if clas.secDB is None: clas.secDB = SectionDB() def addSection(self, section): """Create or replace the section properties of the element. If 'section' is a dict, it will be added to 'self.secDB'. If 'section' is a string, this string will be used as a key to search in 'self.secDB'. """ if isinstance(section, str): if self.secDB.has_key(section): self.section = self.secDB[section] else: warning("Section '%s' is not in the database" % section) elif isinstance(section,dict): # WE COULD ADD AUTOMATIC CALCULATION OF SECTION PROPERTIES #self.computeSection(section) #print(section) self.secDB[section['name']] = CDict(section) self.section = self.secDB[section['name']] elif section==None: self.section = section else: raise ValueError,"Expected a string or a dict" def computeSection(self,section): """Compute the section characteristics of specific sections.""" if not section.has_key('sectiontype'): return if section['sectiontype'] == 'circ': r = section['radius'] A = pi * r**2 I = pi * r**4 / 4 section.update({'cross_section':A, 'moment_inertia_11':I, 'moment_inertia_22':I, 'moment_inertia_12':0.0, 'torsional_constant':2*I, }) else: raise ValueError,"Invalid sectiontype" def addMaterial(self, material): """Create or replace the material properties of the element. If the argument is a dict, it will be added to 'self.matDB'. If the argument is a string, this string will be used as a key to search in 'self.matDB'. """ if isinstance(material, str) : if self.matDB.has_key(material): self.material = self.matDB[material] else: warning("Material '%s' is not in the database" % material) elif isinstance(material, dict): self.matDB[material['name']] = CDict(material) self.material = self.matDB[material['name']] elif material==None: self.material=material else: raise ValueError,"Expected a string or a dict" class ElemLoad(CDict): """Distributed loading on an element.""" def __init__(self,label=None,value=None): """Create a new element load. Empty by default. An element load can hold the following sub-properties: - label: the distributed load type label. - value: the magnitude of the distibuted load. """ Dict.__init__(self,{'label':label,'value':value}) class EdgeLoad(CDict): """Distributed loading on an element edge.""" def __init__(self,edge=-1,label=None,value=None): """Create a new element edge load. Empty by default. An element edgeload can hold the following sub-properties: - edge: the element edge number - label: the distributed load type label ('x','y','z'). - value: the magnitude of the distibuted load. """ Dict.__init__(self,{'edge':edge,'label':label,'value':value}) ############## Basic property data classes ######################## class CoordSystem(object): """A class for storing coordinate systems.""" valid_csys = 'RSC' def __init__(self,csys,cdata): """Create a new coordinate system. csys is one of 'Rectangular', 'Spherical', 'Cylindrical'. Case is ignored and the first letter suffices. cdata is a list of 6 coordinates specifying the two points that determine the coordinate transformation """ try: csys = csys[0].upper() if not csys in CoordSystem.valid_csys: raise cdata = asarray(cdata).reshape(2,3) except: raise ValueError,"Invalid initialization data for CoordSystem" self.sys = csys self.data = cdata class Amplitude(object): """A class for storing an amplitude. The amplitude is a list of tuples (time,value). """ def __init__(self,data,definition='TABULAR'): """Create a new amplitude.""" if definition in [ 'TABULAR', 'SMOOTH STEP' ]: self.data = checkArray(data,(-1,2),'f','i') self.type = definition else: raise ValueError,"Expected definition = 'TABULAR' or 'SMOOTH STEP'" ################################################### ############ Utility routines ##################### def checkIdValue(values): """Check that a variable is a list of (id,value) tuples `id` should be convertible to an int, value to a float. If ok, return the values as a list of (int,float) tuples. """ try: l = [ len(v) for v in values ] if min(l) == 2 and max(l) == 2: return [ (int(i),float(v)) for i,v in values ] except: raise ValueError,"Expected a list of (int,float) tuples" def checkArrayOrIdValue(values): """Check that a variable is an list of values or (id,value) tuples This convenience function checks that the argument is either: - a list of 6 float values (or convertible to it), or - a list of (id,value) tuples where id is convertible to an int, value to a float. If ok, return the values as a list of (int,float) tuples. """ ##print("VALUES IN: %s" % values) try: v = checkArray(values,(6,),'f','i') w = where(v != 0.0)[0] values = [ (i,v[i]) for i in w ] except: values = checkIdValue(values) ##print("VALUES OUT: %s" % values) return values def checkString(a,valid): """Check that a string a has one of the valid values. This is case insensitive, and returns the upper case string if valid. Else, an error is raised. """ try: a = a.upper() if a in valid: return a except: print("Expected one of %s, got: %s" % (valid,a)) raise ValueError # Create automatic names for node and element sets def autoName(base,*args): return (base + '_%s' * len(args)) % args # The following are not used by the PropertyDB class, # but may be convenient for the user in applications def Nset(*args): return autoName('Nset',*args) def Eset(*args): return autoName('Eset',*args) ############################################################# ##################### Properties Database ################### def FindListItem(l,p): """Find the item p in the list l. If p is an item in the list (not a copy of it!), this returns its position. Else, -1 is returned. Matches are found with a 'is' function, not an '=='. Only the first match will be reported. """ for i,j in enumerate(l): if j is p: return i return -1 def RemoveListItem(l,p): """Remove the item p from the list l. If p is an item in the list (not a copy of it!), it is removed from the list. Matches are found with a 'is' comparison. This is different from the normal Python list.remove() method, which uses '=='. As a result, we can find complex objects which do not allow '==', such as ndarrays. """ i = FindListItem(l,p) if i >= 0: del l[i] class PropertyDB(Dict): """A database class for all properties. This class collects all properties that can be set on a geometrical model. This should allow for storing: - materials - sections - any properties - node properties - elem properties - model properties (current unused: use unnamed properties) """ bound_strings = [ 'XSYMM', 'YSYMM', 'ZSYMM', 'ENCASTRE', 'PINNED' ] def __init__(self): """Create a new properties database.""" Dict.__init__(self) self.mats = MaterialDB() self.sect = SectionDB() self.prop = [] self.nprop = [] self.eprop = [] #self.mprop = [] @classmethod def autoName(clas,kind,*args): return autoName((kind+'set').capitalize(),*args) def setMaterialDB(self,aDict): """Set the materials database to an external source""" if isinstance(aDict,MaterialDB): self.mats = aDict ElemSection.matDB = aDict def setSectionDB(self,aDict): """Set the sections database to an external source""" if isinstance(aDict,SectionDB): self.sect = aDict ElemSection.secDB = aDict def Prop(self,kind='',tag=None,set=None,name=None,**kargs): """Create a new property, empty by default. A property can hold almost anything, just like any Dict type. It has however four predefined keys that should not be used for anything else than explained hereafter: - nr: a unique id, that never should be set/changed by the user. - tag: an identification tag used to group properties - name: the name to be used for this set. Default is to use an automatically generated name. - set: identifies the geometrical elements for which the defined properties will hold. This can be either: - a single number, - a list of numbers, - the name of an already defined set, - a list of such names. Besides these, any other fields may be defined and will be added without checking. """ d = CDict() # update with kargs first, to make sure tag,set and nr are sane d.update(dict(**kargs)) prop = getattr(self,kind+'prop') d.nr = len(prop) if tag is not None: d.tag = str(tag) if name is None and kargs.has_key('setname'): # allow for backwards compatibility import warnings warnings.warn("!! 'setname' is deprecated, please use 'name'") name = setname if name is None and type(set) is str: ### convenience to allow set='name' as alias for name='name' ### to reuse already defined set name,set = set,name if name is None: name = self.autoName(kind,d.nr) elif type(name) is not str: raise ValueError,"Property name should be a string" d.name = name if set is not None: if type(set) is int or type(set) is str: set = [ set ] d.set = unique(set) prop.append(d) return d # This should maybe change to operate on the property keys # and finally return the selected keys or properties? def getProp(self,kind='',rec=None,tag=None,attr=[],noattr=[],delete=False): """Return all properties of type kind matching tag and having attr. kind is either '', 'n', 'e' or 'm' If rec is given, it is a list of record numbers or a single number. If a tag or a list of tags is given, only the properties having a matching tag attribute are returned. attr and noattr are lists of attributes. Only the properties having all the attributes in attr and none of the properties in noattr are returned. Attributes whose value is None are treated as non-existing. If delete==True, the returned properties are removed from the database. """ prop = getattr(self,kind+'prop') if rec is not None: if type(rec) != list: rec = [ rec ] rec = [ i for i in rec if i < len(prop) ] prop = [ prop[i] for i in rec ] if tag is not None: if type(tag) != list: tag = [ tag ] tag = map(str,tag) # tags are always converted to strings! prop = [ p for p in prop if p.has_key('tag') and p['tag'] in tag ] for a in attr: prop = [ p for p in prop if p.has_key(a) and p[a] is not None ] for a in noattr: prop = [ p for p in prop if not p.has_key(a) or p[a] is None ] if delete: self._delete(prop,kind=kind) return prop def _delete(self,plist,kind=''): """Delete the specified properties from the database. plist is a list of property records (not copies of them!), such as returned by getProp. The kind parameter can specify a specific property database. """ prop = getattr(self,kind+'prop') if not type(plist) == list: pdel = [ plist ] for p in plist: RemoveListItem(prop,p) self._sanitize(kind) def _sanitize(self,kind): """Sanitize the record numbers after deletion""" prop = getattr(self,kind+'prop') for i,p in enumerate(prop): p.nr = i def delProp(self,kind='',rec=None,tag=None,attr=[]): """Delete properties. This is equivalent to getProp() but the returned properties are removed from the database. """ return self.getProp(kind=kind,rec=rec,tag=tag,attr=attr,delete=True) def nodeProp(self,prop=None,set=None,name=None,tag=None,cload=None,bound=None,displ=None,veloc=None,accel=None,csys=None,ampl=None,**kargs): """Create a new node property, empty by default. A node property can contain any combination of the following fields: - tag: an identification tag used to group properties (this is e.g. used to flag Step, increment, load case, ...) - set: a single number or a list of numbers identifying the node(s) for which this property will be set, or a set name If None, the property will hold for all nodes. - cload: a concentrated load: a list of 6 float values [FX,FY,FZ,MX,MY,MZ] or a list of (dofid,value) tuples. - displ,veloc,accel: prescribed displacement, velocity or acceleration: a list of 6 float values [UX,UY,UZ,RX,RY,RZ] or a list of tuples (dofid,value) - bound: a boundary condition: a str or a list of 6 codes (0/1) - csys: a CoordSystem - ampl: the name of an Amplitude """ try: d = kargs if cload is not None: d['cload'] = checkArrayOrIdValue(cload) if displ is not None: d['displ'] = checkArrayOrIdValue(displ) if veloc is not None: d['veloc'] = checkArrayOrIdValue(veloc) if accel is not None: d['accel'] = checkArrayOrIdValue(accel) if bound is not None: if type(bound) == str: d['bound'] = checkString(bound,self.bound_strings) elif type(bound) == list: d['bound'] = checkArray1D(bound,6,'i') if csys is not None: if isinstance(csys,CoordSystem): d['csys'] = csys else: raise ValueError,"Invalid Coordinate System" # Currently unchecked! if ampl is not None: d['ampl'] = ampl return self.Prop(kind='n',prop=prop,tag=tag,set=set,name=name,**d) except: print("tag=%s,set=%s,name=%s,cload=%s,bound=%s,displ=%s,csys=%s" % (tag,set,name,cload,bound,displ,csys)) raise ValueError,"Invalid Node Property" def elemProp(self,prop=None,grp=None,set=None,name=None,tag=None,section=None,eltype=None,dload=None,eload=None,ampl=None,**kargs): """Create a new element property, empty by default. An elem property can contain any combination of the following fields: - tag: an identification tag used to group properties (this is e.g. used to flag Step, increment, load case, ...) - set: a single number or a list of numbers identifying the element(s) for which this property will be set, or a set name If None, the property will hold for all elements. - grp: an elements group number (default None). If specified, the element numbers given in set are local to the specified group. If not, elements are global and should match the global numbering according to the order in which element groups will be specified in the Model. - eltype: the element type (currently in Abaqus terms). - section: an ElemSection specifying the element section properties. - dload: an ElemLoad specifying a distributed load on the element. - ampl: the name of an Amplitude """ try: d = {} if eltype is not None: d['eltype'] = eltype.upper() if section is not None: d['section'] = section if dload is not None: d['dload'] = dload if eload is not None: d['eload'] = eload # Currently unchecked! if ampl is not None: d['ampl'] = ampl d.update(kargs) return self.Prop(kind='e',prop=prop,tag=tag,set=set,name=name,**d) except: raise ValueError,"Invalid Elem Property\n tag=%s,set=%s,name=%s,eltype=%s,section=%s,dload=%s,eload=%s" % (tag,set,name,eltype,section,dload,eload) ##################################### Test ########################### if __name__ == "script" or __name__ == "draw": if pf.GUI: chdir(__file__) print(os.getcwd()) P = PropertyDB() Stick = P.Prop(color='green',name='Stick',weight=25,comment='This could be anything: a gum, a frog, a usb-stick,...') print(Stick) author = P.Prop(tag='author',alias='Alfred E Neuman',address=CDict({'street':'Krijgslaan', 'city':'Gent','country':'Belgium'})) print(P.getProp(tag='author')[0]) Stick.weight=30 Stick.length=10 print(Stick) print(author.street) author.street='Voskenslaan' print(author.street) print(author.address.street) author.address.street = 'Wiemersdreef' print(author.address.street) author = P.Prop(tag='author',name='John Doe',address={'city': 'London', 'street': 'Downing Street 10', 'country': 'United Kingdom'}) print(author) for p in P.getProp(rec=[0,2]): print(p.name) for p in P.getProp(tag=['author']): print(p.name) for p in P.getProp(attr=['name']): print(p.nr) P.Prop(set=[0,1,3],name='green_elements',color='green') P.Prop(name='green_elements',transparent=True) a = P.Prop(set=[0,2,4,6],thickness=3.2) P.Prop(name=a.name,material='steel') for p in P.getProp(attr=['name']): print(p) P.Prop(set='green_elements',transparent=False) for p in P.getProp(attr=['name']): if p.name == 'green_elements': print(p.nr,p.transparent) print("before") for p in P.getProp(): print(p) P.getProp(attr=['transparent'],delete=True) P.delProp(attr=['color']) pl = P.getProp(rec=[5,6]) print(pl) P._delete(pl) print("after") for p in P.getProp(): print(p) #exit() times = arange(10) values = square(times) amp = Amplitude(column_stack([times,values])) P.Prop(amplitude=amp,name='amp1') Mat = MaterialDB(getcfg('datadir')+'/materials.db') Sec = SectionDB(getcfg('datadir')+'/sections.db') P.setMaterialDB(Mat) P.setSectionDB(Sec) P1 = [ 1.0,1.0,1.0, 0.0,0.0,0.0 ] P2 = [ 0.0 ] * 3 + [ 1.0 ] * 3 B1 = [ 1 ] + [ 0 ] * 5 CYL = CoordSystem('cylindrical',[0,0,0,0,0,1]) # node property on single node P.nodeProp(1,cload=[5,0,-75,0,0,0]) # node property on nodes 2 and 3 P.nodeProp(set=[2,3],bound='pinned') # node property on ALL nodes P.nodeProp(cload=P1,bound=B1,csys=CYL,ampl='amp1') # node property whose set will be reused nset1 = P.nodeProp(tag='step1',set=[2,3,4],cload=P1).nr # node properties with an already named set P.nodeProp(tag='step2',set=Nset(nset1),cload=P2) print('nodeproperties') print(P.nprop) print('all nodeproperties') print(P.getProp('n')) print("properties 0 and 2") for p in P.getProp('n',rec=[0,2]): print(p) print("tags 1 and step1") for p in P.getProp('n',tag=[1,'step1']): print(p) print("cload attributes") for p in P.getProp('n',attr=['cload']): print(p) # materials and sections ElemSection.matDB = Mat ElemSection.secDB = Sec vert = ElemSection('IPEA100', 'steel') hor = ElemSection({'name':'IPEM800','A':951247,'I':CDict({'Ix':1542,'Iy':6251,'Ixy':352})}, {'name':'S400','E':210,'fy':400}) circ = ElemSection({'name':'circle','radius':10,'sectiontype':'circ'},'steel') print("Materials") for m in Mat: print(Mat[m]) print("Sections") for s in Sec: print(Sec[s]) q1 = ElemLoad('PZ',2.5) q2 = ElemLoad('PY',3.14) top = P.elemProp(set=[0,1,2],eltype='B22',section=hor,dload=q1) column = P.elemProp(eltype='B22',section=vert) diagonal = P.elemProp(eltype='B22',section=hor) bottom = P.elemProp(section=hor,dload=q2,ampl='amp1') print('elemproperties') for p in P.eprop: print(p) print("section properties") for p in P.getProp('e',attr=['section']): print(p.nr) P.Prop(set='cylinder',name='cylsurf',surftype='element',label='SNEG') print P.getProp(attr=['surftype']) # End pyformex-0.8.6/pyformex/plugins/postproc_menu.py0000644000211500021150000006463511705104656022056 0ustar benebene00000000000000# $Id: postproc_menu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## import pyformex as pf from gui import menu from plugins.postproc import * from plugins.fe_post import FeResult from plugins.objects import Objects from gui.colorscale import ColorScale,ColorLegend import utils from odict import ODict import commands from numpy import * from formex import * from gui.draw import * from gui.colors import * from PyQt4 import QtCore class AttributeModel(QtCore.QAbstractTableModel): """A model representing the attributes of an object. """ header = [ 'attribute', 'value', 'is a dict', 'has __dict__', '__class__' ] def __init__(self,name,dic=None,parent=None,*args): QtCore.QAbstractItemModel.__init__(self,parent,*args) if dic is None: dic = gobals() self.dic = dic self.name = name self.obj = dic.get(name,None) keys = dir(self.obj) vals = [ str(getattr(self.obj,k)) for k in keys ] isdict = [ isinstance(self.obj,dict) for k in keys ] has_dict = [ hasattr(self.obj,'__dict__') for k in keys ] has_class = [ getattr(self.obj,'__class__') for k in keys ] self.items = zip(keys,vals,isdict,has_dict,has_class) def rowCount(self,parent): return len(self.items) def columnCount(self,parent): return len(self.header) def data(self,index,role=QtCore.Qt.DisplayRole): if index.isValid() and role == QtCore.Qt.DisplayRole: return QtCore.QVariant(self.items[index.row()][index.column()]) return QtCore.QVariant() def headerData(self,col,orientation=QtCore.Qt.Horizontal,role=QtCore.Qt.DisplayRole): if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: return QtCore.QVariant(AttributeModel.header[col]) return QtCore.QVariant() class DictModel(QtCore.QAbstractTableModel): """A model representing a dictionary.""" header = [ 'key', 'type', 'value' ] def __init__(self,dic,name,parent=None,*args): QtCore.QAbstractItemModel.__init__(self,parent,*args) self.dic = dic self.name = name keys = dic.keys() vals = dic.values() typs = [ str(type(v)) for v in vals ] self.items = zip(keys,typs,vals) #print(self.items) def rowCount(self,parent): return len(self.items) def columnCount(self,parent): return len(self.header) def data(self,index,role=QtCore.Qt.DisplayRole): if index.isValid() and role == QtCore.Qt.DisplayRole: return QtCore.QVariant(self.items[index.row()][index.column()]) return QtCore.QVariant() def headerData(self,col,orientation=QtCore.Qt.Horizontal,role=QtCore.Qt.DisplayRole): if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: return QtCore.QVariant(DictModel.header[col]) return QtCore.QVariant() class Table(QtGui.QDialog): """A dialog widget to show two-dimensional arrays of items.""" def __init__(self,datamodel,caption="pyFormex - Table",parent=None,actions=[('OK',)],default='OK'): """Create the Table dialog. data is a 2-D array of items, mith nrow rows and ncol columns. chead is an optional list of ncol column headers. rhead is an optional list of nrow row headers. """ if parent is None: parent = pf.GUI QtGui.QDialog.__init__(self,parent) self.setWindowTitle(str(caption)) form = QtGui.QVBoxLayout() table = QtGui.QTableView() table.setModel(datamodel) table.horizontalHeader().setVisible(True) table.verticalHeader().setVisible(False) table.resizeColumnsToContents() #print(table.size()) form.addWidget(table) but = widgets.dialogButtons(self,actions,default) form.addLayout(but) self.setLayout(form) #print(table.size()) #print(form.size()) #self.resize(table.size()) self.table = table self.show() self.setSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Minimum) #form.setSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Minimum) table.setSizePolicy(QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Minimum) if globals().has_key('tbl'): if tbl is not None: tbl.close() tbl = None def showfields(): """Show the table of field acronyms.""" global tbl tbl = widgets.Table(result_types.items(),['acronym','description'],actions=[('Cancel',),('Ok',),('Print',tblIndex)]) tbl.show() def tblIndex(): print(tbl.table.currentIndex()) r = tbl.table.currentIndex().row() c = tbl.table.currentIndex().column() print("(%s,%s)" % (r,c)) m = tbl.table.model() p = m.data(m.index(r,c)) print(p,p.toString(),p.toBool()) def showattr(name=None,dic=None): """Show the table of field acronyms.""" global tbl if dic is None: dic = globals() k = dic.keys() sort(k) print(k) if name is None: name = 'dia_full' tbl = AttributeTable(name,dic,actions=[('Cancel',),('Ok',),('Print',tblIndex)]) tbl.show() def showdict(dic,name=None): global tbl model = DictModel(dic,name) tbl = Table(model,caption="Dict '%s'" % name,actions=[('Cancel',),('Ok',),('Print',tblIndex)]) tbl.show() tbl.table.resizeColumnsToContents() tbl.table.updateGeometry() tbl.updateGeometry() #################### def keys(items): """Return the list of keys in items""" return [ i[0] for i in items ] def named_item(items,name): """Return the named item""" n = keys(items).index(name) return items[n] def showModel(nodes=True,elems=True): print DB.elems M = [ Mesh(DB.nodes,el,eltype='quad%d'%el.shape[1],prop=i) for i,el in enumerate(DB.elems.itervalues()) ] if nodes: draw([m.coords for m in M],nolight=True) if elems: draw(M) smoothwire() lights(False) transparent(True) zoomAll() def showResults(nodes,elems,displ,text,val,showref=False,dscale=100., count=1,sleeptime=-1.): """Display a constant or linear field on triangular elements. nodes is an array with nodal coordinates elems is a single element group or a list of elem groups displ are the displacements at the nodes, may be set to None. val are the scalar values at the nodes, may also be None. If not None, displ should have the same shape as nodes and val should have shape (nnodes). If dscale is a list of values, the results will be drawn with subsequent deformation scales, with a sleeptime intermission, and the whole cycle will be repeated count times. """ clear() if displ is not None: # expand displ if it is smaller than nodes # e.g. in 2d returning only 2d displacements n = nodes.shape[1] - displ.shape[1] if n > 0: displ = growAxis(displ,n,axis=1,fill=0.0) if nodes.shape != displ.shape: warning("The displacements do not match the mesh: the mesh coords have shape %s; but the displacements have shape %s. I will continue without displacements." % (nodes.shape,displ.shape)) displ = None if type(elems) != list: elems = [ elems ] print ["ELEMS: %s" % str(el.shape) for el in elems ] if val is not None: print "VAL: %s" % str(val.shape) # draw undeformed structure if showref: ref = [ Mesh(nodes,el,eltype='quad%d'%el.shape[1]) for el in elems ] draw(ref,bbox=None,color='green',linewidth=1,mode='wireframe',nolight=True) # compute the colors according to the values multiplier = 0 if val is not None: if val.shape != (nodes.shape[0],): warning("The values do not match the mesh: there are %s nodes in the mesh, and I got values with shape. I will continue without showing values." % (nodes.shape[0],val.shape)) val = None if val is not None: # create a colorscale and draw the colorlegend vmin,vmax = val.min(),val.max() if vmin*vmax < 0.0: vmid = 0.0 else: vmid = 0.5*(vmin+vmax) scalev = [vmin,vmid,vmax] print scalev logv = [ abs(a) for a in scalev if a != 0.0 ] logs = log10(logv) logma = int(logs.max()) if logma < 0: multiplier = 3 * ((2 - logma) / 3 ) #print("MULTIPLIER %s" % multiplier) CS = ColorScale('RAINBOW',vmin,vmax,vmid,1.,1.) cval = array(map(CS.color,val)) CL = ColorLegend(CS,100) CLA = decors.ColorLegend(CL,20,20,30,200,scale=multiplier) pf.canvas.addDecoration(CLA) # the supplied text if text: if multiplier != 0: text += ' (* 10**%s)' % -multiplier drawText(text,200,30) smooth() lights(False) transparent(False) # create the frames while displaying them dscale = array(dscale) frames = [] # a place to store the drawn frames bboxes = [] if sleeptime >= 0: delay(sleeptime) for dsc in dscale.flat: if displ is None: dnodes = nodes else: dnodes = nodes + dsc * displ deformed = [ Mesh(dnodes,el,eltype='quad%d'%el.shape[1]) for el in elems ] bboxes.append(bbox(deformed)) # We store the changing parts of the display, so that we can # easily remove/redisplay them #print(val) if val is None: F = [ draw(df,color='blue',view=None,bbox='last',wait=False) for df in deformed ] else: print [ df.report() + "\nCOLORS %s" % str(cval[el].shape) for df,el in zip(deformed,elems) ] F = [ draw(df,color=cval[el],view=None,bbox='last',wait=False) for df,el in zip(deformed,elems) ] T = drawText('Deformation scale = %s' % dsc,200,10) # remove the last frame # This is a clever trick: we remove the old drawings only after # displaying new ones. This makes the animation a lot smoother # (though the code is less clear and compact). if len(frames) > 0: for Fi in frames[-1][0]: pf.canvas.removeActor(Fi) pf.canvas.removeDecoration(frames[-1][1]) # add the latest frame to the stored list of frames frames.append((F,T)) wait() zoomBbox(bbox(bboxes)) # display the remaining cycles count -= 1 FA,TA = frames[-1] while count > 0: count -= 1 for F,T in frames: # It would be interesting if addactor would add/remove a list # of actors for Fi in F: pf.canvas.addActor(Fi) pf.canvas.addDecoration(T) for Fi in FA: pf.canvas.removeActor(Fi) pf.canvas.removeDecoration(TA) pf.canvas.display() pf.canvas.update() FA,TA = F,T wait() ############################# PostProc ################################# ## class PostProc(object): ## """A class to visualize Fe Results.""" ## def __init__(self,DB=None): ## """Initialize the PostProc. An Fe Results database may be given.""" ## self.resetAll() ## self.setDB(DB) ## def resetAll(self): ## """Reset settings to defaults""" ## self._show_model = True ## self._show_elems = True ## def postABQ(self,fn=None): ## """Translate an Abaqus .fil file in a postproc script.""" ## types = [ 'Abaqus results file (*.fil)' ] ## fn = askFilename(pf.cfg['workdir'],types) ## if fn: ## chdir(fn) ## name,ext = os.path.splitext(fn) ## post = name+'.post' ## cmd = "%s/lib/postabq %s > %s" % (pf.cfg['pyformexdir'],fn,post) ## sta,out = utils.runCommand(cmd) ## if sta: ## pf.message(out) ## def selectStepInc(self): ## res = askItems([('Step',self.DB.step,'select',self.DB.res.keys())]) ## if res: ## step = int(res['Step']) ## res = askItems([('Increment',None,'select',self.DB.res[step].keys())]) ## if res: ## inc = int(res['Increment']) ## pf.message("Step %s; Increment %s;" % (step,inc)) ## self.DB.setStepInc(step,inc) ################### menu ################# ## class PostProcGui(PostProc): ## def __init__(self,*args,**kargs): ## self.post_button = None ## self._step_combo = None ## self._inc_combo = None ## self.selection = Objects(clas=FeResult) ## PostProc.__init__(self,*args,**kargs) ## def setDB(self,DB=None,name=None): ## """Set the FeResult database. ## DB can either be an FeResult instance or the name of an exported ## FeResult. ## If a name is given, it is displayed on the status bar. ## """ ## if type(DB) == str: ## DB = named(DB) ## if isinstance(DB,FeResult): ## self.DB = DB ## else: ## self.DB = None ## if self.DB: ## # self.hideStepInc() ## # self.hideName() ## self.showName(name) ## self.showStepInc() ## else: ## self.hideName() ## self.hideStepInc() ## def showName(self,name=None): ## """Show a statusbar button with the name of the DB (hide if None).""" ## if name is None: ## self.hideName() ## else: ## if self.post_button is None: ## self.post_button = widgets.ButtonBox('PostDB:',['None'],[self.select]) ## pf.GUI.statusbar.addWidget(self.post_button) ## self.post_button.setText(name) ## def hideName(self): ## """Hide the statusbar button with the name of the DB.""" ## if self.post_button: ## pf.GUI.statusbar.removeWidget(self.post_button) ## def showStepInc(self): ## """Show the step/inc combo boxes""" ## steps = self.DB.getSteps() ## if steps: ## self.step_combo = widgets.ComboBox('Step:',steps,self.setStep) ## pf.GUI.statusbar.addWidget(self.step_combo) ## self.showInc(steps[0]) ## def showInc(self,step=None): ## """Show the inc combo boxes""" ## if step: ## incs = self.DB.getIncs(step) ## self.inc_combo = widgets.ComboBox('Inc:',incs,self.setInc) ## pf.GUI.statusbar.addWidget(self.inc_combo) ## def hideStepInc(self): ## """Hide the step/inc combo boxes""" ## if self._inc_combo: ## pf.GUI.statusbar.removeWidget(self._inc_combo) ## if self._step_combo: ## pf.GUI.statusbar.removeWidget(self._step_combo) ## def setStep(self,i): ## print( "Current index: %s" % i) ## step = str(self.step_combo.combo.input.currentText()) ## if step != self.DB.step: ## print("Current step: %s" % step) ## self.showInc(step) ## inc = self.DB.getIncs(step)[0] ## self.setInc(-1) ## self.DB.setStepInc(step,inc) ## def setInc(self,i): ## inc = str(self.inc_combo.combo.input.currentText()) ## if inc != self.DB.inc: ## self.DB.setStepInc(step,inc) ## print("Current step/inc: %s/%s" % (self.DB.step,self.DB.inc)) ## def select(self,sel=None): ## sel = self.selection.ask1() ## if sel: ## self.setDB(sel,self.selection[0]) ########## Postproc results dialog ####### result_types = ODict([ ('','None'), ('U','[Displacement]'), ('U0','X-Displacement'), ('U1','Y-Displacement'), ('U2','Z-Displacement'), ('S0','X-Normal Stress'), ('S1','Y-Normal Stress'), ('S2','Z-Normal Stress'), ('S3','XY-Shear Stress'), ('S4','XZ-Shear Stress'), ('S5','YZ-Shear Stress'), ('SP0','1-Principal Stress'), ('SP1','2-Principal Stress'), ('SP2','3-Principal Stress'), ('SF0','x-Normal Membrane Force'), ('SF1','y-Normal Membrane Force'), ('SF2','xy-Shear Membrane Force'), ('SF3','x-Bending Moment'), ('SF4','y-Bending Moment'), ('SF5','xy-Twisting Moment'), ('SINV0','Mises Stress'), ('SINV1','Tresca Stress'), ('SINV2','Hydrostatic Pressure'), ('SINV6','Third Invariant'), ('COORD0','X-Coordinate'), ('COORD1','Y-Coordinate'), ('COORD2','Z-Coordinate'), ('Computed','Distance from a point'), ]) input_items = [ dict(name='feresult',text='FE Result DB',value='',itemtype='info'), dict(name='elgroup',text='Element Group',choices=['--ALL--',]), dict(name='restype',text='Type of result',choices=result_types.values()), dict(name='loadcase',text='Load case',value=0), dict(name='autoscale',text='Autocalculate deformation scale',value=True), dict(name='dscale',text='Deformation scale',value=100.), dict(name='showref',text='Show undeformed configuration',value=True), dict(name='animate',text='Animate results',value=False), dict(name='shape',text='Amplitude shape',value='linear',itemtype='radio',choices=['linear','sine']), dict(name='cycle',text='Animation cycle',value='updown',itemtype='radio',choices=['up','updown','revert']), dict(name='count',text='Number of cycles',value=5), dict(name='nframes',text='Number of frames',value=10), dict(name='sleeptime',text='Animation sleeptime',value=0.1), ] selection = Objects(clas=FeResult) dialog = None DB = None def show(): """Show the results""" data = dialog_getresults() #print(data) globals().update(data) nodes = DB.nodes if elgroup == '--ALL--': elems = DB.elems.values() else: elems = [ DB.elems[elgroup] ] dscale = data['dscale'] displ = DB.getres('U') if displ is not None: displ = displ[:,0:3] if autoscale: siz0 = Coords(nodes).sizes() siz1 = Coords(displ).sizes() w = where(siz0 > 0.0)[0] dscale = niceNumber(0.5/(siz1[w]/siz0[w]).max()) if animate: dscale = dscale * frameScale(nframes,cycle=cycle,shape=shape) # Get the scalar element result values from the results. txt = 'No Results' val = None if resindex > 0: key = result_types.keys()[resindex] print("RESULT KEY = %s" % key) if key == 'Computed': if askPoint(): val = Coords(nodes).distanceFromPoint(point) else: val = DB.getres(key) if key == 'U': val = norm2(val) if val is not None: txt = result_types.values()[resindex] showResults(nodes,elems,displ,txt,val,showref,dscale,count,sleeptime) return val def setDB(db): """Set the current result. db is an FeResult instance.""" global DB if isinstance(db,FeResult): DB = db else: DB = None pf.PF['PostProcMenu_result'] = DB def selectDB(db=None): """Select the result database to work upon. If db is an FeResult instance, it is set as the current database. If None is given, a dialog is popped up to select one. If a database is succesfully selected, the screen is cleared and the geometry of the model is displayed. Returns the database or None. """ if not isinstance(db,FeResult): db = selection.ask1() if db: print("Selected results database: %s" % selection.names[0]) if db: setDB(db) clear() print(DB.about.get('heading','No Heading')) print('Stress tensor has %s components' % DB.datasize['S']) showModel() return db def importFlavia(fn=None): """Import a flavia file and select it as the current results. Flavia files are the postprocessing format used by GiD pre- and postprocessor, and can also be written by the FE program calix. There usually are two files named 'BASE.flavia.msh' and 'BASE.flavia.res' which hold the FE mesh and results, respectively. This functions asks the user to select a flavia file (either mesh or results), will then read both the mesh and corrseponding results files, and store the results in a FeResult instance, which will be set as the current results database for the postprocessing menu. """ from plugins.flavia import readFlavia if fn is None: types = [ utils.fileDescription('flavia'), utils.fileDescription('all') ] fn = askFilename(pf.cfg['workdir'],types) if fn: chdir(fn) if fn.endswith('.msh'): meshfile = fn resfile = utils.changeExt(fn,'res') else: resfile = fn meshfile = utils.changeExt(fn,'msh') db = readFlavia(meshfile,resfile) if not isinstance(db,FeResult): warning("!Something went wrong during the import of the flavia database %s" % fn) return ### ok: export and select the DB name = os.path.splitext(os.path.basename(fn))[0].replace('.flavia','') export({name:db}) db.printSteps() print db.R print db.datasize selection.set([name]) selectDB(db) def importDB(fn=None): """Import a _post.py database and select it as the current.""" if fn is None: types = utils.fileDescription('postproc') fn = askFilename(pf.cfg['workdir'],types) if fn: chdir(fn) size = os.stat(fn).st_size if size > 1000000 and ask(""" BEWARE!!! The size of this file is very large: %s bytes It is unlikely that I will be able to process this file. I strongly recommend you to cancel the operation now. """ % size,["Continue","Cancel"]) != "Continue": return # import the results DB play(fn) ### check whether the import succeeded name = FeResult._name_ db = pf.PF[name] if not isinstance(db,FeResult): warning("!Something went wrong during the import of the database %s" % fn) return ### ok: select the DB selection.set([name]) selectDB(db) def checkDB(): """Make sure that a database is selected. If no results database was already selected, asks the user to do so. Returns True if a databases is selected. """ print(DB) if not isinstance(DB,FeResult): selectDB() return isinstance(DB,FeResult) def dialog_getresults(): """Return the dialog data with short keys.""" dialog.acceptData() data = dialog.results data['resindex'] = result_types.values().index(data['restype']) return data def dialog_reset(data=None): # data is a dict with short keys/data if data is None: data = dict((i['name'],i.get('value',None)) for i in input_items) dialog.updateData(data) def open_dialog(): global dialog if not checkDB(): warning("No results database was selected!") return close_dialog() actions = [ ('Close',close_dialog), ('Reset',reset), # ('Select DB',selectDB), ('Show',show), # ('Show Fields',showfields), # ('Show Attr',showattr), ] dialog = widgets.InputDialog(input_items,caption='Results Dialog',actions=actions,default='Show') # Update the data items from saved values try: newdata = named('PostProcMenu_data') print newdata except: newdata = {} pass if selection.check(single=True): newdata['feresult'] = selection.names[0] if DB: newdata['elgroup'] = ['--ALL--',] + DB.elems.keys() dialog.updateData(newdata) dialog.show() #pf.PF['__PostProcMenu_dialog__'] = dialog def close_dialog(): global dialog if dialog: pf.PF['PostProcMenu_data'] = dialog_getresults() dialog.close() dialog = None if tbl: tbl.close() def askPoint(): global point res = askItems([('Point',point)]) if res: point = res['Point'] return point else: return None ################################## Menu ############################# _menu = 'Postproc' def create_menu(): """Create the Postproc menu.""" MenuData = [ # ("&Translate Abaqus .fil to FeResult database",P.postABQ), ("&Read FeResult Database",importDB), ("&Read Flavia Database",importFlavia), ("&Select FeResult Data",selectDB), # ("&Forget FeResult Data",P.selection.forget), ("---",None), ("Show Geometry",showModel), # ("Select Step/Inc",P.selectStepInc), # ("Show Results",P.postProc), ("Results Dialog",open_dialog), ("---",None), ("&Reload menu",reload_menu), ("&Close menu",close_menu), ] return menu.Menu(_menu,items=MenuData,parent=pf.GUI.menu,before='help') def show_menu(): """Show the menu.""" if not pf.GUI.menu.item(_menu): create_menu() def close_menu(): """Close the menu.""" pf.GUI.menu.removeItem(_menu) def reload_menu(): """Reload the Postproc menu.""" global DB close_menu() DB = pf.PF.get('PostProcMenu_result',None) print("Current database %s" % DB) import plugins plugins.refresh('postproc_menu') show_menu() #################################################################### ######### What to do when the script is executed ################### if __name__ == "draw": reload_menu() # End pyformex-0.8.6/pyformex/plugins/centerline.py0000644000211500021150000002477311705104656021310 0ustar benebene00000000000000# $Id: centerline.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Centerline.py Determine the (inner) voronoi diagram of a triangulated surface. Determine approximation for the centerline. """ import pyformex as pf import os from numpy import * from plugins import trisurface,tetgen from utils import runCommand import coords,connectivity def det3(f): """Calculate the determinant of each of the 3 by 3 arrays. f is a (n,3,3) array. The output is 1d array containing the n corresponding determinants. """ det = f[:,0,0]*(f[:,1,1]*f[:,2,2]-f[:,1,2]*f[:,2,1]) - f[:,0,1]*(f[:,1,0]*f[:,2,2]-f[:,1,2]*f[:,2,0]) + f[:,0,2]*(f[:,1,0]*f[:,2,1]-f[:,1,1]*f[:,2,0]) return det def det4(f): """Calculate the determinant of each of the 4 by 4 arrays. f is a (n,4,4) array. The output is 1d array containing the n corresponding determinants. """ det = f[:,0,0]*det3(f[:,1:,1:])-f[:,0,1]*det3(f[:,1:,[0,2,3]])+f[:,0,2]*det3(f[:,1:,[0,1,3]])-f[:,0,3]*det3(f[:,1:,[0,1,2]]) return det def encode2(i,j,n): return n*i+j def decode2(code,n): i,j = code/n, code%n return i,j def circumcenter(nodes,elems): """Calculate the circumcenters of a list of tetrahedrons. For a description of the method: http://mathworld.wolfram.com/Circumsphere.html The output are the circumcenters and the corresponding radii. """ kwadSum = (nodes*nodes).sum(axis=-1) one = zeros(nodes.shape[0])+1 nodesOne = append(nodes,one.reshape(-1,1),1) nodesKwadOne = append(kwadSum.reshape(-1,1),nodesOne,1) #construct necessary 4 by 4 arrays Wx = nodesKwadOne[:,[0,2,3,4]] Wy = nodesKwadOne[:,[0,1,3,4]] Wz = nodesKwadOne[:,[0,1,2,4]] #calculate derminants of the 4 by 4 arrays Dx = det4(Wx[elems]) Dy = -det4(Wy[elems]) Dz = det4(Wz[elems]) alfa = det4(nodesOne[elems[:]]) #circumcenters centers = column_stack([Dx[:]/(2*alfa[:]),Dy[:]/(2*alfa[:]),Dz[:]/(2*alfa[:])]) #calculate radii of the circumscribed spheres vec = centers[:]-nodes[elems[:,0]] radii = sqrt((vec*vec).sum(axis=-1)) return centers,radii ## Voronoi: vor diagram is determined using Tetgen. Some of the vor nodes may fall outside the surface. This should be avoided as this may compromise the centerline determination. Therefore, we created a second definition to determine the inner voronoi diagram (voronoiInner). def voronoi(fn): """Determine the voronoi diagram corresponding with a triangulated surface. fn is the file name of a surface, including the extension (.off, .stl, .gts, .neu or .smesh) The voronoi diagram is determined by Tetgen. The output are the voronoi nodes and the corresponding radii of the voronoi spheres. """ S = trisurface.TriSurface.read(fn) fn,ftype = os.path.splitext(fn) ftype = ftype.strip('.').lower() if ftype != 'smesh': S.write('%s.smesh' %fn) sta,out = runCommand('tetgen -zpv %s.smesh' %fn) #information tetrahedra elems = tetgen.readElems('%s.1.ele' %fn)[0] nodes = tetgen.readNodes('%s.1.node' %fn)[0] #voronoi information nodesVor = tetgen.readNodes('%s.1.v.node' %fn)[0] #calculate the radii of the voronoi spheres vec = nodesVor[:]-nodes[elems[:,0]] radii = sqrt((vec*vec).sum(axis=-1)) return nodesVor,radii def voronoiInner(fn): """Determine the inner voronoi diagram corresponding with a triangulated surface. fn is the file name of a surface, including the extension (.off, .stl, .gts, .neu or .smesh) The output are the voronoi nodes and the corresponding radii of the voronoi spheres. """ S = trisurface.TriSurface.read(fn) fn,ftype = os.path.splitext(fn) ftype = ftype.strip('.').lower() if ftype != 'smesh': S.write('%s.smesh' %fn) sta,out = runCommand('tetgen -zp %s.smesh' %fn) #information tetrahedra elems = tetgen.readElems('%s.1.ele' %fn)[0] nodes = tetgen.readNodes('%s.1.node' %fn)[0].astype(float64) #calculate surface normal for each point elemsS = array(S.elems) NT = S.areaNormals()[1] NP = zeros([nodes.shape[0],3]) for i in [0,1,2]: NP[elemsS[:,i]] = NT #calculate centrum circumsphere of each tetrahedron centers = circumcenter(nodes,elems)[0] #check if circumcenter falls within the geomety described by the surface ie = column_stack([((nodes[elems[:,j]] - centers[:])*NP[elems[:,j]]).sum(axis=-1) for j in [0,1,2,3]]) ie = ie[:,:]>=0 w = where(ie.all(1))[0] elemsInner = elems[w] nodesVorInner = centers[w] #calculate the radii of the voronoi spheres vec = nodesVorInner[:]-nodes[elemsInner[:,0]] radii = sqrt((vec*vec).sum(axis=-1)) return nodesVorInner,radii def selectMaxVor(nodesVor,radii,r1=1.,r2=2.,q=0.7,maxruns=-1): """Select the local maxima of the voronoi spheres. Description of the procedure: 1) The largest voronoi sphere in the record is selected (voronoi node N and radius R). 2) All the voronoi nodes laying within a cube all deleted from the record. This cube is defined by: a) the centrum of the cube N. b) the edge length which is 2*r1*R. 3) Some voronoi nodes laying within a 2nd, larger cube are also deleted. This is when their corresponding radius is smaller than q times R. This cube is defined by: a) the centrum of the cube N. b) the edge length which is 2*r2*R. 4) These three operations are repeated until all nodes are deleted. """ nodesCent = array([]) radCent = array([]) run = 0 while nodesVor.shape[0] and (maxruns < 0 or run < maxruns): #find maximum voronoi sphere in the record w = where(radii[:] == radii[:].max())[0] maxR = radii[w].reshape(-1) maxP = nodesVor[w].reshape(-1) #remove all the nodes within the first cube t1 = (nodesVor[:] > (maxP-r1*maxR)).all(axis=1) t2 = (nodesVor[:] < (maxP+r1*maxR)).all(axis=1) ttot1 = t1*t2 radii = radii[-ttot1] nodesVor = nodesVor[-ttot1] #remove some of the nodes within the second cube t3 = (nodesVor[:] > (maxP-r2*maxR)).all(axis=1) t4 = (nodesVor[:] < (maxP+r2*maxR)).all(axis=1) t5 = (radii (nodes[i]-v*radii[i])).all(axis=2) t2 = (nodes[:] < (nodes[i]+v*radii[i])).all(axis=2) t = t1*t2 t[i] = False w1 = where(t == 1)[0] c = coords.Coords(nodes[w1]) d =c.distanceFromPoint(nodes[i]).reshape(-1) w2 = d < radii[w1] + radii[i] w = w1[w2] for j in w: connections = append(connections,i) connections = append(connections,j) connections = connections.reshape(-1,2) connections = removeDoubles(connections) return connections.reshape(-1,2) def removeTriangles(elems): """Remove the triangles from the centerline. This is a clean-up function for the centerline. Triangles appearing in the centerline are removed by this function. Both input and output are the connectivity of the centerline. """ rev = connectivity.Connectivity(elems).inverse() if rev.shape[1] > 2: w = where(rev[:,-3] != -1)[0] for i in w: el = rev[i].compress(rev[i] != -1) u = unique(elems[el].reshape(-1)) NB = u.compress(u != i) int = intersect1d(w,NB) if int.shape[0] == 2: tri = append(int,i) w1 = where(tri != tri.min())[0] t = (elems[:,0] == tri[w1[0]])*(elems[:,1] == tri[w1[1]]) elems[t] = -1 w2 = where(elems[:,0] != -1)[0] return elems[w2] def centerline(fn): """Determine an approximated centerline corresponding with a triangulated surface. fn is the file name of a surface, including the extension (.off, .stl, .gts, .neu or .smesh) The output are the centerline nodes, an array containing the connectivity information and radii of the voronoi spheres. """ nodesVor,radii= voronoiInner('%s' %fn) nodesC,radii=selectMaxVor(nodesVor,radii) elemsC = connectVorNodes(nodesC,radii) elemsC = removeTriangles(elemsC) return nodesC,elemsC,radii # End pyformex-0.8.6/pyformex/plugins/inertia.py0000644000211500021150000000711211705104656020577 0ustar benebene00000000000000# $Id: inertia.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """inertia.py Compute inertia related quantities of a Formex. This comprises: center of gravity, inertia tensor, principal axes Currently, these functions work on arrays of nodes, not on Formices! Use func(F,f) to operate on a Formex F. """ from numpy import * from arraytools import normalize def centroids(X): """Compute the centroids of the points of a set of elements. X (nelems,nplex,3) """ return X.sum(axis=1) / X.shape[1] def center(X,mass=None): """Compute the center of gravity of an array of points. mass is an optional array of masses to be atributed to the points. The default is to attribute a mass=1 to all points. If you also need the inertia tensor, it is more efficient to use the inertia() function. """ X = X.reshape((-1,X.shape[-1])) if mass is not None: mass = array(mass) ctr = (X*mass).sum(axis=0) / mass.sum() else: ctr = X.mean(axis=0) return ctr def inertia(X,mass=None): """Compute the inertia tensor of an array of points. mass is an optional array of masses to be atributed to the points. The default is to attribute a mass=1 to all points. The result is a tuple of two float arrays: - the center of gravity: shape (3,) - the inertia tensor: shape (6,) with the following values (in order): Ixx, Iyy, Izz, Ixy, Ixz, Iyz """ X = X.reshape((-1,X.shape[-1])) if mass is not None: mass = array(mass) ctr = (X*mass).sum(axis=0) / mass.sum() else: ctr = X.mean(axis=0) Xc = X - ctr x,y,z = Xc[:,0],Xc[:,1],Xc[:,2] xx,yy,zz,yz,zx,xy = x*x, y*y, z*z, y*z, z*x, x*y I = column_stack([ yy+zz, zz+xx, xx+yy, -yz, -zx, -xy ]) if mass is not None: I *= mass return ctr,I.sum(axis=0) def principal(inertia,sort=False,right_handed=False): """Returns the principal values and axes of the inertia tensor. If sort is True, they are sorted (maximum comes first). If right_handed is True, the axes define a right-handed coordinate system. """ Ixx,Iyy,Izz,Iyz,Izx,Ixy = inertia Itensor = array([ [Ixx,Ixy,Izx], [Ixy,Iyy,Iyz], [Izx,Iyz,Izz] ]) Iprin,Iaxes = linalg.eig(Itensor) if sort: s = Iprin.argsort()[::-1] Iprin = Iprin[s] Iaxes = Iaxes[:,s] if right_handed and not allclose(normalize(cross(Iaxes[:,0],Iaxes[:,1])),Iaxes[:,2]): Iaxes[:,2] = -Iaxes[:,2] return Iprin,Iaxes # End pyformex-0.8.6/pyformex/plugins/__init__.py0000644000211500021150000001242011705104656020701 0ustar benebene00000000000000# $Id: __init__.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """pyFormex plugins initialisation. This module contains the functions to detect and load the pyFormex plugin menus. """ import pyformex as pf from types import ModuleType # The following imports help with reading back old projects # (We moved these modules from pluginst to core) import project import mesh def load(plugin): """Load the named plugin""" # import is done here to defer loading until possible __import__('plugins.'+plugin) module = globals().get(plugin,None) if type(module) is ModuleType and hasattr(module,'show_menu'): module.show_menu() def refresh(plugin): """Reload the named plugin""" __import__('plugins.'+plugin) module = globals().get(plugin,None) if type(module) is ModuleType and hasattr(module,'show_menu'): reload(module) else: error("No such module: %s" % plugin) ## def refresh_menu(plugin): ## """Reload the named plugin""" ## module = globals().get(plugin,None) ## if type(module) is ModuleType and hasattr(module,'show_menu'): ## reload(module) ## else: ## print "Type of %s id %s" % (plugin,type(module)) def loaded_modules(): d = [ k for k in globals() if type(globals()[k]) is ModuleType ] d.sort() return d def find_plugin_menus(): """Return a list of plugin menus in the pyFormex 'plugins' directory. """ ## import os ## import utils ## plugindir = os.path.join(pf.cfg['pyformexdir'],'plugins') ## files = utils.listTree(plugindir,listdirs=False,excludedirs=['.*'],includefiles=['.*_menu.py$']) ## files = [ os.path.basename(f) for f in files ] ## files = [ os.path.splitext(f)[0] for f in files ] ## print files ## return files ## # ## # This will be replaced with a directory search ## # # We prefer a fixed order of plugin menus return [ 'geometry_menu', 'formex_menu', 'surface_menu', 'tools_menu', 'draw2d_menu', 'nurbs_menu', 'jobs_menu', 'postproc_menu', ] plugin_menus = find_plugin_menus() def pluginMenus(): """Return a list of plugin name and description. Returns a list of tuples (name,description). The name is the base name of the corresponding module file. The description is the text string displayed to the user. It is a beautified version of the plugin name. """ return [ (name,name.capitalize().replace('_',' ')) for name in plugin_menus ] def create_plugin_menu(parent=None,before=None): from gui import menu loadmenu = menu.Menu('&Load plugins',parent=parent,before=before) loadactions = menu.ActionList(function=load,menu=loadmenu) for name,text in pluginMenus(): loadactions.add(name,icon=None,text=text) return loadactions def loadConfiguredPlugins(ok_plugins=None): if ok_plugins is None: ok_plugins = pf.cfg['gui/plugins'] for p in plugin_menus: if p in ok_plugins: load(p) else: module = globals().get(p,None) if hasattr(module,'close_menu'): module.close_menu() #################### EXPERIMENTAL STUFF BELOW !! ################ import odict _registered_plugins = odict.ODict() def show_menu(name,before='help'): """Show the menu.""" if not pf.GUI.menu.action(_menu): create_menu(before=before) def close_menu(name): """Close the menu.""" name.replace('_menu','') print "CLOSING MENU %s" % name pf.GUI.menu.removeItem(name) def register_plugin_menu(name,menudata,before=['help']): menudata.extend([ ("---",None), # ("&Reload Menu",reload_menu,{'data':name}), ("&Close Menu",close_menu,{'data':name}), ]) w = menu.Menu(name,items=menudata,parent=pf.GUI.menu,before=before[0]) _registered_plugins[name] = w return w def reload_menu(name): """Reload the menu.""" before = pf.GUI.menu.nextitem(_menu) print "Menu %s was before %s" % (_menu,before) close_menu() import plugins plugins.refresh('draw2d') show_menu(before=before) setDrawOptions({'bbox':'last'}) print pf.GUI.menu.actionList() # End pyformex-0.8.6/pyformex/plugins/tetgen.py0000644000211500021150000003067711705104656020446 0ustar benebene00000000000000# $Id: tetgen.py 2150 2012-01-16 20:33:48Z bverheg $ pyformex ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Interface with tetgen A collection of functions to read/write tetgen files and to run the tetgen program tetgen is a quality tetrahedral mesh generator and a 3D Delaunay triangulator. See http://tetgen.org """ from coords import * from formex import Formex from connectivity import Connectivity from mesh import Mesh from filewrite import * import utils import os filetypes = [ 'poly', 'smesh', 'ele', 'face', 'node' ] def readNodeFile(fn): """Read a tetgen .node file. Returns a tuple as described in readNodesBlock. """ fil = open(fn,'r') line = skipComments(fil) npts,ndim,nattr,nbmark = getInts(line,4) return readNodesBlock(fil,npts,ndim,nattr,nbmark) def readEleFile(fn): """Read a tetgen .ele file. Returns a tuple as described in readElemsBlock. """ fil = open(fn,'r') line = skipComments(fil) nelems,nplex,nattr = getInts(line,3) return readElemsBlock(fil,nelems,nplex,nattr) def readFaceFile(fn): """Read a tetgen .face file. Returns a tuple as described in readFacesBlock. """ fil = open(fn,'r') line = skipComments(fil) nelems,nbmark = getInts(line,2) return readFacesBlock(fil,nelems,nbmark) def readSmeshFile(fn): """Read a tetgen .smesh file. Returns an array of triangle elements. """ fil = open(fn,'r') # node section. line = skipComments(fil) npts,ndim,nattr,nbmark = getInts(line,4) if npts > 0: nodeInfo = readNodesBlock(fil,npts,ndim,nattr,nbmark) else: # corresponding .node file nodeInfo = readNodeFile(utils.changeExt(fn,'.node')) # facet section line = skipComments(fil) nelems,nbmark = getInts(line,2) facetInfo = readSmeshFacetsBlock(fil,nelems,nbmark) nodenrs = nodeInfo[1] if nodenrs.min() == 1 and nodenrs.max()==nodenrs.size: elems = facetInfo[0] for e in elems: elems[e] -= 1 # We currently do not read the holes and attributes return nodeInfo[0],facetInfo[0] def readPolyFile(fn): """Read a tetgen .poly file. Returns an array of triangle elements. """ fil = open(fn,'r') # node section. line = skipComments(fil) npts,ndim,nattr,nbmark = getInts(line,4) if npts > 0: nodeInfo = readNodesBlock(fil,npts,ndim,nattr,nbmark) else: # corresponding .node file nodeInfo = readNodeFile(utils.changeExt(fn,'.node')) # facet section line = skipComments(fil) nelems,nbmark = getInts(line,2) facetInfo = readFacetsBlock(fil,nelems,nbmark) print "NEXT LINE:" print line return nodeInfo[0],facetinfo[0] ## s = line.strip('\n').split() ## nelems = int(s[0]) ## elems = fromfile(fil,sep=' ',dtype=int32, count=4*nelems) ## elems = elems.reshape((-1,4)) ## return elems[:,1:] def readSurface(fn): """Read a tetgen surface from a .node/.face file pair. The given filename is either the .node or .face file. Returns a tuple of (nodes,elems). """ nodeInfo = readNodeFile(utils.changeExt(fn,'.node')) nodes = nodeInfo[0] print("Read %s nodes" % nodes.shape[0]) elemInfo = readFaceFile(utils.changeExt(fn,'.face')) elems = elemInfo[0] print("Read %s elems" % elems.shape[0]) #if numbers[0] == 1: # elems -= 1 return nodes,elems ######### Support functions #################################### def skipComments(fil): """Skip comments and blank lines on a tetgen file. Reads from a file until the first non-comment and non-empty line. Then returns the non-empty, non-comment line, stripped from possible trailing comments. Returns None if end of file is reached. """ while True: line = fil.readline() if len(line) == 0: return None # EOF line = stripLine(line) if len(line) > 0: return line # non-comment line found def stripLine(line): """Strip blanks, newline and comments from a line of text. """ nc = line.find('#') if nc >= 0: line = line[:nc] # strip comments return line.strip() # strips blanks and end of line def getInts(line,nint): """Read a number of ints from a line, adding zero for omitted values. line is a string with b;anks separated integer values. Returns a list of nint integers. The trailing ones are set to zero if the strings contains less values. """ s = map(int,line.split()) if len(s) < nint: s.extend([0]*(nint-len(s))) return s def addElem(elems,nrs,e,n,nplex): """Add an element to a collection.""" if not elems.has_key(nplex): elems[nplex] = [] nrs[nplex] = [] elems[nplex].append(e) nrs[nplex].append(n) def readNodesBlock(fil,npts,ndim,nattr,nbmark): """Read a tetgen nodes block. Returns a tuple with: - coords: Coords array with nodal coordinates - nrs: node numbers - attr: node attributes - bmrk: node boundary marker The last two may be None. """ ndata = 1 + ndim + nattr + nbmark data = fromfile(fil,sep=' ',dtype=Float,count=npts*(ndata)).reshape(npts,ndata) nrs = data[:,0].astype(int32) coords = Coords(data[:,1:ndim+1]) if nattr > 0: attr = data[:,1+ndim:1+ndim+nattr].astype(int32) else: attr = None if nbmark == 1: bmark = data[:,-1].astype(int32) else: bmark = None return coords,nrs,attr,bmark def readElemsBlock(fil,nelems,nplex,nattr): """Read a tetgen elems block. Returns a tuple with: - elems: Connectivity of type 'tet4' or 'tet10' - nrs: the element numbers - attr: the element attributes The last can be None. """ ndata = 1 + nplex + nattr data = fromfile(fil,sep=' ',dtype=int32,count=ndata*nelems).reshape(nelems,ndata) nrs = data[:,0] elems = data[:,1:1+nplex] if nattr > 0: attr = data[:,1+nplex:] else: attr = None if nplex == 4: eltype = 'tet4' elif nplex == 10: eltype= 'tet10' else: raise ValueError,"Unknown tetgen .ele plexitude %s" % nplex return Connectivity(elems,eltype=eltype),nrs,attr def readFacesBlock(fil,nelems,nbmark): """Read a tetgen faces block. Returns a a tuple with: - elems: Connectivity of type 'tri3' - nrs: face numbers - bmrk: face boundary marker The last can be None. """ ndata = 1 + 3 + nbmark data = fromfile(fil,sep=' ',dtype=int32, count=ndata*nelems).reshape(nelems,ndata) nrs = data[:,0] elems = data[:,1:4] if nbmark == 1: bmark = data[:,-1] else: bmark = None return Connectivity(elems,eltype='tri3'),nrs,bmark def readSmeshFacetsBlock(fil,nfacets,nbmark): """Read a tetgen .smesh facets bock. Returns a tuple of dictionaries with plexitudes as keys: - elems: for each plexitude a Connectivity array - nrs: for each plexitude a list of element numbers in corresponding elems """ elems = {} nrs = {} for i in range(nfacets): line = fil.readline() line = line.strip() if len(line) > 0: data = fromstring(line,sep=' ',dtype=int32) nplex = data[0] if nplex > 0: e = data[1:1+nplex] # bmark currently not read addElem(elems,nrs,e,i,nplex) else: raise ValueError,"Invalid data line:\n%s" % line for np in elems: if np == 3: eltype= 'tri3' elif np == 4: eltype = 'quad4' else: eltype = None elems[np] = Connectivity(elems[np],eltype=eltype) nrs[np] = array(nrs[np]) return elems,nrs def readNeigh(fn): """Read a tetgen .neigh file. Returns an arrays containing the tetrahedra neighbours: """ fil = open(fn,'r') line = fil.readline() s = line.strip('\n').split() nelems, nneigh = map(int,s) elems = fromfile(fil,sep=' ',dtype=int32).reshape((nelems,nneigh+1)) return elems[:,1:] def writeNodes(fn,coords,offset=0): """Write a tetgen .node file.""" coords = asarray(coords).reshape((-1,3)) fil = open(fn,'w') fil.write("%d %d 0 0\n" % coords.shape) writeIData(coords,fil,"%f ",ind=offset) fil.close() def writeSmesh(fn,facets,coords=None,holes=None,regions=None): """Write a tetgen .smesh file. Currently it only writes the facets of a triangular surface mesh. Coords should be written independently to a .node file. """ fil = open(fn,'w') fil.write("# part 1: node list.\n") if coords is None: fil.write("0 3 0 0 # coords are found in %s.node.\n") fil.write("# part 2: facet list.\n") fil.write("%s 0\n" % facets.shape[0]) for i,n in enumerate(facets): # adding comments breaks fast readback # fil.write("3 %s %s %s # %s\n" % (n[0],n[1],n[2],i)) fil.write("3 %s %s %s\n" % (n[0],n[1],n[2])) fil.write("# part 3: hole list.\n") if holes is None: fil.write("0\n") fil.write("# part 4: region list.\n") if regions is None: fil.write("0\n") fil.write("# Generated by pyFormex\n") def writeSurface(fn,coords,elems): """Write a tetgen surface model to .node and .smesh files. The provided file name is the .node or the .smesh filename. """ writeNodes(utils.changeExt(fn,'.node'),coords) writeSmesh(utils.changeExt(fn,'.smesh'),elems) def nextFilename(fn): """Returns the next file name in a family of tetgen file names.""" m = re.compile("(?P.*)\.(?P\d*)\.(?P.*)").match(fn) if m: return "%s.%s.%s" % (m.group('base'),int(m.group('id'))+1,m.group('ext')) else: return '.1'.join(os.path.splitext(fn)) def runTetgen(fn,options=''): """Run tetgen mesher on the specified file. The input file is a closed triangulated surface. tetgen will generate a volume tetraeder mesh inside the surface, and create a new approximation of the surface as a by-product. """ if os.path.exists(fn): sta,out = utils.runCommand('tetgen -z%s %s' % (options,fn)) def readTetgen(fn): """Read and draw a tetgen file. This is an experimental function for the geometry import menu. """ res = {} base,ext = os.path.splitext(fn) if ext == '.node': nodes = readNodeFile(fn)[0] res['tetgen'+ext] = nodes elif ext in [ '.ele', '.face' ]: nodes,nodenrs = readNodeFile(utils.changeExt(fn,'.node'))[:2] if ext == '.ele': elems = readEleFile(fn)[0] elif ext == '.face': elems = readFaceFile(fn)[0] if nodenrs.min() == 1 and nodenrs.max()==nodenrs.size: elems = elems-1 M = Mesh(nodes,elems,eltype=elems.eltype) res['tetgen'+ext] = nodes elif ext == '.smesh': nodes,elems = readSmeshFile(fn) ML = [ Mesh(nodes,elems[e]) for e in elems ] res = dict([('Mesh-%s'%M.nplex(),M) for M in ML]) elif ext == '.poly': nodes,elems = readPolyFile(fn) ML = [ Mesh(nodes,elems[e]) for e in elems ] res = dict([('Mesh-%s'%M.nplex(),M) for M in ML]) return res if __name__ == 'draw': clear() dir = os.path.dirname(pf.cfg['pyformexdir']) fn = askFilename(filter=utils.fileDescription('tetgen')) if not fn: exit() res = readTetgen(fn) print res draw(res.values()) # End pyformex-0.8.6/pyformex/plugins/geometry_menu.py0000644000211500021150000007510611705104656022033 0ustar benebene00000000000000# $Id: geometry_menu.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """geometry_menu.py This is a pyFormex plugin menu. It is not intended to be executed as a script, but to be loaded into pyFormex using the plugin facility. The geometry menu is intended to become the major interactive geometry menu in pyFormex. """ import pyformex as pf import utils from odict import ODict from geometry import Geometry from geomfile import GeometryFile from formex import Formex from connectivity import Connectivity from mesh import Mesh,mergeMeshes from trisurface import TriSurface from elements import elementType import simple from gui import actors from gui import menu from gui.draw import * from plugins import objects,trisurface,inertia,partition,sectionize,dxf,tetgen,surface_menu import commands, os, timer _name_ = 'geometry_menu' ################### automatic naming of objects ########################## _autoname = {} autoname = _autoname # alias used by some other plugins that still need to be updated def autoName(clas): """Return the autoname class instance for objects of type clas. clas can be either a class instance, a class object, or a string with a class name. The resulting autoname object will generate strings starting with the class name in lower case. >>> F = Formex() >>> print autoName(F).next() formex-000 """ if isinstance(clas,str): name = clas else: try: name = clas.__name__ except: try: name = clas.__class__.__name__ except: raise ValueError,"Expected an instance, class or string" name = name.lower() if not name in _autoname: _autoname[name] = utils.NameSequence(name) return _autoname[name] ##################### selection of objects ########################## # We put these in an init function to allow --testmodule to run without GUI def _init_(): global drawable,selection,drawAll global selection_F,selection_M,selection_TS,selection_PL,selection_NC,setSelection,drawSelection drawable = pf.GUI.drawable selection = pf.GUI.selection['geometry'] drawAll = drawable.draw def set_selection(clas='geometry'): sel = pf.GUI.selection.get(clas) if sel: res = sel.ask() if res is None: warning('Nothing to select') return if not sel.names: message("Nothing selected") # selection.set(sel.names) # selection.draw() ##################### read and write ########################## def readGeometry(filename,filetype=None): """Read geometry from a stored file. This is a wrapper function over several other functions specialized for some file type. Some file types require the existence of more than one file, may need to write intermediate files, or may call external programs. The return value is a dictionary with named geometry objects read from the file. If no filetype is given, it is derived from the filename extension. Currently the following file types can be handled. 'pgf': pyFormex Geometry File. This is the native pyFormex geometry file format. It can store multiple parts of different type, together with their name. 'surface': a global filetype for any of the following surface formats: 'stl', 'gts', 'off', 'stl': """ res = {} if filetype is None: filetype = utils.fileTypeFromExt(filename) print "Reading file of type %s" % filetype if filetype == 'pgf': res = GeometryFile(filename).read() elif filetype in ['surface','stl','off','gts','neu']: surf = TriSurface.read(filename) res = {autoName(TriSurface).next():surf} elif filetype in tetgen.filetypes: res = tetgen.readTetgen(filename) else: error("Can not import from file %s of type %s" % (filename,filetype)) return res def importGeometry(select=True,draw=True,ftype=None): """Read geometry from file. If select is True (default), the imported geometry becomes the current selection. If select and draw are True (default), the selection is drawn. """ if ftype is None: ftype = ['pgf','pyf','surface','off','stl','gts','smesh','neu','all'] else: ftype = [ftype] types = utils.fileDescription(ftype) cur = pf.cfg['workdir'] fn = askFilename(cur=cur,filter=types) if fn: message("Reading geometry file %s" % fn) res = readGeometry(fn) export(res) #selection.readFromFile(fn) print "Items read: %s" % [ "%s(%s)" % (k,res[k].__class__.__name__) for k in res] if select: selection.set(res.keys()) if draw: selection.draw() zoomAll() def importPgf(): importGeometry(ftype='pgf') def importSurface(): importGeometry(ftype='surface') def importTetgen(): importGeometry(ftype='tetgen') def importAny(): importGeometry(ftype=None) def getParams(line): """Strip the parameters from a comment line""" s = line.split() d = {'mode': s.pop(0),'filename': s.pop(0)} d.update(dict(zip(s[::2],s[1::2]))) return d def readNodes(fil): """Read a set of nodes from an open mesh file""" a = fromfile(fil,sep=" ").reshape(-1,3) x = Coords(a) #print(x.shape) return x def readElems(fil,nplex): """Read a set of elems of plexitude nplex from an open mesh file""" print("Reading elements of plexitude %s" % nplex) e = fromfile(fil,sep=" ",dtype=Int).reshape(-1,nplex) e = Connectivity(e) #print(e.shape) return e def readEsets(fil): """Read the eset data of type generate""" data = [] for line in fil: s = line.strip('\n').split() if len(s) == 4: data.append(s[:1]+map(int,s[1:])) return data def readMesh(fn): """Read a nodes/elems model from file. Returns an (x,e) tuple or None """ d = {} pf.GUI.setBusy(True) fil = open(fn,'r') for line in fil: if line[0] == '#': line = line[1:] globals().update(getParams(line)) dfil = open(filename,'r') if mode == 'nodes': d['coords'] = readNodes(dfil) elif mode == 'elems': elems = d.setdefault('elems',[]) e = readElems(dfil,int(nplex)) - int(offset) elems.append(e) elif mode == 'esets': d['esets'] = readEsets(dfil) else: print("Skipping unrecognized line: %s" % line) dfil.close() pf.GUI.setBusy(False) fil.close() return d def importModel(fn=None): """Read one or more element meshes into pyFormex. Models are composed of matching nodes.txt and elems.txt files. A single nodes fliename or a list of node file names can be specified. If none is given, it will be asked from the user. """ if fn is None: fn = askFilename(".","*.mesh",multi=True) if not fn: return if type(fn) == str: fn = [fn] for f in fn: d = readMesh(f) print(type(d)) x = d['coords'] e = d['elems'] modelname = os.path.basename(f).replace('.mesh','') export({modelname:d}) export(dict([("%s-%d"%(modelname,i), Mesh(x,ei)) for i,ei in enumerate(e)])) def readInp(fn=None): """Read an Abaqus .inp file and convert to pyFormex .mesh. """ if fn is None: fn = askFilename(".","*.inp",multi=True) if not fn: return for f in fn: convert_inp(f) return converter = os.path.join(pf.cfg['pyformexdir'],'bin','read_abq_inp.awk') dirname = os.path.dirname(fn) basename = os.path.basename(fn) cmd = 'cd %s;%s %s' % (dirname,converter,basename) print(cmd) pf.GUI.setBusy() print(utils.runCommand(cmd)) pf.GUI.setBusy(False) def importDxf(convert=False,keep=False): """Import a DXF file. The user is asked for the name of a .DXF file. Depending on the parameters, the following processing is done: ======= ===== ================================================ convert keep actions ======= ===== ================================================ False False import DXF entities to pyFormex (default) False True import DXF and save intermediate .dxftext format True any convert .dxf to .dxftext only ======= ===== ================================================ If convert == False, this function returns the list imported DXF entities. """ fn = askFilename(filter=utils.fileDescription('dxf')) if not fn: return pf.GUI.setBusy() text = dxf.readDXF(fn) pf.GUI.setBusy(False) if text: if convert or keep: f = file(utils.changeExt(fn,'.dxftext'),'w') f.write(text) f.close() if not convert: return importDxftext(text) def importSaveDxf(): """Import a DXF file and save the intermediate .dxftext.""" importDxf(keep=True) def convertDxf(): """Read a DXF file and convert to dxftext.""" importDxf(convert=True) def importDxftext(text=None): """Import a dxftext script or file. A dxftext script is a script containing only function calls that generate dxf entities. See :func:`dxf.convertDXF`. - Without parameter, the name of a .dxftext file is asked and the script is read from that file. - If `text` is a single line string, it is used as the filename of the script. - If `text` contains at least one newline character, it is interpreted as the dxf script text. """ if text is None: fn = askFilename(filter=utils.fileDescription('dxftext')) if not fn: return text = open(fn).read() elif '\n' not in text: text = open(text).read() pf.GUI.setBusy() dxf_parts = dxf.convertDXF(text) pf.GUI.setBusy(False) export({'_dxf_import_':dxf_parts}) draw(dxf_parts,color='black') return dxf_parts def writeGeometry(obj,filename,filetype=None,shortlines=False): """Write the geometry items in objdict to the specified file. """ if filetype is None: filetype = utils.fileTypeFromExt(filename) print "Writing file of type %s" % filetype if filetype in [ 'pgf', 'pyf' ]: # Can write anything if filetype == 'pgf': res = writeGeomFile(filename,obj,shortlines=shortlines) else: error("Don't know how to export in '%s' format" % filetype) return res def exportGeometry(types=['pgf','all'],shortlines=False): """Write geometry to file.""" drawable.ask() if not drawable.check(): return filter = utils.fileDescription(types) cur = pf.cfg['workdir'] fn = askNewFilename(cur=cur,filter=filter) if fn: message("Writing geometry file %s" % fn) res = writeGeometry(drawable.odict(),fn,shortlines=shortlines) pf.message("Contents: %s" % res) def exportPgf(): exportGeometry(['pgf']) def exportPgfShortlines(): exportGeometry(['pgf'],shortlines=True) def exportOff(): exportGeometry(['off']) def convertGeometryFile(): """Convert pyFormex geometry file to latest format.""" filter = utils.fileDescription(['pgf','all']) cur = pf.cfg['workdir'] fn = askFilename(cur=cur,filter=filter) if fn: from geomfile import GeometryFile message("Converting geometry file %s to version %s" % (fn,GeometryFile._version_)) GeometryFile(fn).rewrite() ##################### properties ########################## def printDataSize(): for s in selection.names: S = named(s) try: size = S.info() except: size = 'no info available' pf.message("* %s (%s): %s" % (s,S.__class__.__name__,size)) ##################### conversion ########################## def toFormex(suffix=''): """Transform the selected Geometry objects to Formices. If a suffix is given, the Formices are stored with names equal to the object names plus the suffix, else, the original object names will be reused. """ if not selection.check(): selection.ask() if not selection.names: return names = selection.names if suffix: names = [ n + suffix for n in names ] values = [ named(n).toFormex() for n in names ] export2(names,values) clear() selection.draw() def toMesh(suffix=''): """Transform the selected Geometry objects to Meshes. If a suffix is given, the Meshes are stored with names equal to the Formex names plus the suffix, else, the Formex names will be used (and the Formices will thus be cleared from memory). """ if not selection.check(): selection.ask() if not selection.names: return names = selection.names objects = [ named(n) for n in names ] if suffix: names = [ n + suffix for n in names ] print "CONVERTING %s" % names meshes = dict([ (n,o.toMesh()) for n,o in zip(names,objects) if hasattr(o,'toMesh')]) print("Converted %s" % meshes.keys()) export(meshes) selection.set(meshes.keys()) def toSurface(suffix=''): """Transform the selected Geometry objects to TriSurfaces. If a suffix is given, the TriSurfaces are stored with names equal to the Formex names plus the suffix, else, the Formex names will be used (and the Formices will thus be cleared from memory). """ if not selection.check(): selection.ask() if not selection.names: return names = selection.names objects = [ named(n) for n in names ] ok = [ o.nplex()==3 for o in objects ] print ok if not all(ok): warning("Only objects with plexitude 3 can be converted to TriSurface. I can not convert the following objects: %s" % [ n for i,n in zip(ok,names) if not i ]) return if suffix: names = [ n + suffix for n in names ] print "CONVERTING %s" % names surfaces = dict([ (n,TriSurface(o)) for n,o in zip(names,objects)]) print("Converted %s" % surfaces.keys()) export(surfaces) selection.set(surfaces.keys()) ############################################# ### Property functions ############################################# def splitProp(): """Split the selected object based on property values""" from plugins import partition F = selection.check(single=True) if not F: return name = selection[0] partition.splitProp(F,name) ############################################# ### Create Geometry functions ############################################# def convertFormex(F,totype): if totype != 'Formex': F = F.toMesh() if totype == 'TriSurface': F = TriSurface(F) return F base_patterns = [ 'l:1', 'l:2', 'l:12', 'l:127', ] def createGrid(): _data_ = _name_+'createGrid_data' dia = Dialog( items = [ _I('name','__auto__'), _I('object type',choices=['Formex','Mesh','TriSurface']), _I('base',choices=base_patterns), _I('nx',4), _I('ny',2), _I('stepx',1.), _I('stepy',1.), _I('taper',0), _I('bias',0.), ] ) if pf.PF.has_key(_data_): dia.updateData(pf.PF[_data_]) res = dia.getResults() if res: pf.PF[_data_] = res name = res['name'] if name == '__auto__': name = autoName(res['object type']).next() F = Formex(res['base']).replic2( n1=res['nx'], n2=res['ny'], t1=res['stepx'], t2=res['stepy'], bias=res['bias'], taper=res['taper']) F = convertFormex(F,res['object type']) export({name:F}) selection.set([name]) if res['object type'] == 'TriSurface': surface_menu.selection.set([name]) selection.draw() def createRectangle(): _data_ = _name_+'createRectangle_data' dia = Dialog( items = [ _I('name','__auto__'), _I('object type',choices=['Formex','Mesh','TriSurface']), _I('nx',1), _I('ny',1), _I('width',1.), _I('height',1.), _I('bias',0.), _I('diag','up',choices=['none','up','down','x-both']), ] ) if pf.PF.has_key(_data_): dia.updateData(pf.PF[_data_]) res = dia.getResults() if res: pf.PF[_data_] = res name = res['name'] if name == '__auto__': name = autoName(res['object type']).next() F = simple.rectangle( nx=res['nx'],ny=res['ny'], b=res['width'],h=res['height'], bias=res['bias'],diag=res['diag'][0]) F = convertFormex(F,res['object type']) export({name:F}) selection.set([name]) if res['object type'] == 'TriSurface': surface_menu.selection.set([name]) selection.draw() def createCylinder(): _data_ = _name_+'createCylinder_data' dia = Dialog(items=[ _I('name','__auto__'), _I('object type',choices=['Formex','Mesh','TriSurface']), _I('base diameter',1.), _I('top diameter',1.), _I('height',2.), _I('angle',360.), _I('div_along_length',6), _I('div_along_circ',12), _I('bias',0.), _I('diag','up',choices=['none','up','down','x-both']), ], ) if pf.PF.has_key(_data_): dia.updateData(pf.PF[_data_]) res = dia.getResults() if res: pf.PF[_data_] = res name = res['name'] if name == '__auto__': name = autoName(res['object type']).next() F = simple.cylinder( L=res['height'], D=res['base diameter'], D1=res['top diameter'], angle=res['angle'], nt=res['div_along_circ'], nl=res['div_along_length'], bias=res['bias'], diag=res['diag'][0] ) F = convertFormex(F,res['object type']) export({name:F}) selection.set([name]) if res['object type'] == 'TriSurface': surface_menu.selection.set([name]) selection.draw() def createCone(): _data_ = _name_+'createCone_data' res = { 'name' : '__auto__', 'object type':'Formex', 'radius': 1., 'height': 1., 'angle': 360., 'div_along_radius': 6, 'div_along_circ':12, 'diagonals':'up', } if pf.PF.has_key(_data_): res.update(pf.PF[_data_]) res = askItems(store=res, items=[ _I('name'), _I('object type',choices=['Formex','Mesh','TriSurface']), _I('radius'), _I('height'), _I('angle'), _I('div_along_radius'), _I('div_along_circ'), _I('diagonals',choices=['none','up','down']), ]) if res: pf.PF[_data_] = res name = res['name'] if name == '__auto__': name = autoName(res['object type']).next() F = simple.sector(r=res['radius'],t=res['angle'],nr=res['div_along_radius'], nt=res['div_along_circ'],h=res['height'],diag=res['diagonals']) F = convertFormex(F,res['object type']) export({name:F}) selection.set([name]) if res['object type'] == 'TriSurface': surface_menu.selection.set([name]) selection.draw() ############################################# ### Mesh functions ############################################# def narrow_selection(clas): global selection print "BEFORE",selection.names selection.set([n for n in selection.names if isinstance(named(n),clas)]) print "BEFORE",selection.names def fuseMesh(): """Fuse the nodes of a Mesh""" if not selection.check(): selection.ask() narrow_selection(Mesh) if not selection.names: return meshes = [ named(n) for n in selection.names ] res = askItems([ _I('Relative Tolerance',1.e-5), _I('Absolute Tolerance',1.e-5), _I('Shift',0.5), _I('Nodes per box',1)]) if not res: return before = [ m.ncoords() for m in meshes ] meshes = [ m.fuse( rtol = res['Relative Tolerance'], atol = res['Absolute Tolerance'], shift = res['Shift'], nodesperbox = res['Nodes per box'], ) for m in meshes ] after = [ m.ncoords() for m in meshes ] print "Number of points before fusing: %s" % before print "Number of points after fusing: %s" % after names = [ "%s_fused" % n for n in selection.names ] export2(names,meshes) selection.set(names) clear() selection.draw() def divideMesh(): """Create a mesh by subdividing existing elements. """ if not selection.check(): selection.ask() narrow_selection(Mesh) if not selection.names: return meshes = [ named(n) for n in selection.names ] eltypes = set([ m.eltype for m in meshes if m.eltype is not None]) print "eltypes in selected meshes: %s" % eltypes if len(eltypes) > 1: warning("I can only divide meshes with the same element type\nPlease narrow your selection before trying conversion.") return if len(eltypes) == 1: fromtype = eltypes.pop() showInfo("Sorry, this function is not implemented yet!") def convertMesh(): """Transform the element type of the selected meshes. """ if not selection.check(): selection.ask() narrow_selection(Mesh) if not selection.names: return meshes = [ named(n) for n in selection.names ] eltypes = set([ m.eltype.name() for m in meshes if m.eltype is not None]) print "eltypes in selected meshes: %s" % eltypes if len(eltypes) > 1: warning("I can only convert meshes with the same element type\nPlease narrow your selection before trying conversion.") return if len(eltypes) == 1: fromtype = elementType(eltypes.pop()) choices = ["%s -> %s" % (fromtype,to) for to in fromtype.conversions.keys()] if len(choices) == 0: warning("Sorry, can not convert a %s mesh"%fromtype) return res = askItems([ _I('_conversion',itemtype='vradio',text='Conversion Type',choices=choices), _I('_compact',True), _I('_merge',itemtype='hradio',text="Merge Meshes",choices=['None','Each','All']), ]) if res: globals().update(res) print "Selected conversion %s" % _conversion totype = _conversion.split()[-1] names = [ "%s_converted" % n for n in selection.names ] meshes = [ m.convert(totype) for m in meshes ] if _merge == 'Each': meshes = [ m.fuse() for m in meshes ] elif _merge == 'All': print _merge coords,elems = mergeMeshes(meshes) print elems ## names = [ "_merged_mesh_%s" % e.nplex() for e in elems ] ## meshes = [ Mesh(coords,e,eltype=meshes[0].eltype) for e in elems ] ## print meshes[0].elems meshes = [ Mesh(coords,e,m.prop,m.eltype) for e,m in zip(elems,meshes) ] if _compact: print "compacting meshes" meshes = [ m.compact() for m in meshes ] export2(names,meshes) selection.set(names) clear() selection.draw() def renumberMeshInElemsOrder(): """Renumber the selected Meshes in elems order. """ if not selection.check(): selection.ask() narrow_selection(Mesh) if not selection.names: return meshes = [ named(n) for n in selection.names ] names = selection.names meshes = [ M.renumber() for M in meshes ] export2(names,meshes) selection.set(names) clear() selection.draw() ################### menu ################# _menu = 'Geometry' def create_menu(): """Create the plugin menu.""" _init_() MenuData = [ ("&Import ",[ (utils.fileDescription('pgf'),importPgf), (utils.fileDescription('surface'),importSurface), (utils.fileDescription('tetgen'),importTetgen), ("All known geometry formats",importAny), ("Abaqus .inp",[ ("&Convert Abaqus .inp file",readInp), ("&Import Converted Abaqus Model",importModel), ]), ("AutoCAD .dxf",[ ("&Import AutoCAD .dxf",importDxf), ("&Import AutoCAD .dxf and save .dxftext",importSaveDxf), ("&Convert AutoCAD .dxf to .dxftext",convertDxf,dict(tooltip="Parse a .DXF file and output dxftext script.")), ("&Import .dxftext",importDxftext), ]), ('&Upgrade pyFormex Geometry File',convertGeometryFile,dict(tooltip="Convert a pyFormex Geometry File (.pgf) to the latest format, overwriting the file.")), ]), ("&Export ",[ (utils.fileDescription('pgf'),exportPgf), ("pyFormex Geometry File with short lines",exportPgfShortlines), ("Object File Format (.off)",exportOff), ]), ("&Select ",[ ('Any',set_selection), ('Formex',set_selection,{'data':'formex'}), ('Mesh',set_selection,{'data':'mesh'}), ('TriSurface',set_selection,{'data':'surface'}), ('PolyLine',set_selection,{'data':'polyline'}), ('Curve',set_selection,{'data':'curve'}), ('NurbsCurve',set_selection,{'data':'nurbs'}), ]), ("&Draw Selection",selection.draw), ("&Forget Selection",selection.forget), ("---",None), ("Print &Information ",[ ('&Bounding Box',selection.printbbox), ('&Type and Size',printDataSize), ]), ("Toggle &Annotations ",[ ("&Names",selection.toggleNames,dict(checkable=True)), ("&Elem Numbers",selection.toggleNumbers,dict(checkable=True)), ("&Node Numbers",selection.toggleNodeNumbers,dict(checkable=True,checked=selection.hasNodeNumbers())), ("&Free Edges",selection.toggleFreeEdges,dict(checkable=True,checked=selection.hasFreeEdges())), ("&Node Marks",selection.toggleNodes,dict(checkable=True,checked=selection.hasNodeMarks())), ('&Toggle Bbox',selection.toggleBbox,dict(checkable=True)), ]), ("---",None), ("&Convert",[ ("To &Formex",toFormex), ("To &Mesh",toMesh), ("To &TriSurface",toSurface), ## ("To &PolyLine",toPolyLine), ## ("To &BezierSpline",toBezierSpline), ## ("To &NurbsCurve",toNurbsCurve), ]), ("&Property Numbers",[ ("&Set",selection.setProp), ("&Delete",selection.delProp), ("&Split",splitProp), ]), ("&Create Object",[ ('&Grid',createGrid), ('&Rectangle',createRectangle), ('&Cylinder, Cone, Truncated Cone',createCylinder), ('&Circle, Sector, Cone',createCone), ]), ## ("&Shrink",shrink), ## ("&Bbox", ## [('&Show Bbox Planes',showBbox), ## ('&Remove Bbox Planes',removeBbox), ## ]), ## ("&Transform", ## [("&Scale Selection",scaleSelection), ## ("&Scale non-uniformly",scale3Selection), ## ("&Translate",translateSelection), ## ("&Center",centerSelection), ## ("&Rotate",rotateSelection), ## ("&Rotate Around",rotateAround), ## ("&Roll Axes",rollAxes), ## ]), ## ("&Clip/Cut", ## [("&Clip",clipSelection), ## ("&Cut With Plane",cutSelection), ## ]), ## ("&Undo Last Changes",selection.undoChanges), ## ("---",None), ## ("Show &Principal Axes",showPrincipal), ## ("Rotate to &Principal Axes",rotatePrincipal), ## ("Transform to &Principal Axes",transformPrincipal), ## ("---",None), ## ("&Concatenate Selection",concatenateSelection), ## ("&Partition Selection",partitionSelection), ## ("&Create Parts",createParts), ## ("&Sectionize Selection",sectionizeSelection), ## ("---",None), ## ("&Fly",fly), ("Mesh",[ ("&Convert element type",convertMesh), ("&Subdivide",divideMesh), ("&Fuse nodes",fuseMesh), ("&Renumber nodes in element order",renumberMeshInElemsOrder), ]), ("---",None), ("&Reload menu",reload_menu), ("&Close",close_menu), ] M = menu.Menu(_menu,items=MenuData,parent=pf.GUI.menu,before='help') ## if not utils.hasExternal('dxfparser'): ## I = M.item("&Import ").item("AutoCAD .dxf") ## I.setEnabled(False) return M def show_menu(): """Show the Tools menu.""" if not pf.GUI.menu.item(_menu): create_menu() def close_menu(): """Close the Tools menu.""" m = pf.GUI.menu.item(_menu) if m : m.remove() def reload_menu(): """Reload the Postproc menu.""" from plugins import refresh close_menu() refresh('geometry_menu') show_menu() #################################################################### ######### What to do when the script is executed ################### if __name__ == "draw": _init_() reload_menu() # End pyformex-0.8.6/pyformex/plugins/draw2d.py0000644000211500021150000002414711705104656020336 0ustar benebene00000000000000# $Id: draw2d.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Interactive 2D drawing in a 3D space This pyFormex plugin provides some interactive 2D drawing functions. While the drawing operations themselves are in 2D, they can be performed on a plane with any orientation in space. The constructed geometry always has 3D coordinates in the global cartesian coordinate system. """ from simple import circle from odict import ODict from geomtools import triangleCircumCircle from plugins.curve import * from plugins.nurbs import * from plugins.tools_menu import _drawables from plugins import objects from plugins.geometry_menu import autoname,autoName from gui.draw import * draw_mode_2d = ['point','polyline','curve','nurbs','circle'] autoname['point'] = autoName('coords') autoname['curve'] = autoName('bezierspline') autoname['nurbs'] = autoName('nurbscurve') autoname['circle'] = autoName('circle') _preview = False def set_preview(onoff=True): global _preview _preview = onoff def toggle_preview(onoff=None): global _preview if onoff is None: try: onoff = pf.GUI.menu.item(_menu).item('preview').isChecked() except: onoff = not _preview _preview = onoff def draw2D(mode='point',npoints=-1,zplane=0.,coords=None,func=None): """Enter interactive drawing mode and return the line drawing. See viewport.py for more details. This function differs in that it provides default displaying during the drawing operation and a button to stop the drawing operation. The drawing can be edited using the methods 'undo', 'clear' and 'close', which are presented in a combobox. """ if pf.canvas.drawmode is not None: warning("You need to finish the previous drawing operation first!") return if func == None: func = highlightDrawing return pf.canvas.idraw(mode,npoints,zplane,func,coords,_preview) obj_params = {} def drawnObject(points,mode='point'): """Return the geometric object resulting from draw2D points""" minor = None if '_' in mode: mode,minor = mode.split('_') closed = minor=='closed' if mode == 'point': return points elif mode == 'polyline': return PolyLine(points,closed=closed) elif mode == 'curve' and points.ncoords() > 1: curl = obj_params.get('curl',None) closed = obj_params.get('closed',None) return BezierSpline(points,curl=curl,closed=closed) elif mode == 'nurbs': degree = obj_params.get('degree',None) if points.ncoords() <= degree: return None closed = obj_params.get('closed',None) return NurbsCurve(points,degree=degree,closed=closed) elif mode == 'circle' and points.ncoords() % 3 == 0: R,C,N = triangleCircumCircle(points.reshape(-1,3,3)) circles = [circle(r=r,c=c,n=n) for r,c,n in zip(R,C,N)] if len(circles) == 1: return circles[0] else: return circles else: return None def highlightDrawing(points,mode): """Highlight a temporary drawing on the canvas. pts is an array of points. """ from gui import actors pf.canvas.removeHighlights() #print points[-1] PA = actors.GeomActor(Formex(points)) PA.specular=0.0 pf.canvas.addHighlight(PA) obj = drawnObject(points,mode=mode) if obj is not None: if mode == 'nurbs': OA = obj.actor(color=pf.canvas.settings.slcolor) else: if hasattr(obj,'toFormex'): F = obj.toFormex() else: F = Formex(obj) OA = actors.GeomActor(F,color=pf.canvas.settings.slcolor) OA.specular=0.0 pf.canvas.addHighlight(OA) pf.canvas.update() def drawPoints2D(mode,npoints=-1,zvalue=0.,coords=None): """Draw a 2D opbject in the xy-plane with given z-value""" if mode not in draw_mode_2d: return x,y,z = pf.canvas.project(0.,0.,zvalue) return draw2D(mode,npoints=npoints,zplane=z,coords=coords) def drawObject2D(mode,npoints=-1,zvalue=0.,coords=None): """Draw a 2D opbject in the xy-plane with given z-value""" points = drawPoints2D(mode,npoints=npoints,zvalue=zvalue,coords=coords) return drawnObject(points,mode=mode) def selectObject(mode=None): selection = objects.drawAble(like=mode+'-') res = widgets.ListSelection( selection.listAll(), caption='Known %ss' % mode, sort=True).getResult() # UNFINISHED ################################### the_zvalue = 0. def draw_object(mode,npoints=-1): print "z value = %s" % the_zvalue points = drawPoints2D(mode,npoints=-1,zvalue=the_zvalue) if points is None: return print "POINTS %s" % points obj = drawnObject(points,mode=mode) if obj is None: pf.canvas.removeHighlights() return print "OBJECT IS %s" % obj res = askItems([ ('name',autoname[mode].peek(),{'text':'Name for storing the object'}), ('color','blue','color',{'text':'Color for the object'}), ]) if not res: return name = res['name'] color = res['color'] if name == autoname[mode].peek(): autoname[mode].next() export({name:obj}) pf.canvas.removeHighlights() draw(points,color='black',nolight=True) if mode != 'point': draw(obj,color=color,nolight=True) if mode == 'nurbs': print "DRAWING KNOTS" draw(obj.knotPoints(),color=color,marksize=5) return name def draw_points(npoints=-1): return draw_object('point',npoints=npoints) def draw_polyline(): return draw_object('polyline') def draw_curve(): global obj_params res = askItems([('curl',1./3.),('closed',False)]) obj_params.update(res) return draw_object('curve') def draw_nurbs(): global obj_params res = askItems([('degree',3),('closed',False)]) obj_params.update(res) return draw_object('nurbs') def draw_circle(): return draw_object('circle') def objectName(actor): """Find the exported name corresponding to a canvas actor""" if hasattr(actor,'object'): obj = actor.object print "OBJECT",obj for name in pf.PF: print name print named(name) if named(name) is obj: return name return None def splitPolyLine(c): """Interactively split the specified polyline""" pf.options.debug = 1 XA = draw(c.coords,clear=False,bbox='last',nolight=True) pf.canvas.pickable = [XA] #print "ACTORS",pf.canvas.actors #print "PICKABLE",pf.canvas.pickable k = pickPoints(filter='single',oneshot=True) pf.canvas.pickable = None undraw(XA) if k.has_key(0): at = k[0] return c.split(at) else: return [] def split_curve(): k = pickActors(filter='single',oneshot=True) print k print k[-1] if not k.has_key(-1): return nr = k[-1][0] actor = pf.canvas.actors[nr] name = objectName(actor) print "Enter a point to split %s" % name c = named(name) cs = splitPolyLine(c) if len(cs) == 2: draw(cs[0],color='red') draw(cs[1],color='green') _grid_data = [ ['autosize',False], ['dx',1.,{'text':'Horizontal distance between grid lines'}], ['dy',1.,{'text':'Vertical distance between grid lines'}], ['width',100.,{'text':'Horizontal grid size'}], ['height',100.,{'text':'Vertical grid size'}], ['point',[0.,0.,0.],{'text':'Point in grid plane'}], ['normal',[0.,0.,1.],{'text':'Normal on the plane'}], ['lcolor','black','color',{'text':'Line color'}], ['lwidth',1.0,{'text':'Line width'}], ['showplane',False,{'text':'Show backplane'}], ['pcolor','white','color',{'text':'Backplane color'}], ['alpha','0.3',{'text':'Alpha transparency'}], ] def create_grid(): global dx,dy if hasattr(pf.canvas,'_grid'): if hasattr(pf.canvas,'_grid_data'): updateData(_grid_data,pf.canvas._grid_data) res = askItems(_grid_data) if res: pf.canvas._grid_data = res globals().update(res) nx = int(ceil(width/dx)) ny = int(ceil(height/dy)) obj = None if autosize: obj = _drawables.check() if obj: bb = bbox(obj) nx = ny = 20 dx = dy = bb.sizes().max() / nx * 2. ox = (-nx*dx/2.,-ny*dy/2.,0.) if obj: c = bbox(obj).center() ox = c + ox grid = actors.CoordPlaneActor(nx=(nx,ny,0),ox=ox,dx=(dx,dy,0.),linewidth=lwidth,linecolor=lcolor,planes=showplane,planecolor=pcolor,alpha=0.3) remove_grid() drawActor(grid) pf.canvas._grid = grid def remove_grid(): if hasattr(pf.canvas,'_grid'): undraw(pf.canvas._grid) pf.canvas._grid = None def updateData(data,newdata): """Update the input data fields with new data values""" if newdata: for d in data: v = newdata.get(d[0],None) if v is not None: d[1] = v # End pyformex-0.8.6/pyformex/plugins/nurbs.py0000644000211500021150000006545211705104656020310 0ustar benebene00000000000000# $Id: nurbs.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Using NURBS in pyFormex. The :mod:`nurbs` module defines functions and classes to manipulate NURBS curves and surface in pyFormex. """ from coords import * from lib import nurbs from plugins import curve from pyformex import options import olist ########################################################################### ## ## class Coords4 ## ######################### # class Coords4(ndarray): """A collection of points represented by their homogeneous coordinates. While most of the pyFormex implementation is based on the 3D Cartesian coordinates class :class:`Coords`, some applications may benefit from using homogeneous coordinates. The class :class:`Coords4` provides some basic functions and conversion to and from cartesian coordinates. Through the conversion, all other pyFormex functions, such as transformations, are available. :class:`Coords4` is implemented as a float type :class:`numpy.ndarray` whose last axis has a length equal to 4. Each set of 4 values (x,y,z,w) along the last axis represents a single point in 3D space. The cartesian coordinates of the point are obtained by dividing the first three values by the fourth: (x/w, y/w, z/w). A zero w-value represents a point at infinity. Converting such points to :class:`Coords` will result in Inf or NaN values in the resulting object. The float datatype is only checked at creation time. It is the responsibility of the user to keep this consistent throughout the lifetime of the object. Just like :class:`Coords`, the class :class:`Coords4` is derived from :class:`numpy.ndarray`. Parameters `data`: array_like If specified, data should evaluate to an array of floats, with the length of its last axis not larger than 4. When equal to four, each tuple along the last axis represents a ingle point in homogeneous coordinates. If smaller than four, the last axis will be expanded to four by adding values zero in the second and third position and values 1 in the last position. If no data are given, a single point (0.,0.,0.) will be created. `w`: array_like If specified, the w values are used to denormalize the homogeneous data such that the last component becomes w. `dtyp`: data-type The datatype to be used. It not specified, the datatype of `data` is used, or the default :data:`Float` (which is equivalent to :data:`numpy.float32`). `copy`: boolean If ``True``, the data are copied. By default, the original data are used if possible, e.g. if a correctly shaped and typed :class:`numpy.ndarray` is specified. """ def __new__(cls, data=None, w=None, dtyp=Float, copy=False): """Create a new instance of :class:`Coords4`.""" if data is None: # create an empty array ar = ndarray((0,4),dtype=dtyp) else: # turn the data into an array, and copy if requested ar = array(data, dtype=dtyp, copy=copy) if ar.shape[-1] in [3,4]: pass elif ar.shape[-1] in [1,2]: # make last axis length 3, adding 0 values ar = growAxis(ar,3-ar.shape[-1],-1) elif ar.shape[-1] == 0: # allow empty coords objects ar = ar.reshape(0,3) else: raise ValueError,"Expected a length 1,2,3 or 4 for last array axis" # Make sure dtype is a float type if ar.dtype.kind != 'f': ar = ar.astype(Float) # We should now have a float array with last axis 3 or 4 if ar.shape[-1] == 3: # Expand last axis to length 4, adding values 1 ar = growAxis(ar,1,-1,1.0) # Denormalize if w is specified if w is not None: self.deNormalize(w) # Transform 'subarr' from an ndarray to our new subclass. ar = ar.view(cls) return ar def normalize(self): """Normalize the homogeneous coordinates. Two sets of homogeneous coordinates that differ only by a multiplicative constant refer to the same points in cartesian space. Normalization of the coordinates is a way to make the representation of a single point unique. Normalization is done so that the last component (w) is equal to 1. The normalization of the coordinates is done in place. .. warning:: Normalizing points at infinity will result in Inf or NaN values. """ self /= self[...,3:] def deNormalize(self,w): """Denormalizes the homogeneous coordinates. This multiplies the homogeneous coordinates with the values w. w normally is a constant or an array with shape self.shape[:-1] + (1,). It then multiplies all 4 coordinates of a point with the same value, thus resulting in a denormalization while keeping the position of the point unchanged. The denormalization of the coordinates is done in place. If the Coords4 object was normalized, it will have precisely w as its 4-th coordinate value after the call. """ self *= w def toCoords(self): """Convert homogeneous coordinates to cartesian coordinates. Returns a :class:`Coords` object with the cartesian coordinates of the points. Points at infinity (w=0) will result in Inf or NaN value. If there are no points at infinity, the resulting :class:`Coords` point set is equivalent to the :class:`Coords4` one. """ return Coords(self[...,:3] / self[...,3:]) def npoints(self): """Return the total number of points.""" return asarray(self.shape[:-1]).prod() ncoords = npoints def x(self): """Return the x-plane""" return self[...,0] def y(self): """Return the y-plane""" return self[...,1] def z(self): """Return the z-plane""" return self[...,2] def w(self): """Return the w-plane""" return self[...,3] def bbox(self): """Return the bounding box of a set of points. Returns the bounding box of the cartesian coordinates of the object. """ return self.toCoords().bbox() def actor(self,**kargs): """Graphical representation""" return self.toCoords().actor() class Geometry4(object): def scale(self,*args,**kargs): self.coords[...,:3] = Coords(self.coords[...,:3]).scale(*args,**kargs) return self ####################################################### ## NURBS CURVES ## # 3*0 - 2*1 - 3*2 : 8 = 5+3 # nctrl = nparts * degree + 1 # nknots = nctrl + degree + 1 # nknots = (nparts+1) * degree + 2 # # degree nparts nctrl nknots # 2 1 3 6 # 2 2 5 8 # 2 3 7 10 # 2 4 9 12 # 3 1 4 8 # 3 2 7 11 # 3 3 10 14 # 3 4 13 17 # 4 1 5 10 # 4 2 9 14 # 4 3 13 18 # 5 1 6 12 # 5 2 11 17 # 5 3 16 22 # 6 1 7 14 # 6 2 13 20 # 7 1 8 16 # 8 1 9 18 # This should be a valid combination of ntrl/degree # drawing is only done if degree <= 7 class NurbsCurve(Geometry4): """A NURBS curve The Nurbs curve is defined by nctrl control points, a degree (>= 1) and a knot vector with knots = nctrl+degree+1 parameter values. The knots vector should hold nknots values in ascending order. The values are only defined upon a multiplicative constant and will be normalized to set the last value to 1. Sensible default values are constructed automatically by calling :func:`knotVector`. If no knots are given and no degree is specified, the degree is set to the number of control points - 1 if the curve is blended. If not blended, the degree is not set larger than 3. """ # # order (2,3,4,...) = degree+1 = min. number of control points # ncontrol >= order # nknots = order + ncontrol >= 2*order # # convenient solutions: # OPEN: # nparts = (ncontrol-1) / degree # nintern = # def __init__(self,control,degree=None,wts=None,knots=None,closed=False,blended=True): self.closed = closed nctrl = len(control) if degree is None: if knots is None: degree = nctrl-1 if not blended: degree = min(degree,3) else: degree = len(knots) - nctrl -1 if degree <= 0: raise ValueError,"Length of knot vector (%s) must be at least number of control points (%s) plus 2" % (len(knots),nctrl) order = degree+1 control = Coords4(control) if wts is not None: control.deNormalize(wts.reshape(wts.shape[-1],1)) if closed: if knots is None: nextra = degree else: nextra = len(knots) - nctrl - order nextra1 = (nextra+1) // 2 #nextra1 = 0 nextra2 = nextra-nextra1 print "extra %s = %s + %s" % (nextra,nextra1,nextra2) control = Coords4(concatenate([control[-nextra1:],control,control[:nextra2]],axis=0)) nctrl = control.shape[0] if nctrl < order: raise ValueError,"Number of control points (%s) must not be smaller than order (%s)" % (nctrl,order) if knots is None: knots = knotVector(nctrl,degree,blended=blended,closed=closed) else: knots = asarray(knots).ravel() knots = knots / knots[-1] nknots = knots.shape[0] if nknots != nctrl+order: raise ValueError,"Length of knot vector (%s) must be equal to number of control points (%s) plus order (%s)" % (nknots,nctrl,order) self.coords = control self.knots = knots self.degree = degree self.closed = closed def order(self): return len(self.knots)-len(self.coords) def bbox(self): """Return the bounding box of the NURBS curve. """ return self.coords.toCoords().bbox() def pointsAt(self,u): """Return the points on the Nurbs curve at given parametric values. Parameters: `u`: (nu,) shaped float array: parametric values at which a point is to be placed. Returns: (nu,3) shaped Coords with nu points at the specified parametric values. """ ctrl = self.coords.astype(double) knots = self.knots.astype(double) u = asarray(u).astype(double) try: pts = nurbs.curvePoints(ctrl,knots,u) if isnan(pts).any(): print "We got a NaN" raise RuntimeError except: raise RuntimeError,"Some error occurred during the evaluation of the Nurbs curve" if pts.shape[-1] == 4: pts = Coords4(pts).toCoords() else: pts = Coords(pts) return pts def derivatives(self,at,d=1): """Returns the points and derivatives up to d at parameter values at""" if type(at) is int: u = uniformParamValues(at,self.knots[0],self.knots[-1]) else: u = at # sanitize arguments for library call ctrl = self.coords.astype(double) knots = self.knots.astype(double) u = asarray(u).astype(double) d = int(d) try: pts = nurbs.curveDerivs(ctrl,knots,u,d) if isnan(pts).any(): print "We got a NaN" print pts raise RuntimeError except: raise RuntimeError,"Some error occurred during the evaluation of the Nurbs curve" if pts.shape[-1] == 4: pts = Coords4(pts) # When using no weights, ctrl points are Coords4 normalized points, # and the derivatives all have w=0: the points represent directions # We just strip off the w=0. # HOWEVER, if there are weights, not sure what to do. # Points themselves could be just normalized and returned. pts[0].normalize() pts = Coords(pts[...,:3]) else: pts = Coords(pts) return pts def knotPoints(self): """Returns the points at the knot values. The multiplicity of the knots is retained in the points set. """ return self.pointsAt(self.knots) def insertKnots(self,u): """Insert a set of knots in the curve. u is a vector with knot parameter values to be inserted into the curve. The control points are adapted to keep the curve unchanged. Returns a Nurbs curve equivalent with the original but with the specified knot values inserted in the knot vector, and the control points adapted. """ if self.closed: raise ValueError,"insertKnots currently does not work on closed curves" newP,newU = nurbs.curveKnotRefine(self.coords,self.knots,u) return NurbsCurve(newP,degree=self.degree,knots=newU,closed=self.closed) def decompose(self): """Decomposes a curve in subsequent Bezier curves. Returns an equivalent unblended Nurbs. """ X = nurbs.curveDecompose(self.coords,self.knots) return NurbsCurve(X,degree=self.degree,blended=False) def actor(self,**kargs): """Graphical representation""" from gui.actors import NurbsActor return NurbsActor(self,**kargs) ####################################################### ## NURBS Surface ## class NurbsSurface(Geometry4): """A NURBS surface The Nurbs surface is defined as a tensor product of NURBS curves in two parametrical directions u and v. The control points form a grid of (nctrlu,nctrlv) points. The other data are like those for a NURBS curve, but need to be specified as a tuple for the (u,v) directions. The knot values are only defined upon a multiplicative constant, equal to the largest value. Sensible default values are constructed automatically by a call to the knotVector() function. If no knots are given and no degree is specified, the degree is set to the number of control points - 1 if the curve is blended. If not blended, the degree is not set larger than 3. .. warning:: This is a class under development! """ def __init__(self,control,degree=(None,None),wts=None,knots=(None,None),closed=(False,False),blended=(True,True)): self.closed = closed control = Coords4(control) if wts is not None: control.deNormalize(wts.reshape(wts.shape[-1],1)) for d in range(2): nctrl = control.shape[1-d] # BEWARE! the order of the nodes deg = degree[d] kn = knots[d] bl = blended[d] cl = closed[d] if deg is None: if kn is None: deg = nctrl-1 if not bl: deg = min(deg,3) else: deg = len(kn) - nctrl -1 if deg <= 0: raise ValueError,"Length of knot vector (%s) must be at least number of control points (%s) plus 2" % (len(knots),nctrl) order = deg+1 if nctrl < order: raise ValueError,"Number of control points (%s) must not be smaller than order (%s)" % (nctrl,order) if kn is None: kn = knotVector(nctrl,deg,blended=bl,closed=cl) else: kn = asarray(kn).ravel() nknots = kn.shape[0] if nknots != nctrl+order: raise ValueError,"Length of knot vector (%s) must be equal to number of control points (%s) plus order (%s)" % (nknots,nctrl,order) if d == 0: self.uknots = kn else: self.vknots = kn self.coords = control self.degree = degree self.closed = closed def order(self): return (self.uknots.shape[0]-self.coords.shape[1], self.vknots.shape[0]-self.coords.shape[0]) def bbox(self): """Return the bounding box of the NURBS surface. """ return self.coords.toCoords().bbox() def pointsAt(self,u): """Return the points on the Nurbs surface at given parametric values. Parameters: `u`: (nu,2) shaped float array: `nu` parametric values (u,v) at which a point is to be placed. Returns: (nu,3) shaped Coords with `nu` points at the specified parametric values. """ ctrl = self.coords.astype(double) U = self.vknots.astype(double) V = self.uknots.astype(double) u = asarray(u).astype(double) try: pts = nurbs.surfacePoints(ctrl,U,V,u) if isnan(pts).any(): print "We got a NaN" raise RuntimeError except: raise RuntimeError,"Some error occurred during the evaluation of the Nurbs curve" if pts.shape[-1] == 4: pts = Coords4(pts).toCoords() else: pts = Coords(pts) return pts def actor(self,**kargs): """Graphical representation""" from gui.actors import NurbsActor return NurbsActor(self,**kargs) ################################################################ def globalInterpolationCurve(Q,degree=3,strategy=0.5): """Create a global interpolation NurbsCurve. Given an ordered set of points Q, the globalInterpolationCurve is a NURBS curve of the given degree, passing through all the points. Returns a NurbsCurve through the given point set. The number of control points is the same as the number of input points. ..warning: Currently there is the limitation that two consecutive points should not coincide. If they do, a warning is shown and the double points will be removed. The procedure works by computing the control points that will produce a NurbsCurve with the given points occurring at predefined parameter values. The strategy to set this values uses a parameter as exponent. Different values produce (slighly) different curves. Typical values are: 0.0: equally spaced (not recommended) 0.5: centripetal (default, recommended) 1.0: chord length (often used) """ from plugins.curve import PolyLine # set the knot values at the points nc = Q.shape[0] n = nc-1 # chord length d = PolyLine(Q).lengths() if (d==0.0).any(): utils.warn("Your point set appears to contain double points. Currently I cannot handle that. I will skip the doubles and try to go ahead.") Q = concatenate([Q[d!=0.0],Q[-1:]],axis=0) d = PolyLine(Q).lengths() if (d==0.0).any(): raise ValueError,"Double points in the data set are not allowed" # apply strategy d = d ** strategy d = d.cumsum() d /= d[-1] u = concatenate([[0.], d]) #print "u = ",u U,A = nurbs.curveGlobalInterpolationMatrix(Q,u,degree) #print "U = ",U #print "A = ",A P = linalg.solve(A,Q) #print "P = ",P return NurbsCurve(P,knots=U,degree=degree) def uniformParamValues(n,umin=0.0,umax=1.0): """Create a set of uniformly distributed parameter values in a range. Parameters: `n`: int: number of intervals in which the range should be divided. The number of values returned is ``n+1``. `umin`,`umax`: float: start and end value of the interval. Default interval is [0.0..1.0]. Returns: a float array with n+1 equidistant values in the range umin..umax. For n > 0, both of the endpoints are included. For n=0, a single value at the center of the interval will be returned. For n<0, an empty array is returned. Example: >>> uniformParamValues(4).tolist() [0.0, 0.25, 0.5, 0.75, 1.0] >>> uniformParamValues(0).tolist() [0.5] >>> uniformParamValues(-1).tolist() [] >>> uniformParamValues(2,1.5,2.5).tolist() [1.5, 2.0, 2.5] """ if n == 0: return array([0.5*(umax+umin)]) else: return umin + arange(n+1) * (umax-umin) / n def knotVector(nctrl,degree,blended=True,closed=False): """Compute sensible knot vector for a Nurbs curve. A knot vector is a sequence of non-decreasing parametric values. These values define the `knots`, i.e. the points where the analytical expression of the Nurbs curve may change. The knot values are only meaningful upon a multiplicative constant, and they are usually normalized to the range [0.0..1.0]. A Nurbs curve with ``nctrl`` points and of given ``degree`` needs a knot vector with ``nknots = nctrl+degree+1`` values. A ``degree`` curve needs at least ``nctrl = degree+1`` control points, and thus at least ``nknots = 2*(degree+1)`` knot values. To make an open curve start and end in its end points, it needs knots with multiplicity ``degree+1`` at its ends. Thus, for an open blended curve, the default policy is to set the knot values at the ends to 0.0, resp. 1.0, both with multiplicity ``degree+1``, and to spread the remaining ``nctrl - degree - 1`` values equally over the interval. For a closed (blended) curve, the knots are equally spread over the interval, all having a multiplicity 1 for maximum continuity of the curve. For an open unblended curve, all internal knots get multiplicity ``degree``. This results in a curve that is only one time continuously derivable at the knots, thus the curve is smooth, but the curvature may be discontinuous. There is an extra requirement in this case: ``nctrl`` sohuld be a multiple of ``degree`` plus 1. Example: >>> print knotVector(7,3) [ 0. 0. 0. 0. 0.25 0.5 0.75 1. 1. 1. 1. ] >>> print knotVector(7,3,closed=True) [ 0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ] >>> print knotVector(7,3,blended=False) [ 0. 0. 0. 0. 1. 1. 1. 2. 2. 2. 2.] """ nknots = nctrl+degree+1 if closed: knots = uniformParamValues(nknots-1) else: if blended: npts = nknots - 2*degree knots = [0.]*degree + uniformParamValues(npts-1).tolist() + [1.]*degree else: nparts = (nctrl-1) / degree if nparts*degree+1 != nctrl: raise ValueError,"Discrete knot vectors can only be used if the number of control points is a multiple of the degree, plus one." knots = [0.] + [ [float(i)]*degree for i in range(nparts+1) ] + [float(nparts)] knots = olist.flatten(knots) return asarray(knots) def toCoords4(x): """Convert cartesian coordinates to homogeneous `x`: :class:`Coords` Array with cartesian coordinates. Returns a Coords4 object corresponding to the input cartesian coordinates. """ return Coords4(x) Coords.toCoords4 = toCoords4 def pointsOnBezierCurve(P,u): """Compute points on a Bezier curve Parameters: P is an array with n+1 points defining a Bezier curve of degree n. u is a vector with nu parameter values between 0 and 1. Returns: An array with the nu points of the Bezier curve corresponding with the specified parametric values. ERROR: currently u is a single paramtric value! See also: examples BezierCurve, Casteljou """ u = asarray(u).ravel() n = P.shape[0]-1 return Coords.concatenate([ (nurbs.allBernstein(n,ui).reshape(1,-1,1) * P).sum(axis=1) for ui in u ],axis=0) def deCasteljou(P,u): """Compute points on a Bezier curve using deCasteljou algorithm Parameters: P is an array with n+1 points defining a Bezier curve of degree n. u is a single parameter value between 0 and 1. Returns: A list with point sets obtained in the subsequent deCasteljou approximations. The first one is the set of control points, the last one is the point on the Bezier curve. This function works with Coords as well as Coords4 points. """ n = P.shape[0]-1 C = [P] for k in range(n): Q = C[-1] Q = (1.-u) * Q[:-1] + u * Q[1:] C.append(Q) return C def curveToNurbs(B): """Convert a BezierSpline to NurbsCurve""" return NurbsCurve(B.coords,degree=B.degree,closed=B.closed,blended=False) curve.BezierSpline.toNurbs = curveToNurbs def frenet(d1,d2,d3=None): """Returns the 3 Frenet vectors and the curvature. d1,d2 are the first and second derivatives at points of a curve. d3, if given, is the third derivative. Returns 3 normalized vectors along tangent, normal, binormal plus the curvature. if d3 is give, also returns the torsion of the curve. """ l = length(d1) # What to do when l is 0? same as with k? if l.min() == 0.0: print "l is zero at %s" % where(l==0.0)[0] e1 = d1 / l.reshape(-1,1) e2 = d2 - dotpr(d2,e1).reshape(-1,1)*e1 k = length(e2) if k.min() == 0.0: print "k is zero at %s" % where(k==0.0)[0] w = where(k==0.0)[0] # where k = 0: set e2 to mean of previous and following e2 /= k.reshape(-1,1) #e3 = normalize(ddd - dotpr(ddd,e1)*e1 - dotpr(ddd,e2)*e2) e3 = cross(e1,e2) m = dotpr(cross(d1,d2),e3) #print "m",m k = m / l**3 if d3 is None: return e1,e2,e3,k # compute torsion t = dotpr(d1,cross(d2,d3)) / dotpr(d1,d2) return e1,e2,e3,k,t ### End pyformex-0.8.6/pyformex/plugins/fe_post.py0000644000211500021150000002021011705104656020575 0ustar benebene00000000000000# $Id: fe_post.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A postprocessor for ABAQUS output files. The ABAQUS .fil output file can be scanned and translated into a pyFormex script with the 'postabq' command. Use it as follows:: postabq job.fil > job.py Then execute the created script from inside pyFormex. """ import pyformex as pf from arraytools import * from script import export from odict import ODict import re class FeResult(object): _name_ = '__FePost__' re_Skey = re.compile("S[0-5]") re_Ukey = re.compile("U[0-2]") def __init__(self,name=_name_,datasize={'U':3,'S':6,'COORD':3}): self.name = name self.datasize = datasize.copy() self.about = {'creator':pf.Version, 'created':pf.StartTime, } self.modeldone = False self.labels = {} self.nelems = 0 self.nnodes = 0 self.dofs = None self.displ = None self.nodid = None self.nodes = None self.elems = None self.nset = None self.nsetkey = None self.eset = None self.res = None self.hdr = None self.nodnr = 0 self.elnr = 0 def dataSize(self,key,data): if self.datasize.has_key(key): return self.datasize[key] else: return len(data) def Abqver(self,version): self.about.update({'abqver':version}) def Date(self,date,time): self.about.update({'abqdate':date,'abqtime':time}) def Size(self,nelems,nnodes,length): self.nelems = nelems self.nnodes = nnodes self.length = length self.nodid = -ones((nnodes,),dtype=int32) self.nodes = zeros((nnodes,3),dtype=float32) self.elems = {} self.nset = {} self.eset = {} def Dofs(self,data): self.dofs = array(data) self.displ = self.dofs[self.dofs[:6] > 0] if self.displ.max() > 3: self.datasize['U'] = 6 def Heading(self,head): self.about.update({'heading':head}) def Node(self,nr,coords,normal=None): self.nodid[self.nodnr] = nr nn = len(coords) self.nodes[self.nodnr][:nn] = coords self.nodnr += 1 def Element(self,nr,typ,conn): if not self.elems.has_key(typ): self.elems[typ] = [] self.elems[typ].append(conn) def Nodeset(self,key,data): self.nsetkey = key self.nset[key] = asarray(data) def NodesetAdd(self,data): self.nset[self.nsetkey] = union1d(self.nset[self.nsetkey],asarray(data)) def Elemset(self,key,data): self.esetkey = key self.eset[key] = asarray(data) def ElemsetAdd(self,data): self.eset[self.esetkey] = union1d(self.eset[self.esetkey],asarray(data)) def Finalize(self): self.nid = inverseUniqueIndex(self.nodid) for k in self.elems.iterkeys(): v = asarray(self.elems[k]) self.elems[k] = asarray(self.nid[v]) self.modeldone = True # we use lists, to keep the cases in order self.res = ODict() self.step = None self.inc = None def Increment(self,step,inc,**kargs): if not self.modeldone: self.Finalize() if step != self.step: if step not in self.res.keys(): self.res[step] = ODict() self.step = step self.inc = None res = self.res[self.step] if inc != self.inc: if inc not in res.keys(): res[inc] = {} self.inc = inc self.R = self.res[self.step][self.inc] def EndIncrement(self): if not self.modeldone: self.Finalize() self.step = self.inc = -1 def Label(self,tag,value): self.labels[tag] = value def NodeOutput(self,key,nodid,data): if not self.R.has_key(key): self.R[key] = zeros((self.nnodes,self.dataSize(key,data)),dtype=float32) if key == 'U': self.R[key][nodid-1][self.displ-1] = data elif key == 'S': n1 = self.hdr['ndi'] n2 = self.hdr['nshr'] ind = arange(len(data)) ind[n1:] += (3-n1) #print(ind) self.R[key][nodid-1][ind] = data else: self.R[key][nodid-1][:len(data)] = data def ElemHeader(self,**kargs): self.hdr = dict(**kargs) def ElemOutput(self,key,data): if self.hdr['loc'] == 'na': self.NodeOutput(key,self.hdr['i'],data) def Export(self): """Align on the last increment and export results""" try: self.step = self.res.keys()[-1] self.inc = self.res[self.step].keys()[-1] self.R = self.res[self.step][self.inc] except: self.step = None self.inc = None self.R = None export({self.name:self, self._name_:self}) pf.message("Read %d nodes, %d elements" % (self.nnodes,self.nelems)) if self.res is None: pf.message("No results") else: pf.message("Steps: %s" % self.res.keys()) def do_nothing(*arg,**kargs): """A do nothing function to stand in for as yet undefined functions.""" pass TotalEnergies = do_nothing OutputRequest = do_nothing Coordinates = do_nothing Displacements = do_nothing Unknown = do_nothing def setStepInc(self,step,inc): try: self.step = step self.inc = inc self.R = self.res[self.step][self.inc] except: self.R = {} def getSteps(self): """Return all the step keys.""" return self.res.keys() def getIncs(self,step): """Return all the incs for given step.""" if self.res.has_key(step): return self.res[step].keys() def getres(self,key,domain='nodes'): """Return the results of the current step/inc for given key. The key may include a component to return only a single column of a multicolumn value. """ components = '012' if self.re_Skey.match(key): if self.datasize['S']==3: components = '013' else: components = '012345' elif self.re_Ukey.match(key): if self.datasize['U']==2: components = '01' else: components = '012' comp = components.find(key[-1]) if comp >= 0: key = key[:-1] if self.R.has_key(key): val = self.R[key] if comp in range(val.shape[1]): return val[:,comp] else: return val else: return None def printSteps(self): """Print the steps/increments/resultcodes for which we have results.""" if self.res is not None: for i,step in self.res.iteritems(): for j,inc in step.iteritems(): for k,v in inc.iteritems(): print("Step %s, Inc %s, Res %s (%s)" % (i,j,k,str(v.shape))) #End pyformex-0.8.6/pyformex/elements.py0000644000211500021150000007072411705104656017310 0ustar benebene00000000000000# $Id: elements.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Definition of elements. This modules allows for a consistent local numbering scheme of element connectivities throughout pyFormex. When interfacing with other programs, one should be aware that conversions may be necessary. Conversions to/from external programs should be done by the interface modules. """ import pyformex from coords import Coords from connectivity import Connectivity from numpy import array,arange from odict import ODict def _sanitize(ent): # input is Connectivity or (eltype,table) # output is Connectivity if isinstance(ent,Connectivity): return ent else: return Connectivity(ent[1],eltype=ent[0]) class Element(object): """Element base class: an empty element. All derived classes should have a capitalized name: starting with an uppercase character and further only lower case and digits. Each element is defined by the following attributes: - `vertices`: the natural coordinates of its vertices, - `edges`: a list of edges, each defined by 2 or 3 node numbers, - `faces`: a list of faces, each defined by a list of minimum 3 node numbers, - `element`: a list of all node numbers - `drawfaces`: a list of faces to be drawn, if different from faces. This is an optional attribute. If defined, it will be used instead of the `faces` attribute to draw the element. This can e.g. be used to draw approximate representations for higher order elements for which there is no correct drawing function. The vertices of the elements are defined in a unit space [0,1] in each axis direction. The elements guarantee a fixed local numbering scheme of the vertices. One should however not rely on a specific numbering scheme of edges, faces or elements. For solid elements, it is guaranteed that the vertices of all faces are numbered in a consecutive order spinning positively around the outward normal on the face. The list of available element types can be found by: >>> printElementTypes() Available Element Types: 0-dimensional elements: ['point'] 1-dimensional elements: ['line2', 'line3'] 2-dimensional elements: ['tri3', 'tri6', 'quad4', 'quad6', 'quad8', 'quad9'] 3-dimensional elements: ['tet4', 'tet10', 'tet14', 'tet15', 'wedge6', 'hex8', 'hex16', 'hex20', 'icosa'] Optional attributes: - `conversions`: Defines possible strategies for conversion of the element to other element types. It is a dictionary with the target element name as key, and a list of actions as value. Each action in the list consists of a tuple ( action, data ), where action is one of the action identifier characters defined below, and data are the data needed for this action. Conversion actions: 'm': add new nodes to the element by taking the mean values of existing nodes. data is a list of tuples containing the nodes numbers whose coorrdinates have to be averaged. 's': select nodes from the existing ones. data is a list of the node numbers to retain in the new element. This can be used to reduce the plexitude but also just to reorder the existing nodes. 'v': perform a conversion via an intermediate type. data is the name of the intermediate element type. The current element will first be converted to the intermediate type, and then conversion from that type to the target will be attempted. 'r': randomly choose one of the possible conversions. data is a list of element names. This can e.g. be used to select randomly between different but equivalent conversion paths. """ proposed_changes = """.. Proposed changes in the Element class ===================================== - nnodes = nvertices - nodal coordinates are specified as follows: - in symmetry directions: between -1 and +1, centered on 0 - in non-symmetry directions: between 0 and +1, aligned on 0 - getCoords() : return coords as is - getAlignedCoords(): return coords between 0 ad 1 and aligned on 0 in all directions - Make the elements subclasses instead of Element class instances? """ collection = ODict() # the full collection with all defined elements def __init__(self,name,doc,ndim,vertices,edges=('',[]),faces=('',[]),**kargs): self.doc = doc self.ndim = ndim self.vertices = Coords(vertices) self.edges = _sanitize(edges) self.faces = _sanitize(faces) for a in [ 'drawedges', 'drawedges2', 'drawfaces', 'drawfaces2']: if a in kargs: setattr(self,a, [ _sanitize(e) for e in kargs[a] ]) if 'reversed' in kargs: self.reversed = kargs['reversed'] # add the element to the collection name = name.lower() Element.collection[name] = self def nplex(self): return self.vertices.shape[0] nvertices = nplex nnodes = nplex def nedges(self): return self.edges.nelems() def nfaces(self): return self.faces.nelems() def getPoints(self): return self.getEntities(0) def getEdges(self): return self.getEntities(1) def getFaces(self): return self.getEntities(2) def getCells(self): return self.getEntities(3) def getElement(self): return self.getEntities(self.ndim) def getEntities(self,level,reduce=False): """Return the type and connectivity table of some element entities. The full list of entities with increasing dimensionality 0,1,2,3 is:: ['points', 'edges', 'faces', 'cells' ] If level is negative, the dimensionality returned is relative to the highest dimensionality (.i.e., that of the element). If it is positive, it is taken absolute. Thus, for a 3D element type, getEntities(-1) returns the faces, while for a 2D element type, it returns the edges. For both types however, getLowerEntities(+1) returns the edges. The return value is a dict where the keys are element types and the values are connectivity tables. If reduce == False: there will be only one connectivity table and it may include degenerate elements. If reduce == True, an attempt is made to reduce the degenerate elements. The returned dict may then have multiple entries. If the requested entity level is outside the range 0..ndim, the return value is None. """ if level < 0: level = self.ndim + level if level < 0 or level > self.ndim: return Connectivity() if level == 0: return Connectivity(arange(self.nplex()).reshape((-1,1)),eltype='point') elif level == self.ndim: return Connectivity(arange(self.nplex()).reshape((1,-1)),eltype=self.name()) elif level == 1: return self.edges elif level == 2: return self.faces def getDrawEdges(self,quadratic=False): if quadratic and hasattr(self,'drawedges2'): return self.drawedges2 if not hasattr(self,'drawedges'): self.drawedges = self.getEdges().reduceDegenerate() return self.drawedges def getDrawFaces(self,quadratic=False): #print "QUADRATIC %s" % quadratic if quadratic and hasattr(self,'drawfaces2'): #print self.drawfaces2 return self.drawfaces2 if not hasattr(self,'drawfaces'): self.drawfaces = self.getFaces().reduceDegenerate() return self.drawfaces def toFormex(self): from formex import Formex x = self.vertices e = self.getElement() return Formex(x[e],eltype=e.eltype) def toMesh(self): from mesh import Mesh x = self.vertices e = self.getElement() return Mesh(x,e,eltype=e.eltype) def name(self): for k,v in Element.collection.items(): if v == self: return k return 'unregistered_element' def __str__(self): return self.name() def report(self): print("Element %s: ndim=%s, nplex=%s, nedges=%s, nfaces=%s" % (self.name(),self.ndim,self.nplex(),self.nedges(),self.nfaces())) @classmethod def listAll(clas): return Element.collection.keys() @classmethod def printAll(clas): for k,v in Element.collection.items(): print("Element %s: ndim=%s, nplex=%s, nedges=%s, nfaces=%s" % (k,v.ndim,v.nplex(),v.nedges(),v.nfaces())) ##################################################### # Define the collection of default pyFormex elements Point = Element( 'point',"A single point", ndim = 0, vertices = [ ( 0.0, 0.0, 0.0 ) ], ) Line2 = Element( 'line2',"A 2-node line segment", ndim = 1, vertices = [ ( 0.0, 0.0, 0.0 ), ( 1.0, 0.0, 0.0 ), ], ) Line3 = Element( 'line3',"A 3-node quadratic line segment", ndim = 1, vertices = [ ( 0.0, 0.0, 0.0 ), ( 0.5, 0.0, 0.0 ), ( 1.0, 0.0, 0.0 ), ], ) ######### 2D ################### Tri3 = Element( 'tri3',"A 3-node triangle", ndim = 2, vertices = [ ( 0.0, 0.0, 0.0 ), ( 1.0, 0.0, 0.0 ), ( 0.0, 1.0, 0.0 ), ], edges = ('line2', [ (0,1), (1,2), (2,0) ]) ) Tri6 = Element( 'tri6',"A 6-node triangle", ndim = 2, vertices = [ ( 0.0, 0.0, 0.0 ), ( 1.0, 0.0, 0.0 ), ( 0.0, 1.0, 0.0 ), ( 0.5, 0.0, 0.0 ), ( 0.5, 0.5, 0.0 ), ( 0.0, 0.5, 0.0 ), ], edges = ('line3', [ (0,3,1), (1,4,2), (2,5,0) ], ), reversed = (2,1,0,4,3,5), drawfaces = [('tri3', [ (0,3,5),(3,1,4),(4,2,5),(3,4,5) ] )] ) Quad4 = Element( 'quad4',"A 4-node quadrilateral", ndim = 2, vertices = [ ( 0.0, 0.0, 0.0 ), ( 1.0, 0.0, 0.0 ), ( 1.0, 1.0, 0.0 ), ( 0.0, 1.0, 0.0 ), ], edges = ('line2', [ (0,1), (1,2), (2,3), (3,0) ], ), ) Quad6 = Element( 'quad6',"A 6-node quadrilateral", ndim = 2, vertices = Coords.concatenate([ Quad4.vertices, [ ( 0.5, 0.0, 0.0 ), ( 0.5, 1.0, 0.0 ), ]]), edges = ('line3', [ (0,4,1), (1,1,2), (2,5,3), (3,3,0) ] ), reversed = (3,2,1,0,5,4), drawedges = [ ('line2', [(1,2), (3,0)]), ('line3', [(0,4,1), (2,5,3)]) ], # drawfaces = [('tri3',[(0,4,3),(4,5,3),(4,1,5),(1,2,5)])] drawfaces = [('quad4',[(0,4,5,3),(2,5,4,1)], )], ) Quad8 = Element( 'quad8',"A 8-node quadrilateral", ndim = 2, vertices = Coords.concatenate([ Quad4.vertices, [ ( 0.5, 0.0, 0.0 ), ( 1.0, 0.5, 0.0 ), ( 0.5, 1.0, 0.0 ), ( 0.0, 0.5, 0.0 ), ]]), edges = ('line3',[ (0,4,1), (1,5,2), (2,6,3), (3,7,0), ]), reversed = (3,2,1,0,6,5,4,7), # drawfaces = [('tri3', [(0,4,7), (1,5,4), (2,6,5), (3,7,6), (4,5,6), (4,6,7) ], )], drawfaces = [('tri3', [(0,4,7), (1,5,4), (2,6,5), (3,7,6)]), ('quad4', [(4,5,6,7)], )], drawfaces2 = [('quad8', [(0,1,2,3,4,5,6,7)], )], ) Quad9 = Element( 'quad9',"A 9-node quadrilateral", ndim = 2, vertices = Coords.concatenate([ Quad8.vertices, [ ( 0.5, 0.5, 0.0 ), ]]), edges = Quad8.edges, reversed = (3,2,1,0,6,5,4,7,8), # drawfaces = [('tri3', [(0,4,8),(4,1,8),(1,5,8),(5,2,8),(2,6,8),(6,3,8),(3,7,8),(7,0,8) ], )], drawfaces = [('quad4', [(0,4,8,7),(1,5,8,4),(2,6,8,5),(3,7,8,6) ], )], drawfaces2 = [('quad9', [(0,1,2,3,4,5,6,7,8)], )], ) ######### 3D ################### Tet4 = Element( 'tet4',"A 4-node tetrahedron", ndim = 3, vertices = [ ( 0.0, 0.0, 0.0 ), ( 1.0, 0.0, 0.0 ), ( 0.0, 1.0, 0.0 ), ( 0.0, 0.0, 1.0 ), ], edges = ('line2', [ (0,1), (1,2), (2,0), (0,3), (1,3), (2,3) ], ), faces = ('tri3', [ (0,2,1), (0,1,3), (1,2,3), (2,0,3) ], ), reversed = (0,1,3,2), ) Tet10 = Element( 'tet10',"A 10-node tetrahedron", ndim = 3, vertices = [ ( 0.0, 0.0, 0.0 ), ( 1.0, 0.0, 0.0 ), ( 0.0, 1.0, 0.0 ), ( 0.0, 0.0, 1.0 ), ( 0.5, 0.0, 0.0 ), ( 0.0, 0.5, 0.0 ), ( 0.0, 0.0, 0.5 ), ( 0.5, 0.5, 0.0 ), ( 0.0, 0.5, 0.5 ), ( 0.5, 0.0, 0.5 ), ], edges = ('line3', [ (0,4,1),(1,7,2),(2,5,0),(0,6,3),(1,9,3),(2,8,3) ],), # BV: This needs further specification! faces = Tet4.faces, reversed = (0,1,3,2,4,6,5,9,8,7), ) Tet14 = Element( 'tet14',"A 14-node tetrahedron", ndim = 3, vertices = Coords.concatenate([ Tet10.vertices, [ ( 1./3., 1./3., 0.0 ), ( 0.0, 1./3., 1./3. ), ( 1./3., 0.0, 1./3. ), ( 1./3., 1./3., 1./3. ), ]]), edges = Tet10.edges, # BV: This needs further specification! faces = Tet4.faces, reversed = (0,1,3,2,4,6,5,9,8,7,12,11,10,13), ) Tet15 = Element( 'tet15',"A 15-node tetrahedron", ndim = 3, vertices = Coords.concatenate([ Tet14.vertices, [ ( 0.25, 0.25, 0.25 ), ]]), edges = Tet10.edges, # BV: This needs further specification! faces = Tet4.faces, reversed = (0,1,3,2,4,6,5,9,8,7,12,11,10,13,14), ) Wedge6 = Element( 'wedge6',"A 6-node wedge element", ndim = 3, vertices = [ ( 0.0, 0.0, 1.0 ), ( 1.0, 0.0, 1.0 ), ( 0.0, 1.0, 1.0 ), ( 0.0, 0.0,-1.0 ), ( 1.0, 0.0,-1.0 ), ( 0.0, 1.0,-1.0 ), ], edges = ('line2', [ (0,1), (1,2), (2,0), (0,3), (1,4), (2,5), (3,4), (4,5), (5,3) ], ), faces = ('quad4', [ (0,1,1,2), (3,5,5,4), (0,3,4,1), (1,4,5,2), (2,5,3,0) ], ), reversed = (3,4,5,0,1,2), drawfaces = [ ('tri3', [ (0,1,2), (3,5,4)] ), ('quad4', [(0,3,4,1), (1,4,5,2), (2,5,3,0) ], )] ) Hex8 = Element( 'hex8',"An 8-node hexahedron", ndim = 3, vertices = [ ( 0.0, 0.0, 0.0 ), ( 1.0, 0.0, 0.0 ), ( 1.0, 1.0, 0.0 ), ( 0.0, 1.0, 0.0 ), ( 0.0, 0.0, 1.0 ), ( 1.0, 0.0, 1.0 ), ( 1.0, 1.0, 1.0 ), ( 0.0, 1.0, 1.0 ), ], edges = ('line2',[ (0,1), (1,2), (2,3), (3,0), (4,5), (5,6), (6,7), (7,4), (0,4), (1,5), (2,6), (3,7) ], ), faces = ('quad4', [ (0,4,7,3), (1,2,6,5), (0,1,5,4), (3,7,6,2), (0,3,2,1), (4,5,6,7) ], ), reversed = (4,5,6,7,0,1,2,3), ) Hex16 = Element( 'hex16',"A 16-node hexahedron", ndim = 3, vertices = Coords.concatenate([ Hex8.vertices, [( 0.5, 0.0, 0.0 ), ( 1.0, 0.5, 0.0 ), ( 0.5, 1.0, 0.0 ), ( 0.0, 0.5, 0.0 ), ( 0.5, 0.0, 1.0 ), ( 1.0, 0.5, 1.0 ), ( 0.5, 1.0, 1.0 ), ( 0.0, 0.5, 1.0 ), ]]), edges = ('line3', [ (0,8,1), (1,9,2), (2,10,3),(3,11,0), (4,12,5),(5,13,6),(6,14,7),(7,15,4), (0,0,4),(1,1,5),(2,2,6),(3,3,7) ], ), faces = ('quad8', [ (0,4,7,3,0,15,7,11), (1,2,6,5,9,2,13,5), (0,1,5,4,8,1,12,4), (3,7,6,2,3,14,6,10), (0,3,2,1,11,10,9,8), (4,5,6,7,12,13,14,15) ], ), reversed= (4,5,6,7,0,1,2,3,12,13,14,15,8,9,10,11), drawedges = [ Hex8.edges ], drawfaces = [ Hex8.faces ] ) Hex20 = Element( 'hex20',"A 20-node hexahedron", ndim = 3, vertices = Coords.concatenate([ Hex16.vertices, [( 0.0, 0.0, 0.5 ), ( 1.0, 0.0, 0.5 ), ( 1.0, 1.0, 0.5 ), ( 0.0, 1.0, 0.5 ) ]]), edges = ('line3',[ (0,8,1), (1,9,2), (2,10,3),(3,11,0), (4,12,5),(5,13,6),(6,14,7),(7,15,4), (0,16,4),(1,17,5),(2,18,6),(3,19,7) ],), faces = ('quad8',[ (0,4,7,3,16,15,19,11), (1,2,6,5,9,18,13,17), (0,1,5,4,8,17,12,16), (3,7,6,2,19,14,18,10), (0,3,2,1,11,10,9,8), (4,5,6,7,12,13,14,15) ], ), reversed = (4,5,6,7,0,1,2,3,12,13,14,15,8,9,10,11,16,17,18,19), ) Hex20.drawfaces = [ Hex20.faces.selectNodes(i) for i in Quad8.drawfaces ] Hex20.drawfaces2 = [ Hex20.faces ] # THIS ELEMENT USES A REGULAR NODE NUMBERING!! # WE MIGHT SWITCH OTHER ELEMENTS TO THIS REGULAR SCHEME TOO # AND ADD THE RENUMBERING TO THE FE OUTPUT MODULES from simple import regularGrid Hex27 = Element( 'hex27',"A 27-node hexahedron", ndim = 3, vertices = regularGrid([0.,0.,0.],[1.,1.,1.],[2,2,2]).swapaxes(0,2).reshape(-1,3), edges = ('line3',[ (0,1,2),(6,7,8),(18,19,20),(24,25,26), (0,3,6),(2,5,8),(18,21,24),(20,23,26), (0,9,18),(2,11,20),(6,15,24),(8,17,26) ],), faces = ('quad9',[ (0,18,24,6,9,21,15,3,12),(2,8,26,20,5,17,23,11,14), (0,2,20,18,1,11,19,9,10),(6,24,26,8,15,25,17,7,16), (0,6,8,2,3,7,5,1,4),(18,20,26,24,19,23,25,21,22), ],), ) ###################################################################### ########## element type conversions ################################## Line3.conversions = { 'line2' : [ ('s', [ (0,2) ]), ], 'line2-2' : [ ('s', [ (0,1), (1,2) ]), ], } Tri3.conversions = { 'tri3-4' : [ ('v', 'tri6'), ], 'tri6' : [ ('m', [ (0,1), (1,2), (2,0) ]), ], 'quad4' : [ ('v', 'tri6'), ], } Tri6.conversions = { 'tri3' : [ ('s', [ (0,1,2) ]), ], 'tri3-4' : [ ('s', [ (0,3,5),(3,1,4),(4,2,5),(3,4,5) ]), ], 'quad4' : [ ('m', [ (0,1,2), ]), ('s', [ (0,3,6,5),(1,4,6,3),(2,5,6,4) ]), ], } Quad4.conversions = { 'tri3' : 'tri3-u', 'tri3-r' : [ ('r', ['tri3-u','tri3-d']), ], 'tri3-u' : [ ('s', [ (0,1,2), (2,3,0) ]), ], 'tri3-d' : [ ('s', [ (0,1,3), (2,3,1) ]), ], 'tri3-x' : [ ('m', [ (0,1,2,3) ]), ('s', [ (0,1,4),(1,2,4),(2,3,4),(3,0,4) ]), ], 'quad8' : [ ('m', [ (0,1), (1,2), (2,3), (3,0) ])], 'quad4-4': [ ('v', 'quad9'), ], 'quad9' : [ ('v', 'quad8'), ], } Quad8.conversions = { 'tri3' : [ ('v', 'quad9'), ], 'tri3-v' : [ ('s', [ (0,4,7),(1,5,4),(2,6,5),(3,7,6),(5,6,4),(7,4,6) ]), ], 'tri3-h' : [ ('s', [ (0,4,7),(1,5,4),(2,6,5),(3,7,6),(4,5,7),(6,7,5) ]), ], 'quad4' : [ ('s', [ (0,1,2,3) ]), ], 'quad4-4': [ ('v', 'quad9'), ], 'quad9' : [ ('m', [ (4,5,6,7) ]), ], } Quad9.conversions = { 'quad8' : [ ('s', [ (0,1,2,3,4,5,6,7) ]), ], 'quad4' : [ ('v', 'quad8'), ], 'quad4-4': [ ('s', [ (0,4,8,7),(4,1,5,8),(7,8,6,3),(8,5,2,6) ]), ], 'tri3' : 'tri3-d', 'tri3-d' : [ ('s', [ (0,4,7),(4,1,5),(5,2,6),(6,3,7), (7,4,8),(4,5,8),(5,6,8),(6,7,8) ]), ], 'tri3-x' : [ ('s', [ (0,4,8),(4,1,8),(1,5,8),(5,2,8), (2,6,8),(6,3,8),(3,7,8),(7,0,8) ]), ], } Tet4.conversions = { 'tet10' : [ ('m', [ (0,1), (0,2), (0,3), (1,2), (2, 3), (1, 3)]), ], 'tet14' : [ ('v', 'tet10'), ], 'tet15' : [ ('v', 'tet14'), ], 'hex8' : [ ('v', 'tet15'), ], } Tet10.conversions = { 'tet4' : [ ('s', [ (0,1,2,3,) ]), ], 'tet14' : [ ('m', [ (0,1, 2), (0, 2, 3), (0, 3, 1), (1, 2, 3), ]), ], 'tet15' : [ ('v', 'tet14'), ], 'hex8' : [ ('v', 'tet15'), ], } Tet14.conversions = { 'tet10' : [ ('s', [ (0,1,2,3,4, 5, 6, 7, 8, 9) ]), ], 'tet4' : [ ('v', 'tet10'), ], 'tet15' : [ ('m', [ (0,1, 2, 3), ]), ], 'hex8' : [ ('v', 'tet15'), ], } Tet15.conversions = { 'tet14' : [ ('s', [ (0,1,2,3,4, 5, 6, 7, 8, 9, 10, 11, 12, 13) ]), ], 'tet10' : [ ('v', 'tet14'), ], 'tet4' : [ ('v', 'tet10'), ], 'hex8' : [ ('s', [ (0,4,10, 5, 6, 12, 14, 11), (4,1,7, 10, 12, 9, 13, 14), (5, 10, 7,2,11, 14, 13, 8), (6, 12, 14, 11, 3, 9, 13, 8) ]), ], } Wedge6.conversions = { 'tet4' : [ ('s', [ (0,1,2,3),(1,2,3,4),(2,3,4,5) ]), ], } Hex8.conversions = { 'wedge6': [ ('s', [ (0,1,2,4,5,6),(2,3,0,6,7,4) ]), ], 'tet4' : [ ('s', [ (0,1,2,5),(2,3,0,7),(5,7,6,2),(7,5,4,0),(0,5,2,7) ]), ], 'tet4-6': [ ('v', 'wedge6') ], 'hex8-8': [ ('v', 'hex20'), ], 'hex20' : [ ('m', [ (0,1), (1,2), (2,3), (3,0), (4,5), (5,6), (6,7), (7,4), (0,4), (1,5), (2,6), (3,7), ]), ], } Hex20.conversions = { 'hex8' : [ ('s', [ (0,1,2,3,4,5,6,7) ]), ], 'hex8-8': [ ('v', 'hex27'), ], # 'hex27' : 'tet4' : [ ('v', 'hex8'), ], } Hex27.conversions = {} Hex16.conversions = { 'hex20' : [ ('m',[ (0,8), (1,9), (2,10), (3,11) ]), ('s',[(0, 1, 2, 3, 8, 9, 10, 11, 4, 5, 6, 7, 12, 13, 14, 15, 16, 17, 18, 19)])], } Quad6.conversions = { 'quad8' : [ ('m',[ (0,3), (1,2)]), ('s',[(0, 1, 2, 3, 4, 7, 5, 6)])], } ########################################################## ############ Extrusions ################################## # # Extrusion database # # For each element, extruded is a dictionary with # # key = degree of extrusion (1 or 2) # value = tuple (Target element type, Node reordering) # If no Node reordering is specified, the nodes of the translated entity are jus append to # those of the original entity. # # NEED TO CHECK THIS !!!!: # For degree 2, the default order is: first plane, intermediate plane, last plane. Point.extruded = { 1: (Line2, []), 2: (Line3, [0,2,1]) } Line2.extruded = { 1: (Quad4, [0,1,3,2] ) } Line3.extruded = { 1: (Quad6, [0,2,5,3,1,4]), 2: (Quad9, [0,1,7,6,2,4,8,3,5]), } Tri3.extruded = { 1: (Wedge6, [] ) } Quad4.extruded = { 1: (Hex8, [] ) } Quad8.extruded = { 1: (Hex16, [] ) } # BV: If Quad9 would be numbered consecutively, extrusion would be as easy as #Quad9.extruded = { 2: (Hex27, [] } Quad9.extruded = { 2: (Hex27, [ 0, 4, 1, 7, 8, 5, 3, 6, 2, 9,13,10,16,17,14,12,15,11, 18,22,19,25,26,23,21,24,20, ]) } ############################################################ ############ Reduction of degenerate elements ############## Line3.degenerate = { 'line2' : [ ([[0,1]], [0,2]), ([[1,2]], [0,2]), ([[0,2]], [0,1]), ], } Hex8.degenerate = { 'wedge6' : [ ([[0,1],[4,5]], [0,2,3,4,6,7]), ([[1,2],[5,6]], [0,1,3,4,5,7]), ([[2,3],[6,7]], [0,1,2,4,5,6]), ([[3,0],[7,4]], [0,1,2,4,5,6]), ([[0,1],[3,2]], [0,4,5,3,7,6]), ([[1,5],[2,6]], [0,4,5,3,7,6]), ([[5,4],[6,7]], [0,4,1,3,7,2]), ([[4,0],[7,3]], [0,5,1,3,6,2]), ([[0,3],[1,2]], [0,7,4,1,6,5]), ([[3,7],[2,6]], [0,3,4,1,2,5]), ([[7,4],[6,5]], [0,3,4,1,2,5]), ([[4,0],[5,1]], [0,3,7,1,2,6]), ], } ########################################################## # This element added just for fun, no practical importance from arraytools import golden_ratio as phi Icosa = Element( 'icosa', """An icosahedron: a regular polyhedron with 20 triangular surfaces., nfaces = 20, nedges = 30, nvertices = 12 """, ndim = 3, vertices = [ ( 0.0, 1.0, phi ), ( 0.0,-1.0, phi ), ( 0.0, 1.0,-phi ), ( 0.0,-1.0,-phi ), ( 1.0, phi, 0.0 ), (-1.0, phi, 0.0 ), ( 1.0,-phi, 0.0 ), (-1.0,-phi, 0.0 ), ( phi, 0.0, 1.0 ), ( phi, 0.0,-1.0 ), (-phi, 0.0, 1.0 ), (-phi, 0.0,-1.0 ), ], edges = ('line2', [ (0,1), (0,8), (1,8), (0,10),(1,10), (2,3), (2,9), (3,9), (2,11),(3,11), (4,5), (4,0), (5,0), (4,2), (5,2), (6,7), (6,1), (7,1), (6,3), (7,3), (8,9), (8,4), (9,4), (8,6), (9,6), (10,11),(10,5),(11,5),(10,7),(11,7), ], ), faces = ('tri3', [ (0,1,8), (1,0,10), (2,3,11), (3,2,9), (4,5,0), (5,4,2), (6,7,3), (7,6,1), (8,9,4), (9,8,6), (10,11,7),(11,10,5), (0,8,4), (1,6,8), (0,5,10), (1,10,7), (2,11,5), (3,7,11), (2,4,9), (3,9,6), ], ), reversed = (2,3,0,1,4,5,6,7,9,8,11,10), ) # list of default element type per plexitude _default_eltype = { 1 : Point, 2 : Line2, 3 : Tri3, 4 : Quad4, 6 : Wedge6, 8 : Hex8, } _default_facetype = { 3 : 'tri3', 4 : 'quad4', 6 : 'tri6', 8 : 'quad8', 9 : 'quad9', } def elementType(name=None,nplex=-1): """Return the requested element type Parameters: - `name`: a string (case ignored) with the name of an element. If not specified, or the named element does not exist, the default element for the specified plexitude is returned. - `nplex`: plexitude of the element. If specified and no element name was given, the default element type for this plexitude is returned. Returns: a subclass of :class:`Element` Errors: if neither `name` nor `nplex` can resolve into an element type, an error is raised. Example: >>> elementType('tri3').name() 'tri3' >>> elementType(nplex=2).name() 'line2' """ if isinstance(name,Element): return name eltype = None try: eltype = Element.collection[name.lower()] #print "TEST %s (%s)" % (eltype,nplex) # TESTING WOULD BREAK SOME INVALID ELTYPE SETTINGS in mesh # MAYBE INTRODUCE None/INvalidElement for no valid eltype #if not (nplex >= 0 and nplex != eltype.nplex()): return eltype except: pass if eltype is None: try: return _default_eltype[nplex] except: pass return None #return NoElement #raise ValueError("There is no element with name '%s' and plexitude '%s'" % (str(name),str(nplex))) def elementTypes(ndim=None): """Return the names of available elements. If a value is specified for ndim, only the elements with the matching dimensionality are returned. """ if ndim is None: return Element.collection.keys() else: return [ k for k,v in Element.collection.items() if v.ndim==ndim] def printElementTypes(): """Print all available element types. Prints a list of the names of all availabale element types, grouped by their dimensionality. """ print("Available Element Types:") for ndim in range(4): print(" %s-dimensional elements: %s" % (ndim,elementTypes(ndim)) ) def elementName(eltype): if isinstance(eltype,Element): return eltype.name() elif type(eltype) is str: try: return elementType(eltype).name() except: pass else: return None if __name__ == "__main__": printElementTypes() # End pyformex-0.8.6/pyformex/odict.py0000644000211500021150000001565411705104656016577 0ustar benebene00000000000000#!/usr/bin/python # $Id: odict.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """Specialized dictionary type structures.""" import olist def __newobj__(cls, *args): return cls.__new__(cls, *args) class ODict(dict): """**An ordered dictionary.** This is a dictionary that keeps the keys in order. The default order is the insertion order. The current order can be changed at any time. The :class:`ODict` can be initialized with a Python dict or another :class:`ODict` object. If a plain Python dict is used, the resulting order is undefined. """ def __init__(self,data={}): """Create a new ODict instance.""" dict.__init__(self,data) if isinstance(data,ODict): # keep order self._order = data._order elif type(data) is list or type(data) is tuple: # preserve the order self._order = [] self._add_keys([i[0] for i in data]) elif type(data) is dict: # order is undefined self._order = data.keys() else: raise ValueError,"Unexpected initialization value for ODict" def _add_keys(self,keys): """Add a list of keys to the ordered list, removing existing keys.""" for k in keys: if k in self._order: self._order.remove(k) self._order += keys def update(self,data={}): """Add a dictionary to the ODict object. The new keys will be appended to the existing, but the order of the added keys is undetemined if data is a dict object. If data is an ODict its order will be respected.. """ dict.update(self,data) self._add_keys(ODict(data)._order) def __iter__(self): return list.__iter__(self._order) def __repr__(self): """Format the Dict as a string. We use the format Dict({}), so that the string is a valid Python representation of the Dict. """ return [(k,self[k]) for k in self._order].__repr__() def __setitem__(self,key,value): """Allows items to be set using self[key] = value.""" dict.__setitem__(self,key,value) # setting an item should not change the order! # If you want to change the order, first remove the item ## if key in self._order: ## self._order.remove(key) ## self._order.append(key) if key not in self._order: self._order.append(key) def __delitem__(self,key): """Allow items to be deleted using del self[key]. Raises an error if key does not exist. """ dict.__delitem__(self,key) self._order.remove(key) def __add__(self,data): """Add two ODicts's together, returning the result.""" self.update(data) return self def sort(self,keys): """Set the order of the keys. keys should be a list containing exactly all the keys from self. """ if olist.difference(keys,dict.keys(self)) != []: raise ValueError,"List of keys does not match current object's keys" self._order = keys def keys(self): """Return the keys in order.""" return self._order def values(self): """Return the values in order of the keys.""" return [self[k] for k in self._order] def items(self): """Return the key,value pairs in order of the keys.""" return [(k,self[k]) for k in self._order] def pos(self,key): """Return the position of the specified key. If the key is not in the ODict, None is returned""" try: return self._order.index(key) except ValueError: return None def __reduce__(self): state = (dict(self), self.__dict__) return (__newobj__, (self.__class__,), state) def __setstate__(self,state): self.__init__() if type(state) == tuple: self.update(state[0]) self.__dict__.update(state[1]) elif type(state) == dict: #self.__dict__['_default_'] = state.pop('_default_') self.update(state) class KeyedList(ODict): """A named item list. A KeyedList is a list of lists or tuples. Each item (sublist or tuple) should at least have 2 elements: the first one is used as a key to identify the item, but is also part of the information (value) of the item. """ def __init__(self,alist=[]): """Create a new KeyedList, possibly filling it with data. data should be a list of tuples/lists each having at least 2 elements. The (string value of the) first is used as the key. """ L = map(len,alist) if min(L) < 2: raise ValueEror,"All items in the data should have length >= 2" ODict.__init__(self,[[i[0],i[1:]] for i in alist]) print(self) def items(self): """Return the key+value lists in order of the keys.""" return [(k,)+self[k] for k in self._order] if __name__ == "__main__": d = ODict({'a':1,'b':2,'c':3,'a':3}) print(d) d.sort(['a','b','c']) print(d) d = ODict([('a',1),('b',2),('c',3),('a',3)]) print d d['d'] = 4 d['e'] = 5 d['f'] = 6 print(d) del d['c'] print(d) D = ODict(d) print(D) D['d'] = 26 print(D) print(D['b']) D = ODict(zip(range(5),range(6,10))) print(D) print(D.keys()) print(D.values()) print(D.items()) del D[1] del D[2] D[4] = 4 D[3] = 3 D[2] = 2 D[1] = 1 print(D) print(D.keys()) print(D.values()) print(D.items()) k = D.keys() k.sort() D.sort(k) print(D) print(D.keys()) print(D.values()) print(D.items()) D[1] += 7 D[3] += 8 print(D.items()) print("DONE") # End pyformex-0.8.6/pyformex/utils.py0000644000211500021150000007140011705104656016624 0ustar benebene00000000000000# $Id: utils.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## """A collection of miscellaneous utility functions.""" import pyformex as pf import os,re,sys,tempfile from config import formatDict from distutils.version import LooseVersion as SaneVersion ### execute a system command ### def system(cmd): pf.debug("COMMAND: %s" % cmd) import subprocess P = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE) # or .STDOUT to redirect sta = P.wait() # wait for the process to finish out = P.communicate()[0] # get the stdout return sta,out # versions of detected modules/external commands the_version = { 'pyformex':pf.__version__, 'python':sys.version.split()[0], } the_external = {} # Do not include pyformex or python here: they are predefined # and could be erased by the detection known_modules = [ 'numpy','pyopengl','pyqt4','pyqt4gl','calpy', 'gnuplot','gl2ps' ] known_externals = { 'Python': ('python --version','Python (\\S+)'), 'ImageMagick': ('import -version','Version: ImageMagick (\S+)'), 'admesh': ('admesh --version', 'ADMesh - version (\S+)'), 'calpy': ('calpy --version','Calpy (\S+)'), 'tetgen': ('tetgen -h |fgrep Version','Version (\S+)'), 'units': ('units --version','GNU Units version (\S+)'), 'ffmpeg': ('ffmpeg -version','FFmpeg version (\\S+)'), 'gts': ('gtsset -h','Usage(:) set'), 'calix': ('calix --version','CALIX-(\S+)'), 'dxfparser': ('dxfparser --version','dxfparser (\S+)'), } def checkVersion(name,version,external=False): """Checks a version of a program/module. name is either a module or an external program whose availability has been registered. Default is to treat name as a module. Add external=True for a program. Return value is -1, 0 or 1, depending on a version found that is <, == or > than the requested values. This should normally understand version numbers in the format 2.10.1 Returns -2 if no version found. """ if external: ver = hasExternal(name) else: ver = hasModule(name) if not ver: return -2 if SaneVersion(ver) > SaneVersion(version): return 1 elif SaneVersion(ver) == SaneVersion(version): return 0 else: return -1 def hasModule(name,check=False): """Test if we have the named module available. Returns a nonzero (version) string if the module is available, or an empty string if it is not. By default, the module is only checked on the first call. The result is remembered in the the_version dict. The optional argument check==True forces a new detection. """ if the_version.has_key(name) and not check: return the_version[name] else: return checkModule(name) def hasExternal(name,force=False): """Test if we have the external command 'name' available. Returns a nonzero string if the command is available, or an empty string if it is not. The external command is only checked on the first call. The result is remembered in the the_external dict. """ if the_external.has_key(name) and not force: return the_external[name] else: return checkExternal(name) def checkModule(name=None): """Check if the named Python module is available, and record its version. The version string is returned, empty if the module could not be loaded. The (name,version) pair is also inserted into the the_version dict. """ if name is None: [ checkModule(n) for n in known_modules ] return version = '' fatal = False try: if name == 'numpy': fatal = True import numpy version = numpy.__version__ elif name == 'pyopengl': fatal = pf.options.gui import OpenGL version = OpenGL.__version__ elif name == 'pyqt4': fatal = pf.options.gui import PyQt4.QtCore version = PyQt4.QtCore.QT_VERSION_STR elif name == 'pyqt4gl': fatal = pf.options.gui import PyQt4.QtOpenGL import PyQt4.QtCore version = PyQt4.QtCore.QT_VERSION_STR elif name == 'calpy': import calpy version = calpy.__version__ elif name == 'gnuplot': import Gnuplot version = Gnuplot.__version__ elif name == 'gl2ps': import gl2ps version = str(gl2ps.GL2PS_VERSION) except: pass # make sure version is a string (e.g. gl2ps uses a float!) version = str(version) _congratulations(name,version,'module',fatal,quiet=True) the_version[name] = version return version def checkExternal(name=None,command=None,answer=None): """Check if the named external command is available on the system. name is the generic command name, command is the command as it will be executed to check its operation, answer is a regular expression to match positive answers from the command. answer should contain at least one group. In case of a match, the contents of the match will be stored in the the_external dict with name as the key. If the result does not match the specified answer, an empty value is inserted. Usually, command will contain an option to display the version, and the answer re contains a group to select the version string from the result. As a convenience, we provide a list of predeclared external commands, that can be checked by their name alone. If no name is given, all commands in that list are checked, and no value is returned. """ if name is None: [ checkExternal(n) for n in known_externals.keys() ] return if command is None or answer is None: cmd,ans = known_externals.get(name,(name,'(.+)\n')) if command is None: command = cmd if answer is None: answer = ans m = re.match(answer,system(command)[1]) if m: version = m.group(1) else: version = '' _congratulations(name,version,'program',quiet=True) the_external[name] = version return version def FullVersion(): return "%s (Rev. %s)" % (pf.Version,pf.__revision__) def Libraries(): from lib import accelerated acc = [ m.__name__ for m in accelerated ] return ', '.join(acc) def reportDetected(): s = "%s\n\n" % FullVersion() s += "pyFormex C libraries: %s\n\n" % Libraries() s += "Python version: %s\n" % sys.version s += "Operating system: %s\n\n" % sys.platform s += "Detected Python Modules:\n" for k,v in the_version.items(): if not v: v = 'Not Found' s += "%s (%s)\n" % ( k,v) s += "\nDetected External Programs:\n" for k,v in the_external.items(): #if not v: # v = 'Not Found' s += "%s (%s)\n" % ( k,v) return s def procInfo(title): print(title) print('module name: %s' % __name__) print('parent process: %s' % os.getppid()) print('process id: %s' % os.getpid()) def strNorm(s): """Normalize a string. Text normalization removes all '&' characters and converts it to lower case. """ return str(s).replace('&','').lower() def _congratulations(name,version,typ='module',fatal=False,quiet=True): """Report a detected module/program.""" if version and not quiet: pf.message("Congratulations! You have %s (%s)" % (name,version)) if not version: if not quiet or fatal: pf.message("ALAS! I could not find %s '%s' on your system" % (typ,name)) if fatal: pf.message("Sorry, I'm out of here....") sys.exit() def prefixFiles(prefix,files): """Prepend a prefix to a list of filenames.""" return [ os.path.join(prefix,f) for f in files ] def matchMany(regexps,target): """Return multiple regular expression matches of the same target string.""" return [re.match(r,target) for r in regexps] def matchCount(regexps,target): """Return the number of matches of target to regexps.""" return len(filter(None,matchMany(regexps,target))) def matchAny(regexps,target): """Check whether target matches any of the regular expressions.""" return matchCount(regexps,target) > 0 def matchNone(regexps,target): """Check whether targes matches none of the regular expressions.""" return matchCount(regexps,target) == 0 def matchAll(regexps,target): """Check whether targets matches all of the regular expressions.""" return matchCount(regexps,target) == len(regexps) def listTree(path,listdirs=True,topdown=True,sorted=False,excludedirs=[],excludefiles=[],includedirs=[],includefiles=[]): """List all files in path. If ``dirs==False``, directories are not listed. By default the tree is listed top down and entries in the same directory are unsorted. `exludedirs` and `excludefiles` are lists of regular expressions with dirnames, resp. filenames to exclude from the result. `includedirs` and `includefiles` can be given to include only the directories, resp. files matching any of those patterns. Note that 'excludedirs' and 'includedirs' force top down handling. """ filelist = [] if excludedirs or includedirs: topdown = True for root, dirs, files in os.walk(path, topdown=topdown): if sorted: dirs.sort() files.sort() if excludedirs: remove = [ d for d in dirs if matchAny(excludedirs,d) ] for d in remove: dirs.remove(d) if includedirs: remove = [ d for d in dirs if not matchAny(includedirs,d) ] for d in remove: dirs.remove(d) if listdirs and topdown: filelist.append(root) if excludefiles: files = [ f for f in files if matchNone(excludefiles,f) ] if includefiles: files = [ f for f in files if matchAny(includefiles,f) ] filelist.extend(prefixFiles(root,files)) if listdirs and not topdown: filelist.append(root) return filelist def removeTree(path,top=True): """Remove all files below path. If top==True, also path is removed.""" for root, dirs, files in os.walk(path, topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) if top: os.rmdir(path) def pyformexFiles(relative=False): # WE COULD ADD other=None): """Return a list of the pyformex source .py files. """ path = pf.cfg['pyformexdir'] if relative: path = os.path.relpath(path) files = listTree(path,listdirs=False,sorted=True,includedirs=['gui','plugins','examples','lib'],includefiles=['.*\.py$']) return files ###################### locale ################### def setSaneLocale(localestring=''): """Set a sane local configuration for LC_NUMERIC. `localestring` is the locale string to be set, e.g. 'en_US.UTF-8' This will change the ``LC_ALL`` setting to the specified string, and set the ``LC_NUMBERIC`` to 'C'. Changing the LC_NUMERIC setting is a very bad idea! It makes floating point values to be read or written with a comma instead of a the decimal point. Of course this makes input and output files completely incompatible. You will often not be able to process these files any further and create a lot of troubles for yourself and other people if you use an LC_NUMERIC setting different from the standard. Because we do not want to help you shoot yourself in the foot, this function always sets ``LC_NUMERIC`` back to a sane value and we call this function when pyFormex is starting up. """ import locale locale.setlocale(locale.LC_ALL,localestring) locale.setlocale(locale.LC_NUMERIC, 'C') ###################### ReST conversion ################### try: from docutils.core import publish_string def rst2html(text,writer='html'): return publish_string(text,writer_name=writer) except ImportError: def rst2html(text,writer='html'): return text ###################### dos to unix conversion ################### def dos2unix(infile,outfile=None): if outfile is None: cmd = "sed -i 's|\\r||' %s" % infile else: cmd = "sed -i 's|\\r||' %s > %s" % (infile,outfile) return runCommand(cmd) def unix2dos(infile,outfile=None): if outfile is None: cmd = "sed -i 's|$|\\r|' %s" % infile else: cmd = "sed -i 's|$|\\r|' %s > %s" % (infile,outfile) return runCommand(cmd) ###################### image and file formats ################### def all_image_extensions(): """Return a list with all known image extensions.""" imgfmt = [] file_description = { 'all': 'All files (*)', 'dxf': 'AutoCAD .dxf files (*.dxf)', 'dxftext': 'Converted AutoCAD files (*.dxftext)', 'flavia' : 'flavia results (*.flavia.msh *.flavia.res)', 'gts': 'GTS files (*.gts)', 'icon': 'Icons (*.xpm)', 'img': 'Images (*.png *.jpg *.eps *.gif)', 'inp': 'Abaqus input files (*.inp)', 'neu': 'Gambit Neutral files (*.neu)', 'off': 'OFF files (*.off)', 'pgf': 'pyFormex geometry files (*.pgf)', 'png': 'PNG images (*.png)', 'postproc': 'Postproc scripts (*_post.py *.post)', 'pyformex': 'pyFormex scripts (*.py *.pye)', 'pyf': 'pyFormex projects (*.pyf)', 'smesh': 'Tetgen surface mesh files (*.smesh)', 'stl': 'STL files (*.stl)', 'surface': 'Surface model (*.off *.gts *.stl *.neu)', 'tetgen': 'Tetgen file (*.poly *.smesh *.ele *.face *.edge *.node *.neigh)', } def fileDescription(ftype): """Return a description of the specified file type. The description of known types are listed in a dict file_description. If the type is unknown, the returned string has the form ``TYPE files (*.type)`` """ if type(ftype) is list: return map(fileDescription,ftype) ftype = ftype.lower() return file_description.get(ftype,"%s files (*.%s)" % (ftype.upper(),ftype)) def fileType(ftype): """Normalize a filetype string. The string is converted to lower case and a leading dot is removed. This makes it fit for use with a filename extension. Example: >>> fileType('pdf') 'pdf' >>> fileType('.pdf') 'pdf' >>> fileType('PDF') 'pdf' >>> fileType('.PDF') 'pdf' """ ftype = ftype.lower() if len(ftype) > 0 and ftype[0] == '.': ftype = ftype[1:] return ftype def fileTypeFromExt(fname): """Derive the file type from the file name. The derived file type is the file extension part in lower case and without the leading dot. Example: >>> fileTypeFromExt('pyformex.pdf') 'pdf' >>> fileTypeFromExt('.pyformexrc') '' >>> fileTypeFromExt('pyformex') '' """ return fileType(os.path.splitext(fname)[1]) def findIcon(name): """Return the file name for an icon with given name. If no icon file is found, returns the question mark icon. """ fname = os.path.join(pf.cfg['icondir'],name) + pf.cfg['gui/icontype'] if os.path.exists(fname): return fname return os.path.join(pf.cfg['icondir'],'question') + pf.cfg['gui/icontype'] def projectName(fn): """Derive a project name from a file name. The project name is the basename f the file without the extension. """ return os.path.splitext(os.path.basename(fn))[0] def splitme(s): return s[::2],s[1::2] def mergeme(s1,s2): return ''.join([a+b for a,b in zip(s1,s2)]) def mtime(fn): """Return the (UNIX) time of last change of file fn.""" return os.stat(fn).st_mtime def timeEval(s,glob=None): """Return the time needed for evaluating a string. s is a string with a valid Python instructions. The string is evaluated using Python's eval() and the difference in seconds between the current time before and after the evaluation is printed. The result of the evaluation is returned. This is a simple method to measure the time spent in some operation. It should not be used for microlevel instructions though, because the overhead of the time calls. Use Python's timeit module to measure microlevel execution time. """ import time start = time.time() res = eval(s,glob) stop = time.time() pf.message("Timed evaluation: %s seconds" % (stop-start)) return res def countLines(fn): """Return the number of lines in a text file.""" sta,out = runCommand("wc %s" % fn) if sta == 0: return int(out.split()[0]) else: return 0 def runCommand(cmd,RaiseError=True,quiet=False): """Run a command and raise error if exited with error. cmd is a string with the command to be run. The command is run in the background, waiting for the result. If no error occurs, the exit status and stdout are returned. Else an error is raised by default. """ import subprocess if not quiet: pf.message("Running command: %s" % cmd) sta,out = system(cmd) if sta != 0: if not quiet: pf.message(out) pf.message("Command exited with an error (exitcode %s)" % sta) if RaiseError: raise RuntimeError, "Error while executing command:\n %s" % cmd return sta,out.rstrip('\n') def spawn(cmd): """Spawn a child process.""" cmd = cmd.split() pid = os.spawnvp(os.P_NOWAIT,cmd[0],cmd) pf.debug("Spawned child process %s for command '%s'" % (pid,cmd)) return pid def killProcesses(pids,signal): """Send the specified signal to the processes in list""" for pid in pids: try: os.kill(pid,signal) except: pf.debug("Error in killing of process '%s'" % pid) def changeExt(fn,ext): """Change the extension of a file name. The extension is the minimal trailing part of the filename starting with a '.'. If the filename has no '.', the extension will be appended. If the given extension does not start with a dot, one is prepended. """ if not ext.startswith('.'): ext = ".%s" % ext return os.path.splitext(fn)[0] + ext def tildeExpand(fn): """Perform tilde expansion on a filename. Bash, the most used command shell in Linux, expands a '~' in arguments to the users home direction. This function can be used to do the same for strings that did not receive the bash tilde expansion, such as strings in the configuration file. """ return fn.replace('~',os.environ['HOME']) def userName(): """Find the name of the user.""" try: return os.environ['LOGNAME'] except: return 'NOBODY' def is_pyFormex(filename): """Checks whether a file is a pyFormex script. A script is considered to be a pyFormex script if its first line starts with '#!' and contains the substring 'pyformex' A file is considered to be a pyFormex script if its name ends in '.py' and the first line of the file contains the substring 'pyformex'. Typically, a pyFormex script starts with a line:: #!/usr/bin/pyformex """ filename = str(filename) # force it into a string if filename.endswith(".pye"): return True ok = filename.endswith(".py") if ok: try: f = open(filename,'r') ok = f.readline().strip().find('pyformex') >= 0 f.close() except IOError: ok = False return ok tempFile = tempfile.NamedTemporaryFile # BV: We could turn this into a factory class NameSequence(object): """A class for autogenerating sequences of names. The name is a string including a numeric part, which is incremented at each call of the 'next()' method. The constructor takes name template and a possible extension as arguments. If the name starts with a non-numeric part, it is taken as a constant part. If the name ends with a numeric part, the next generated names will be obtained by incrementing this part. If not, a string '-000' will be appended and names will be generated by incrementing this part. If an extension is given, it will be appended as is to the names. This makes it possible to put the numeric part anywhere inside the names. Example: >>> N = NameSequence('hallo.98') >>> [ N.next() for i in range(3) ] ['hallo.98', 'hallo.99', 'hallo.100'] >>> NameSequence('hallo','.png').next() 'hallo-000.png' >>> N = NameSequence('/home/user/hallo23','5.png') >>> [ N.next() for i in range(2) ] ['/home/user/hallo235.png', '/home/user/hallo245.png'] """ def __init__(self,name,ext=''): """Create a new NameSequence from name,ext.""" base,number = splitEndDigits(name) if len(number) > 0: self.nr = int(number) format = "%%0%dd" % len(number) else: self.nr = 0 format = "-%03d" self.name = base+format+ext def next(self): """Return the next name in the sequence""" fn = self.name % self.nr self.nr += 1 return fn def peek(self): """Return the next name in the sequence without incrementing.""" return self.name % self.nr def glob(self): """Return a UNIX glob pattern for the generated names. A NameSequence is often used as a generator for file names. The glob() method returns a pattern that can be used in a UNIX-like shell command to select all the generated file names. """ i = self.name.find('%') j = self.name.find('d',i) return self.name[:i]+'*'+self.name[j+1:] string_digits = re.compile('(.*?)(\d*)$') digits_string = re.compile('(\d*)(.*)$') def splitEndDigits(s): """Split a string in any prefix and a numerical end sequence. A string like 'abc-0123' will be split in 'abc-' and '0123'. Any of both can be empty. """ return string_digits.match(s).groups() def splitStartDigits(s): """Split a string in a numerical sequence and any suffix. A string like '0123-abc' will be split in '0123' and '-abc'. Any of both can be empty. """ return digits_string.match(s).groups() def prefixDict(d,prefix=''): """Prefix all the keys of a dict with the given prefix. - `d`: a dict where all the keys are strings. - `prefix`: a string The return value is a dict with all the items of d, but where the keys have been prefixed with the given string. """ return dict([ (prefix+k,v) for k,v in d.items() ]) def subDict(d,prefix='',strip=True): """Return a dict with the items whose key starts with prefix. - `d`: a dict where all the keys are strings. - `prefix`: a string - `strip`: if True (default), the prefix is stripped from the keys. The return value is a dict with all the items from d whose key starts with prefix. The keys in the returned dict will have the prefix stripped off, unless strip=False is specified. """ if strip: return dict([ (k.replace(prefix,'',1),v) for k,v in d.items() if k.startswith(prefix)]) else: return dict([ (k,v) for k,v in d.items() if k.startswith(prefix)]) def selectDict(d,keys): """Return a dict with the items whose key is in keys. - `d`: a dict where all the keys are strings. - `keys`: a set of key values, can be a list or another dict. The return value is a dict with all the items from d whose key is in keys. See :func:`removeDict` for the complementary operation. Example: >>> d = dict([(c,c*c) for c in range(6)]) >>> selectDict(d,[4,0,1]) {0: 0, 1: 1, 4: 16} """ return dict([ (k,d[k]) for k in keys if k in d ]) def removeDict(d,keys): """Remove a set of keys from a dict. - `d`: a dict - `keys`: a set of key values The return value is a dict with all the items from `d` whose key is not in `keys`. This is the complementary operation of selectDict. Example: >>> d = dict([(c,c*c) for c in range(6)]) >>> removeDict(d,[4,0]) {1: 1, 2: 4, 3: 9, 5: 25} """ return dict([ (k,d[k]) for k in d if k not in keys ]) def refreshDict(d,src): """Refresh a dict with values from another dict. The values in the dict d are update with those in src. Unlike the dict.update method, this will only update existing keys but not add new keys. """ d.update(selectDict(src,d)) def stuur(x,xval,yval,exp=2.5): """Returns a (non)linear response on the input x. xval and yval should be lists of 3 values: ``[xmin,x0,xmax], [ymin,y0,ymax]``. Together with the exponent exp, they define the response curve as function of x. With an exponent > 0, the variation will be slow in the neighbourhood of (x0,y0). For values x < xmin or x > xmax, the limit value ymin or ymax is returned. """ xmin,x0,xmax = xval ymin,y0,ymax = yval if x < xmin: return ymin elif x < x0: xr = float(x-x0) / (xmin-x0) return y0 + (ymin-y0) * xr**exp elif x < xmax: xr = float(x-x0) / (xmax-x0) return y0 + (ymax-y0) * xr**exp else: return ymax ########################################################################### def interrogate(item): """Print useful information about item.""" import odict info = odict.ODict() if hasattr(item, '__name__'): info["NAME: "] = item.__name__ if hasattr(item, '__class__'): info["CLASS: "] = item.__class__.__name__ info["ID: "] = id(item) info["TYPE: "] = type(item) info["VALUE: "] = repr(item) info["CALLABLE:"] = callable(item) if hasattr(item, '__doc__'): doc = getattr(item, '__doc__') doc = doc.strip() # Remove leading/trailing whitespace. firstline = doc.split('\n')[0] info["DOC: "] = firstline for i in info.items(): print("%s %s"% i) def deprecation(message): def decorator(func): def wrapper(*_args,**_kargs): print func.__name__ import warnings warnings.warn(message, Warning, stacklevel=2) return func(*_args,**_kargs) return wrapper return decorator def deprecated(replacement): def decorator(func): def wrapper(*_args,**_kargs): """This function is deprecated.""" print("! Function '%s' is deprecated: use '%s.%s' instead" % (func.func_name,replacement.__module__,replacement.func_name)) return replacement(*_args,**_kargs) return wrapper decorator.__doc__ = replacement.__doc__ return decorator def functionWasRenamed(replacement,text=None): def decorator(func): def wrapper(*_args,**_kargs): print("! Function '%s' is deprecated: use '%s' instead" % (func.func_name,replacement.func_name)) return replacement(*_args,**_kargs) return wrapper decorator.__doc__ = replacement.__doc__ return decorator def functionBecameMethod(replacement): def decorator(func): def wrapper(object,*args,**kargs): print("! Function %s is deprecated: use method %s instead" % (func.func_name,replacement)) repfunc = getattr(object,replacement) return repfunc(*args,**kargs) return wrapper return decorator def filterWarning(message,module='',category='U',action='ignore'): import warnings if category == 'D': category = DeprecationWarning else: category = UserWarning warnings.filterwarnings(action,message,category,module) def warn(message,level=UserWarning,stacklevel=3): import warnings warnings.warn(message,level,stacklevel) def deprec(message,stacklevel=3): warn(message,level=DeprecationWarning,stacklevel=stacklevel) ### End pyformex-0.8.6/setup.py0000644000211500021150000003000611705104656014750 0ustar benebene00000000000000# $Id: setup.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """Setup script for pyFormex To install pyFormex: python setup.py install --prefix=/usr/local To uninstall pyFormex: pyformex --remove """ from distutils.command.install import install as _install from distutils.command.install_data import install_data as _install_data from distutils.command.install_scripts import install_scripts as _install_scripts from distutils.command.build_ext import build_ext as _build_ext from distutils.command.build import build as _build from distutils.command.sdist import sdist as _sdist from distutils.core import setup, Extension from distutils import filelist from distutils.util import get_platform import os,sys,commands # Detect platform pypy = hasattr(sys, 'pypy_version_info') jython = sys.platform.startswith('java') py3k = False if sys.version_info < (2, 4): raise Exception("pyFormex requires Python 2.4 or higher.") elif sys.version_info >= (3, 0): py3k = True # define the things to include from manifest import * # The acceleration libraries LIB_MODULES = [ 'drawgl_', 'misc_', 'nurbs_' ] ext_modules = [Extension('pyformex/lib/%s'%m, sources = ['pyformex/lib/%smodule.c'%m], # optional=True, ) for m in LIB_MODULES ] class BuildFailed(Exception): def __init__(self): self.cause = sys.exc_info()[1] # work around py 2/3 different syntax def status_msgs(*msgs): """Print status messages""" print('*' * 75) for msg in msgs: print(msg) print('*' * 75) class sdist(_sdist): def get_file_list(self): """Figure out the list of files to include in the source distribution, and put it in 'self.filelist'. This might involve reading the manifest template (and writing the manifest), or just reading the manifest, or just using the default file set -- it all depends on the user's options. """ self.filelist = filelist.FileList() self.filelist.files = DIST_FILES self.filelist.sort() self.filelist.remove_duplicates() self.write_manifest() ## class install(_install): ## def run(self): ## # Obviously have to build before we can run pre-install ## if not self.skip_build: ## self.run_command('build') ## # If we built for any other platform, we can't install. ## build_plat = self.distribution.get_command_obj('build').plat_name ## # check warn_dir - it is a clue that the 'install' is happening ## # internally, and not to sys.path, so we don't check the platform ## # matches what we are running. ## if self.warn_dir and build_plat != get_platform(): ## raise DistutilsPlatformError("Can't install when " ## "cross-compiling") ## #os.system("./pre-install %s %s" % (self.build_base,self.install_lib)) ## _install.run(self) ## #os.system("./post-install %s" % self.install_lib) ## class install(_install): ## def run(self): ## global srcdir,globaldocs ## _install.run(self) ## if globaldocs: ## print dir(self) ## localdir = os.path.join(self.install_lib,'pyformex/doc/html') ## globaldir = os.path.join(self.install_data,'share/doc/pyformex/html') ## print "html doc is in ",localdir ## print "html doc should be in ",globaldir ## import shutil ## if os.path.exists(globaldir): ## shutil.rmtree(globaldir) ## shutil.move(localdir,globaldir) ## os.symlink(globaldir,localdir) ## class install_data(_install_data): ## def run(self): ## global srcdir ## srcdir = os.path.join(self.install_dir,'share/doc/pyformex/html') ## _install_data.run(self) ## class build_ext(_build_ext): ## """Specialized Python Extension builder. ## This overrides the normal Python distutils Extension builder. ## Our own builder runs a configuration procedure first, and if ## the configuration does not succeed, the Extension is not built. ## This forms no problem for installing pyFormex, because the ## extensions are optional, and are replaced with pure Python functions ## if the Extensions are not installed. ## """ ## def configure(self): ## """Detect the required header files""" ## print("NOT Configuring the pyFormex acceleration library") ## #cmd = "cd pyformex/lib;./configure >/dev/null && grep '^SUCCESS=' config.log" ## #sta,out = commands.getstatusoutput(cmd) ## #print(out) ## #exec(out) ## #return SUCCESS=='1' ## return True ## def run (self): ## """Configure the extensions and if successful, build them.""" ## ## The current building process will probably not work on ## ## non-posix systems. ## ## If anybody knows how to do it, please go ahead and remove this. ## if os.name != 'posix': ## print("!! The acceleration library is not available for your platform.\n!! You should consider switching to Linux (or some other Posix) Platform.") ## return ## if self.configure(): ## print("Compiling the pyFormex acceleration library") ## _build_ext.run(self) ## # Should we compile postabq even if configure failed? ## #print("Compiling the pyFormex postabq converter") ## #cmd = "cd pyformex/lib;make postabq" ## #sta,out = commands.getstatusoutput(cmd) ## #print(out) ## else: ## print(""" ## Some files required to compile the accelerator library were not found ## on your system. Installation will be continued, and pyFormex will run ## without the library, but some operations on large data sets may run slowly. ## See the manual or the website for information onhow to install the missing ## files. ## """) ## class build(_build): ## sub_commands = [('config_cc', lambda *args: True), ## ('config_fc', lambda *args: True), ## ('build_src', _build.has_ext_modules), ## ] + _build.sub_commands ## user_options = _build.user_options + [ ## ('fcompiler=', None, ## "specify the Fortran compiler type"), ## ] ## help_options = _build.help_options + [ ## ('help-fcompiler',None, "list available Fortran compilers", ## show_fortran_compilers), ## ] ## def initialize_options(self): ## _build.initialize_options(self) ## self.fcompiler = None ## def finalize_options(self): ## build_scripts = self.build_scripts ## _build.finalize_options(self) ## plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3]) ## if build_scripts is None: ## self.build_scripts = os.path.join(self.build_base, ## 'scripts' + plat_specifier) ## def run(self): ## _build.run(self) def run_setup(with_cext): global OTHER_DATA kargs = {} if with_cext: kargs['ext_modules'] = ext_modules # PKG_DATA, relative from pyformex path PKG_DATA = [ 'pyformexrc', 'icons/README', 'icons/*.xpm', 'icons/pyformex*.png', 'examples/scripts.cat', 'examples/Demos/*', 'data/*', ## 'extra/*/*', ] ## if globaldocs: ## # Install in the global doc path ## OTHER_DATA.append(('share/doc/pyformex/html',['pyformex/doc/html/*'])) ## else: ## # Install docs in package path ## PKG_DATA += [ i[9:] for i in DOC_FILES ] ## print OTHER_DATA PKG_DATA += [ i[9:] for i in DOC_FILES ] setup(cmdclass={ ## 'install_scripts': install_scripts, ## 'build_ext': build_ext, ## 'build': build, ## 'install':install, ## 'install_data':install_data, 'sdist':sdist }, name='pyformex', version='0.8.6', description='Program to generate and transform 3D geometries from scripts.', long_description=""" pyFormex is a tool to generate, transform and manipulate large and complex geometrical models of 3D structures by sequences of mathematical operations in a Python script. """, author='Benedict Verhegghe', author_email='benedict.verhegghe@ugent.be', url='http://pyformex.org', license='GNU General Public License (GPL)', packages=[ 'pyformex', 'pyformex.gui', 'pyformex.lib', 'pyformex.plugins', 'pyformex.examples' ], package_data={ 'pyformex': PKG_DATA }, scripts=['pyformex/pyformex'],#,'pyformex/pyformex-search'],#'pyformex-viewer'], data_files=OTHER_DATA, classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Environment :: X11 Applications :: Qt', 'Intended Audience :: End Users/Desktop', 'Intended Audience :: Science/Research', 'Intended Audience :: Education', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Operating System :: POSIX :: Linux', 'Operating System :: POSIX', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: C', 'Topic :: Multimedia :: Graphics :: 3D Modeling', 'Topic :: Multimedia :: Graphics :: 3D Rendering', 'Topic :: Scientific/Engineering :: Mathematics', 'Topic :: Scientific/Engineering :: Visualization', 'Topic :: Scientific/Engineering :: Physics', ], requires=['numpy','OpenGL','PyQt4'], **kargs ) # Detect the --no-accel option try: i = sys.argv.index('--no-accel') del(sys.argv[i]) accel = False except ValueError: accel = True ## # Detect the --globaldocs option ## globaldocs = False ## try: ## i = sys.argv.index('--globaldocs') ## del(sys.argv[i]) ## globaldocs = True ## except ValueError: ## pass if pypy or jython or py3k: accel = False status_msgs( "WARNING: C extensions are not supported on this Python platform," "I will continue without the acceleration libraries." ) # Try with compilation if accel: try: run_setup(accel) sys.exit() except BuildFailed: exc = sys.exc_info()[1] # work around py 2/3 different syntax status_msgs( exc.cause, "WARNING: The acceleration library could not be compiled, " "I will retry without them.") # Run without compilation run_setup(False) status_msgs("WARNING: Building without the acceleration library") # End pyformex-0.8.6/manifest.py0000755000211500021150000001653211705104656015431 0ustar benebene00000000000000#!/usr/bin/env python # $Id: manifest.py 2150 2012-01-16 20:33:48Z bverheg $ ## ## This file is part of pyFormex 0.8.6 (Mon Jan 16 21:15:46 CET 2012) ## pyFormex is a tool for generating, manipulating and transforming 3D ## geometrical models by sequences of mathematical operations. ## Home page: http://pyformex.org ## Project page: http://savannah.nongnu.org/projects/pyformex/ ## Copyright 2004-2011 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be) ## Distributed under the GNU General Public License version 3 or later. ## ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see http://www.gnu.org/licenses/. ## # """manifest.py This script creates the list of files to be included in the pyFormex source distribution. """ import os,re def prefixFiles(prefix,files): """Prepend a prefix to a list of filenames.""" return [ os.path.join(prefix,f) for f in files ] def matchMany(regexps,target): """Return multiple regular expression matches of the same target string.""" return [re.match(r,target) for r in regexps] def matchCount(regexps,target): """Return the number of matches of target to regexps.""" return len(filter(None,matchMany(regexps,target))) def matchAny(regexps,target): """Check whether target matches any of the regular expressions.""" return matchCount(regexps,target) > 0 def matchNone(regexps,target): """Check whether targes matches none of the regular expressions.""" return matchCount(regexps,target) == 0 def listTree(path,listdirs=True,topdown=True,sorted=False,excludedirs=[],excludefiles=[],includedirs=[],includefiles=[]): """List all files in path. If ``dirs==False``, directories are not listed. By default the tree is listed top down and entries in the same directory are unsorted. `exludedirs` and `excludefiles` are lists of regular expressions with dirnames, resp. filenames to exclude from the result. `includedirs` and `includefiles` can be given to include only the directories, resp. files matching any of those patterns. Note that 'excludedirs' and 'includedirs' force top down handling. """ filelist = [] if excludedirs or includedirs: topdown = True for root, dirs, files in os.walk(path, topdown=topdown): if sorted: dirs.sort() files.sort() if excludedirs: remove = [ d for d in dirs if matchAny(excludedirs,d) ] for d in remove: dirs.remove(d) if includedirs: remove = [ d for d in dirs if not matchAny(includedirs,d) ] for d in remove: dirs.remove(d) if listdirs and topdown: filelist.append(root) if excludefiles: files = [ f for f in files if matchNone(excludefiles,f) ] if includefiles: files = [ f for f in files if matchAny(includefiles,f) ] filelist.extend(prefixFiles(root,files)) if listdirs and not topdown: filelist.append(root) return filelist # pyFormex documentation (installed in the pyformex tree) DOC_FILES = listTree( 'pyformex/doc/html',listdirs=False,sorted=True, excludedirs=['.svn'], ) + listTree( 'pyformex/doc', listdirs=False,sorted=True, excludedirs=['.svn','dutch','html'], includefiles=[ 'README', 'COPYING', 'ReleaseNotes', ], ) # pyFormex data files (installed in the pyformex tree) DATA_FILES = listTree( 'pyformex/data',listdirs=False,sorted=True, excludedirs=['.svn','benchmark'], excludefiles=['.*\.pyc','.*~$','PTAPE.*'], includefiles=[ 'README', 'benedict_6.jpg', 'blippo.pgf', 'butterfly.png', 'hesperia-nieve.prop', 'horse.off', 'horse.pgf', 'materials.db', 'sections.db', 'splines.pgf', 'supershape.txt', 'teapot.off', 'world.jpg', ], ) # scripts to install extra programs EXTRA_FILES = listTree( 'pyformex/extra',listdirs=True,sorted=True, excludedirs=[ '.svn', 'build', 'calix', 'dxfparser', 'postabq' 'pyftgl', ], excludefiles=['.*~$'], includefiles=[ 'README', 'Makefile', '.*\.sh', '.*\.rst' '.*\.patch', '.*\.c', '.*\.cc', '.*\.i', '.*\.py', ], ) # Data files to be installed outside the pyformex tree # These are tuples (installdir,filelist) OTHER_DATA = [ ('share/pixmaps', [ 'pyformex/icons/pyformex-64x64.png', 'pyformex/icons/pyformex.xpm', ]), ('share/applications', ['pyformex.desktop']), ('share/man/man1', ['pyformex/doc/pyformex.1']), # the full html documentation ## ('share/doc/pyformex/html',DOC_FILES), ] DIST_FILES = [ 'README', 'COPYING', 'ReleaseNotes', #'pre-install', #'post-install', #'pyformex.desktop', # 'pyformex-viewer', # 'pyformex-search', 'manifest.py', 'setup.py', 'setup.cfg', ] + \ listTree('pyformex',listdirs=False,sorted=True, excludedirs=['.svn'], includedirs=['gui','plugins'], includefiles=['.*\.py$','pyformex(rc)?$'] #,'pyformex-search'] ) + \ listTree('pyformex/icons',listdirs=False,sorted=True, excludedirs=['.svn'], includefiles=['README','.*\.xpm$','pyformex.*\.png$'] ) + \ listTree('pyformex/lib',listdirs=False,sorted=True, excludedirs=['.svn'], includefiles=['.*\.c$','.*\.py$'] ) + \ listTree('pyformex/examples',listdirs=False,sorted=True, excludedirs=['.svn'], excludefiles=['.*\.pyc','.*~$', 'NurbsCircle.py', # missing nurbs 'NurbsSurface.py', # idem ], includefiles=['[_A-Z].*\.py$','scripts.cat','README'] ) + \ DATA_FILES + \ DOC_FILES + \ EXTRA_FILES ## listTree('pyformex/bin',listdirs=False,sorted=True, ## excludedirs=['.svn'], ## excludefiles=['.*~$'], ## ) for i in OTHER_DATA: DIST_FILES += i[1] if __name__ == '__main__': import sys todo = sys.argv[1:] if not todo: todo = ['doc','data','dist'] for a in todo: if a == 'doc': print "=========DOC_FILES=========" print '\n'.join(DOC_FILES) elif a == 'data': print "=========DATA_FILES========" print '\n'.join(DATA_FILES) elif a == 'other': print "=========OTHER_DATA========" for i in OTHER_DATA: print '\n'.join(i[1]) else: print "=========DIST_FILES========" print '\n'.join(DIST_FILES) # End pyformex-0.8.6/COPYING0000644000211500021150000010451311662403426014275 0ustar benebene00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .