]> SALOME platform Git repositories - modules/geom.git/commitdiff
Salome HOME
Adding salome.geom python package
authorboulant <boulant>
Thu, 29 Jul 2010 09:38:04 +0000 (09:38 +0000)
committerboulant <boulant>
Thu, 29 Jul 2010 09:38:04 +0000 (09:38 +0000)
15 files changed:
configure.ac
doc/Makefile.am
doc/docutils/Makefile.am [new file with mode: 0644]
doc/docutils/conf.py [new file with mode: 0644]
doc/docutils/docapi.rst [new file with mode: 0644]
doc/docutils/index.rst [new file with mode: 0644]
doc/docutils/overview.rst [new file with mode: 0644]
src/GEOM_PY/Makefile.am [new file with mode: 0644]
src/GEOM_PY/__init__.py [new file with mode: 0644]
src/GEOM_PY/geomtools.py [new file with mode: 0644]
src/GEOM_PY/structelem/Makefile.am [new file with mode: 0644]
src/GEOM_PY/structelem/__init__.py [new file with mode: 0644]
src/GEOM_PY/structelem/orientation.py [new file with mode: 0644]
src/GEOM_PY/structelem/parts.py [new file with mode: 0644]
src/Makefile.am

index 207f596b8564a239f4c82178b03990db0ae617b2..b544c1e81d892bdebff9836b3199620ddaa64b82 100644 (file)
@@ -316,6 +316,13 @@ echo
 
 CHECK_HTML_GENERATORS
 
+echo
+echo ---------------------------------------------
+echo testing sphinx
+echo ---------------------------------------------
+echo
+CHECK_SPHINX
+
 echo
 echo ---------------------------------------------
 echo Testing Kernel
@@ -333,11 +340,11 @@ echo
 echo Configure
 
 if test "${gui_ok}" = "yes"; then
-  variables="cc_ok lex_yacc_ok python_ok swig_ok threads_ok OpenGL_ok qt_ok vtk_ok hdf5_ok omniORB_ok boost_ok occ_ok doxygen_ok graphviz_ok Kernel_ok gui_ok"
+  variables="cc_ok lex_yacc_ok python_ok swig_ok threads_ok OpenGL_ok qt_ok vtk_ok hdf5_ok omniORB_ok boost_ok occ_ok doxygen_ok graphviz_ok sphinx_ok Kernel_ok gui_ok"
 elif test "${SalomeGUI_need}" != "no"; then
-  variables="cc_ok lex_yacc_ok python_ok swig_ok threads_ok hdf5_ok omniORB_ok boost_ok occ_ok doxygen_ok graphviz_ok Kernel_ok gui_ok"
+  variables="cc_ok lex_yacc_ok python_ok swig_ok threads_ok hdf5_ok omniORB_ok boost_ok occ_ok doxygen_ok graphviz_ok sphinx_ok Kernel_ok gui_ok"
 else
-  variables="cc_ok lex_yacc_ok python_ok swig_ok threads_ok hdf5_ok omniORB_ok boost_ok occ_ok doxygen_ok graphviz_ok Kernel_ok"
+  variables="cc_ok lex_yacc_ok python_ok swig_ok threads_ok hdf5_ok omniORB_ok boost_ok occ_ok doxygen_ok graphviz_ok sphinx_ok Kernel_ok"
 fi
 
 for var in $variables
@@ -381,6 +388,7 @@ AC_OUTPUT([ \
   bin/Makefile \
   GEOM_version.h \
   doc/Makefile \
+  doc/docutils/Makefile \
   doc/salome/Makefile \
   doc/salome/gui/Makefile \
   doc/salome/gui/GEOM/Makefile \
@@ -415,6 +423,8 @@ AC_OUTPUT([ \
   src/GEOM_I_Superv/Makefile \
   src/GEOM_SWIG/Makefile \
   src/GEOM_SWIG_WITHIHM/Makefile \
+  src/GEOM_PY/Makefile \
+  src/GEOM_PY/structelem/Makefile \
   src/GenerationGUI/Makefile \
   src/GroupGUI/Makefile \
   src/IGESExport/Makefile \
index f40fb81a5d12cb8d1b1c351e41b03059a6d13c7e..6cf4c8ea217b6e586992d4136c7160cd1524791d 100644 (file)
@@ -24,7 +24,7 @@
 # $Header$
 # source path
 #
-SUBDIRS = salome
+SUBDIRS = salome docutils
 
 usr_docs:
        (cd salome && $(MAKE) $(AM_MAKEFLAGS) usr_docs)
diff --git a/doc/docutils/Makefile.am b/doc/docutils/Makefile.am
new file mode 100644 (file)
index 0000000..1fe6d62
--- /dev/null
@@ -0,0 +1,92 @@
+#  -*- coding: iso-8859-1 -*-
+#  Copyright (C) 2007-2010  CEA/DEN, EDF R&D, OPEN CASCADE
+#
+#  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+#  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License.
+#
+#  This library 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
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+#  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+
+include $(top_srcdir)/adm_local/unix/make_common_starter.am
+
+pydocdir = $(docdir)/docutils/GEOM
+
+.PHONY : latex
+
+if SPHINX_IS_OK
+
+pydoc_DATA=html/index.html
+html/index.html:$(RSTFILES)
+       make htm
+
+endif
+
+EXTRA_DIST+= html
+
+SPHINXOPTS      =
+SOURCEDIR       = $(srcdir)
+SPHINXBUILD     = sphinx-build
+PAPEROPT_a4     = -D latex_paper_size=a4
+ALLSPHINXOPTS   = -d doctrees $(PAPEROPT_a4) $(SPHINXOPTS) $(SOURCEDIR)
+
+SPHINX_PYTHONPATH = $(prefix)/lib/python$(PYTHON_VERSION)/site-packages/salome:$(KERNEL_ROOT_DIR)/bin/salome:$(KERNEL_ROOT_DIR)/lib/python$(PYTHON_VERSION)/site-packages/salome:$(OMNIORB_ROOT)/lib/python$(PYTHON_VERSION)/site-packages
+
+SPHINX_LD_LIBRARY_PATH = $(OMNIORB_ROOT)/lib
+
+htm:
+       mkdir -p html doctrees
+       PYTHONPATH=$(SPHINX_PYTHONPATH):${PYTHONPATH}; \
+       LD_LIBRARY_PATH=$(SPHINX_LD_LIBRARY_PATH):${LD_LIBRARY_PATH}; \
+       $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) html
+       @echo
+       @echo "Build finished. The HTML pages are in html."
+
+latex:
+       mkdir -p latex doctrees
+       PYTHONPATH=$(SPHINX_PYTHONPATH):${PYTHONPATH}; \
+       LD_LIBRARY_PATH=$(SPHINX_LD_LIBRARY_PATH):${LD_LIBRARY_PATH}; \
+       $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) latex
+       @echo
+       @echo "Build finished; the LaTeX files are in latex."
+       @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+             "run these through (pdf)latex."
+
+html:
+       mkdir -p $@
+
+RSTFILES= \
+       index.rst \
+       overview.rst \
+       docapi.rst
+
+EXTRA_DIST+= $(RSTFILES)
+
+EXTRA_DIST+= \
+       conf.py 
+
+install-data-local:
+       $(INSTALL) -d $(pydocdir)
+       if test -d "html"; then b=; else b="$(srcdir)/"; fi; \
+       cp -rf $$b"html"/* $(pydocdir) ; \
+       if test -f $$b"latex"/geompy.pdf; then cp -f $$b"latex"/geompy.pdf $(pydocdir) ; fi;
+
+uninstall-local:
+       chmod -R +w $(pydocdir)
+       rm -rf $(pydocdir)/*
+
+clean-local:
+       -rm -rf html latex doctrees
+       if test -d "html"; then rm -rf html ; fi
diff --git a/doc/docutils/conf.py b/doc/docutils/conf.py
new file mode 100644 (file)
index 0000000..5afa71b
--- /dev/null
@@ -0,0 +1,200 @@
+# -*- coding: iso-8859-1 -*-
+#
+# yacs documentation build configuration file, created by
+# sphinx-quickstart on Fri Aug 29 09:57:25 2008.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Uncomment the following line to build the links with Python documentation
+# (you might need to set http_proxy environment variable for this to work)
+#extensions += ['sphinx.ext.intersphinx']
+
+# Intersphinx mapping to add links to modules and objects in the Python
+# standard library documentation
+intersphinx_mapping = {'http://docs.python.org': None}
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'GEOM python packages'
+copyright = '2010 EDF R&D'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '5.1.4'
+# The full version, including alpha/beta/rc tags.
+release = '5.1.4'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+language = 'en'
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['.build','ref','images','CVS','.svn']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+html_theme = 'default'
+#html_theme = 'nature'
+#html_theme = 'agogo'
+#html_theme = 'sphinxdoc'
+#html_theme = 'omadoc'
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = ['themes']
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+html_use_modindex = False
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'geompydoc'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+latex_paper_size = 'a4'
+
+# The font size ('10pt', '11pt' or '12pt').
+latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+  ('index', 'geompy.tex', 'Documentation of the GEOM python packages', 'EDF R\&D', 'manual')
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+latex_logo = '../salome/tui/images/head.png'
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = True
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+latex_use_modindex = False
diff --git a/doc/docutils/docapi.rst b/doc/docutils/docapi.rst
new file mode 100644 (file)
index 0000000..20a52ca
--- /dev/null
@@ -0,0 +1,43 @@
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ Documentation of the programming interface (API)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+This section describes the python packages and modules of the
+``salome.geom`` python package. The main part is generated from the
+code documentation included in source python files.
+
+:mod:`salome.geom` -- Package containing the GEOM python utilities
+==================================================================
+
+:mod:`geomtools` -- Tools to access GEOM engine and objects
+-----------------------------------------------------------
+
+.. automodule:: salome.geom.geomtools
+   :members:
+
+:mod:`structelem` -- Structural elements package
+------------------------------------------------
+
+.. automodule:: salome.geom.structelem
+
+.. autoclass:: StructuralElementManager
+   :members:
+
+.. autoclass:: StructuralElement
+   :members:
+
+:mod:`structelem.parts` -- Structural element parts
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. automodule:: salome.geom.structelem.parts
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+:mod:`structelem.orientation` -- Structural element orientation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. automodule:: salome.geom.structelem.orientation
+   :members:
+   :undoc-members:
diff --git a/doc/docutils/index.rst b/doc/docutils/index.rst
new file mode 100644 (file)
index 0000000..81e4f24
--- /dev/null
@@ -0,0 +1,14 @@
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ Documentation of the GEOM python packages
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+Main documentation
+==================
+
+.. toctree::
+   :maxdepth: 3
+
+   overview.rst
+   docapi.rst
+
diff --git a/doc/docutils/overview.rst b/doc/docutils/overview.rst
new file mode 100644 (file)
index 0000000..4742ea7
--- /dev/null
@@ -0,0 +1,31 @@
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+General presentation of the GEOM python package
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+The GEOM python package essentially contains:
+
+* The visualization of structural elements: a function to create
+  geometrical 3D representations of mechanical models called
+  "structural elements".
+
+Note that these functions either encapsulate the python programming
+interface of GEOM core (the CORBA or SWIG interfaces for example) or
+extend existing utilities as the ``geompy.py`` module.
+
+The functions are distributed in the python package
+``salome.geom``. For example, the usage of the visualization of
+structural elements can be appreciated with this set of instructions:
+
+ >>> from salome.geom.structelem import TEST_StructuralElement
+ >>> TEST_StructuralElement()
+
+The specification of the programming interface of this package is
+detailled in the part :doc:`Documentation of the programming interface
+(API)</docapi>` of this documentation.
+
+.. note::
+   The main package ``salome`` contains other sub-packages that are
+   distributed with the other SALOME modules. For example, the KERNEL
+   module provides the python package ``salome.kernel`` and SMESH the
+   package ``salome.smesh``.
diff --git a/src/GEOM_PY/Makefile.am b/src/GEOM_PY/Makefile.am
new file mode 100644 (file)
index 0000000..a3b6695
--- /dev/null
@@ -0,0 +1,27 @@
+#  Copyright (C) 2007-2010  CEA/DEN, EDF R&D, OPEN CASCADE
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License.
+#
+#  This library 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
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+#  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+include $(top_srcdir)/adm_local/unix/make_common_starter.am
+
+SUBDIRS= structelem
+
+salomepypkgdir = $(salomepythondir)/salome/geom
+salomepypkg_PYTHON = \
+       __init__.py \
+       geomtools.py
diff --git a/src/GEOM_PY/__init__.py b/src/GEOM_PY/__init__.py
new file mode 100644 (file)
index 0000000..5d3edfa
--- /dev/null
@@ -0,0 +1 @@
+# -*- coding: iso-8859-1 -*-
diff --git a/src/GEOM_PY/geomtools.py b/src/GEOM_PY/geomtools.py
new file mode 100644 (file)
index 0000000..9693525
--- /dev/null
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright (C) 2007-2009     EDF R&D
+# 
+#    This file is part of PAL_SRC.
+#
+#    PAL_SRC 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 2 of the License, or
+#    (at your option) any later version.
+#
+#    PAL_SRC 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 PAL_SRC; if not, write to the Free Software
+#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+#
+"""
+This module provides tools to facilitate the use of geom engine and geom
+objects in Salome.
+"""
+
+import salome
+GEOM = None    # GEOM module is loaded only when needed
+
+from salome.kernel.logger import Logger
+from salome.kernel import termcolor
+logger = Logger("salome.geom.geomtools", color = termcolor.RED)
+
+from salome.kernel.studyedit import getActiveStudyId, getStudyEditor
+
+_geompys = {}
+
+def getGeompy(studyId = None):
+    """
+    Return an object behaving exactly like geompy module, except that it is
+    associated with the study `studyId`. If `studyId` is :const:`None`, return
+    a pseudo geompy object for the current study.
+    """
+    # We can't use geompy module because it initializes GEOM with
+    # salome.myStudy, which may not exist. So we use this trick to create
+    # a pseudo geompy module. 
+    salome.salome_init()
+    if studyId is None:
+        studyId = getActiveStudyId()
+    if not _geompys.has_key(studyId):
+        import geompyDC
+        _geompys[studyId] = salome.lcc.FindOrLoadComponent("FactoryServer",
+                                                           "GEOM")
+        _geompys[studyId].ShapeType = geompyDC.ShapeType
+        _geompys[studyId].GEOM = geompyDC.GEOM
+        _geompys[studyId].kind = geompyDC.kind
+        _geompys[studyId].info = geompyDC.info
+        _geompys[studyId].PackData = geompyDC.PackData
+        _geompys[studyId].ReadTexture = geompyDC.ReadTexture
+        study = salome.myStudyManager.GetStudyByID(studyId)
+        _geompys[studyId].init_geom(study)
+    return _geompys[studyId]
+
+
+class GeomStudyTools:
+    """
+    This class provides several methods to manipulate geom objects in Salome
+    study. The parameter `studyEditor` defines a
+    :class:`~salome.kernel.studyedit.StudyEditor` object used to access the study. If
+    :const:`None`, the method returns a :class:`~salome.kernel.studyedit.StudyEditor`
+    object on the current study.
+
+    .. attribute:: editor
+    
+       This instance attribute contains the underlying
+       :class:`~salome.kernel.studyedit.StudyEditor` object. It can be used to access
+       the study but the attribute itself should not be modified.
+
+    """
+
+    def __init__(self, studyEditor = None):
+        global GEOM
+        if GEOM is None:
+            GEOM = __import__("GEOM")
+        if studyEditor is None:
+            studyEditor = getStudyEditor()
+        self.editor = studyEditor
+
+    def displayShapeByName(self, shapeName, color = None):
+        """
+        Display the geometrical shape whose name in the study is `shapeName`.
+        
+        :type   shapeName: string
+        :param  shapeName: name of the geometrical shape
+        
+        :type   color: tuple (triplet)
+        :param  color: RGB components of the color of the shape
+        
+        :return: True if the shape was found, False otherwise
+        """
+        logger.debug("displayShapeByName in PAL: %s with color %s" %
+                     (shapeName, color))
+        listSO = self.editor.study.FindObjectByName(shapeName, "GEOM")
+        for sObj in listSO:
+            entry = sObj.GetID()
+            geomObj = self.editor.getOrLoadObject(sObj)
+            if geomObj:
+                shape = geomObj._narrow(GEOM.GEOM_Object)
+                if shape:                
+                    geomgui = salome.ImportComponentGUI("GEOM")            
+                    geomgui.createAndDisplayGO(entry)
+                    geomgui.setDisplayMode(entry, 1)
+                    if color is not None:
+                        geomgui.setColor(entry, color[0], color[1], color[2])
+                    return True
+        return False
diff --git a/src/GEOM_PY/structelem/Makefile.am b/src/GEOM_PY/structelem/Makefile.am
new file mode 100644 (file)
index 0000000..cea34db
--- /dev/null
@@ -0,0 +1,26 @@
+#  Copyright (C) 2007-2010  CEA/DEN, EDF R&D, OPEN CASCADE
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License.
+#
+#  This library 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
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+#  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+include $(top_srcdir)/adm_local/unix/make_common_starter.am
+
+salomepypkgdir = $(salomepythondir)/salome/geom/structelem
+salomepypkg_PYTHON = \
+       __init__.py \
+       parts.py \
+       orientation.py
diff --git a/src/GEOM_PY/structelem/__init__.py b/src/GEOM_PY/structelem/__init__.py
new file mode 100644 (file)
index 0000000..e713832
--- /dev/null
@@ -0,0 +1,486 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright (C) 2007-2009      EDF R&D
+# 
+#    This file is part of PAL_SRC.
+#
+#    PAL_SRC 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 2 of the License, or
+#    (at your option) any later version.
+#
+#    PAL_SRC 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 PAL_SRC; if not, write to the Free Software
+#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+#
+"""
+This package is used to create and visualize structural elements. It contains
+three modules:
+
+* This module :mod:`salome.geom.structelem` defines the main classes
+  :class:`StructuralElement` and :class:`StructuralElementManager` that can be
+  directly used to build structural elements.
+* The module :mod:`salome.geom.structelem.parts` defines the classes corresponding to
+  the different parts (beams, grids, etc.) that make up a structural element.
+  It is used to build the geometric shapes in the structural element.
+* The module :mod:`salome.geom.structelem.orientation` defines the classes that are
+  used to compute the orientation of the structural element parts and to build
+  the corresponding markers.
+
+A structural element is a set of geometric shapes (beams, grids, etc.) that
+are built semi-automatically along a set of geometric primitives (edges for
+instance). They are visualized with the same color as their base primitives in
+the geom viewer.
+
+Structural elements are generally created by the
+:class:`StructuralElementManager` class, from a list of commands describing
+the element to create.
+
+Example::
+
+    commandList = [('VisuPoutreGenerale', {'Group_Maille': 'Edge_1'}),
+                   ('VisuBarreCercle',
+                    {'R': 30, 'Group_Maille': 'Edge_1', 'EP': 15}),
+                  ]
+
+    structElemManager = StructuralElementManager()
+    elem = structElemManager.createElement(commandList)
+    elem.display()
+    salome.sg.updateObjBrowser(True)
+
+"""
+
+import types
+
+import salome
+
+from salome.kernel.logger import Logger
+from salome.kernel import termcolor
+logger = Logger("salome.geom.structelem", color = termcolor.RED)
+from salome.kernel.studyedit import getStudyEditor
+
+__all__ = ["parts", "orientation"]
+
+from salome.geom.structelem import parts
+from salome.geom.structelem.parts import InvalidParameterError
+
+class StructuralElementManager:
+    """
+    This class manages the structural elements in the study. It is used to
+    create a new structural element from a list of commands. The parameter
+    `studyId` defines the ID of the study in which the manager will create
+    structural elements. If it is :const:`None` or not specified, it will use
+    the ID of the current study as defined by
+    :func:`salome.kernel.studyedit.getActiveStudyId` function.
+    """
+    def __init__(self, studyId = None):
+        self._studyEditor = getStudyEditor(studyId)
+
+    def createElement(self, commandList):
+        """
+        Create a structural element from the list of commands `commandList`.
+        Each command in this list represent a part of the structural element,
+        that is a specific kind of shape (circular beam, grid, etc.)
+        associated with one or several geometrical primitives. A command must
+        be a tuple. The first element is the structural element part class
+        name or alias name. The second element is a dictionary containing the
+        parameters describing the part. Valid class names are all the classes
+        defined in the module :mod:`~salome.geom.structelem.parts` and inheriting
+        class :class:`~parts.StructuralElementPart`. There are also several
+        aliases for backward compatibility. Here is the complete list:
+        
+        * :class:`~parts.GeneralBeam`
+        * :class:`~parts.CircularBeam`
+        * :class:`~parts.RectangularBeam`
+        * :class:`~parts.ThickShell`
+        * :class:`~parts.Grid`
+
+        * :func:`~parts.VisuPoutreGenerale` (alias for
+          :class:`~parts.GeneralBeam`)
+        * :func:`~parts.VisuPoutreCercle` (alias for
+          :class:`~parts.CircularBeam`)
+        * :func:`~parts.VisuPoutreRectangle` (alias for
+          :class:`~parts.RectangularBeam`)
+        * :func:`~parts.VisuBarreGenerale` (alias for
+          :class:`~parts.GeneralBeam`)
+        * :func:`~parts.VisuBarreRectangle` (alias for
+          :class:`~parts.RectangularBeam`)
+        * :func:`~parts.VisuBarreCercle` (alias for
+          :class:`~parts.CircularBeam`)
+        * :func:`~parts.VisuCable` (alias for :class:`~parts.CircularBeam`)
+        * :func:`~parts.VisuCoque` (alias for :class:`~parts.ThickShell`)
+        * :func:`~parts.VisuGrille` (alias for :class:`~parts.Grid`)
+        
+        * ``Orientation``: This identifier is used to specify the orientation
+          of one or several 1D structural element parts (i.e. beams). The
+          parameters are described in class
+          :class:`~orientation.Orientation1D`.
+
+        The valid parameters in the dictionary depend on the type of the
+        structural element part, and are detailed in the documentation of
+        the corresponding class. The only parameter that is common to all the
+        classes is "MeshGroups" (that can also be named "Group_Maille"). It
+        defines the name of the geometrical object(s) in the study that will
+        be used as primitives to build the structural element part. This
+        parameter can be either a list of strings or a single string with
+        comma separated names.
+        """
+        logger.debug("StructuralElementManager.createElement: START")
+        logger.debug("Command list: %s" % commandList)
+
+        element = StructuralElement(self._studyEditor.studyId)
+        orientationCmdList = []
+        for command in commandList:
+            (parttype, parameters) = command
+            if parttype == "Orientation":
+                orientationCmdList += [command]
+            elif parttype not in dir(parts):
+                logger.warning('Invalid structural element part name "%s"'
+                               ' in command %s, this command will be '
+                               'ignored.' % (parttype, command))
+            else:
+                (meshGroupList, newparams) = self._extractMeshGroups(command)
+                for meshGroup in meshGroupList:
+                    # Get the geometrical primitive object
+                    groupSObj = self._studyEditor.study.FindObject(meshGroup)
+                    groupGeomObj = None
+                    if groupSObj is not None:
+                        groupGeomObj = \
+                                self._studyEditor.getOrLoadObject(groupSObj)
+                    if groupGeomObj is None:
+                        logger.error("Can't get geom object corresponding to "
+                                     'mesh group "%s", structural element '
+                                     "part %s will not be built." %
+                                     (groupName, part))
+                        continue
+                    
+                    # Create the part
+                    try:
+                        part = parts.__dict__[parttype](
+                                        self._studyEditor.studyId, meshGroup,
+                                        groupGeomObj, newparams)
+                        element.addPart(part)
+                    except InvalidParameterError, e:
+                        logger.error("Invalid parameter error: %s" % e)
+                        raise
+                    except:
+                        logger.exception("Can't create structural element"
+                                         " part with command %s." %
+                                         str(command))
+
+        # Orientations are parsed after the parts because they must be
+        # associated with existing parts.
+        for command in orientationCmdList:
+            (parttype, parameters) = command
+            (meshGroupList, orientParams) = self._extractMeshGroups(command)
+            for meshGroup in meshGroupList:
+                element.addOrientation(meshGroup, orientParams)
+
+        element.build()
+        logger.debug("StructuralElementManager.createElement: END")
+        return element
+    
+    def _extractMeshGroups(self, command):
+        """
+        This method extracts the names of the mesh groups (i.e. the
+        geometrical objects used to build the structural element part) in the
+        command in parameter. It returns a tuple containing the mesh groups as
+        a list of strings and the other parameters of the command as a new
+        dictionary.
+        """
+        (parttype, parameters) = command
+        newparams = parameters.copy()
+        groupMailleParam = newparams.pop("Group_Maille", None)
+        meshGroupParam = newparams.pop("MeshGroups", None)
+        if groupMailleParam is None and meshGroupParam is None:
+            logger.warning("No mesh group specified in command %s, this "
+                           "command will be ignored." % command)
+            return ([], newparams)
+        elif groupMailleParam is not None and meshGroupParam is not None:
+            logger.warning('Both "MeshGroups" and "Group_Maille" specified in'
+                           ' command %s, only "MeshGroups" will be used.' %
+                           command)
+        elif groupMailleParam is not None and meshGroupParam is None:
+            meshGroupParam = groupMailleParam
+        
+        meshGroupList = []
+        if type(meshGroupParam) == types.StringType:
+            meshGroupList = self._getMeshGroupListFromString(meshGroupParam)
+        else:
+            for item in meshGroupParam:
+                meshGroupList += self._getMeshGroupListFromString(item)
+        
+        if len(meshGroupList) == 0:
+            logger.warning("Mesh group list is empty in command %s, this "
+                           "command will be ignored." % command)
+
+        return (meshGroupList, newparams)
+    
+    def _getMeshGroupListFromString(self, meshString):
+        """
+        This method splits the string in parameter to extract comma separated
+        names. Those names are returned as a list of strings.
+        """
+        meshGroupList = []
+        list = meshString.split(",")
+        for item in list:
+            strippedItem = item.strip()
+            if len(strippedItem) > 0:
+                meshGroupList.append(strippedItem)
+        return meshGroupList
+
+
+class StructuralElement:
+    """
+    This class represents a structural element, i.e. a set of geometrical
+    objects built along geometrical primitives. The parameter `studyId`
+    defines the ID of the study that will contain the structural element. If
+    it is :const:`None` or not specified, the constructor will use the ID of
+    the active study as defined by :func:`salome.kernel.studyedit.getActiveStudyId`
+    function. Structural elements are normally created by the class
+    :class:`StructuralElementManager`, so this class should not be
+    instantiated directly in the general case.
+    """
+    _counter = 1
+    _mainFolderTag = 14725
+
+    def __init__(self, studyId = None):
+        # _parts is the dictionary mapping group name to structural element
+        # part. _shapeDict is the dictionary mapping SubShapeID objects to
+        # structural element parts. Both are used to avoid duplicate shapes
+        # in structural elements.
+        self._parts = {}
+        self._shapeDict = {}
+        self._id = StructuralElement._counter
+        StructuralElement._counter += 1
+        self._studyEditor = getStudyEditor(studyId)
+        logger.debug("Creating structural element in study %s" %
+                     self._studyEditor.studyId)
+        self._SObject = None
+
+    def _getSObject(self):
+        """
+        Find or create the study object corresponding to the structural
+        element. This object is named "SE_N" where N is a numerical ID. 
+        """
+        if self._SObject is None:
+            geomComponent = self._studyEditor.study.FindComponent("GEOM")
+            mainFolder = self._studyEditor.setItemAtTag(geomComponent,
+                                            StructuralElement._mainFolderTag,
+                                            name = "Structural Elements")
+            self._SObject = self._studyEditor.findOrCreateItem(mainFolder,
+                                            name = "SE_" + str(self._id))
+        return self._SObject
+
+    def addPart(self, newpart):
+        """
+        Add a part to the structural element.
+
+        :type  newpart: :class:`~parts.StructuralElementPart`
+        :param newpart: the part to add to the structural element.
+
+        """
+        newshapes = newpart.baseShapesSet
+
+        # Check duplicate groups
+        if self._parts.has_key(newpart.groupName):
+            logger.warning('Mesh group "%s" is used several times in the '
+                           'structural element. Only the last definition '
+                           'will be used.' % newpart.groupName)
+        else:
+            # Check duplicate shapes
+            intersect = newshapes.intersection(self._shapeDict.keys())
+            while len(intersect) > 0:
+                shape, = intersect
+                oldpartwithshape = self._shapeDict[shape]
+                oldpartshapes = oldpartwithshape.baseShapesSet
+                intersectwitholdpart = intersect.intersection(oldpartshapes)
+                logger.warning('Some shapes are common to groups "%s" and '
+                               '"%s". For those, the parameters defined for '
+                               '"%s" will be used.' %
+                               (oldpartwithshape.groupName, newpart.groupName,
+                                newpart.groupName))
+                oldpartwithshape.baseShapesSet = \
+                                oldpartshapes.difference(intersectwitholdpart)
+                intersect = intersect.difference(intersectwitholdpart)
+
+        # Finally add the new part in the structural element
+        self._parts[newpart.groupName] = newpart
+        for shape in newshapes:
+            self._shapeDict[shape] = newpart
+
+    def addOrientation(self, meshGroup, orientParams):
+        """
+        Add orientation information to a part in the structural element. This
+        information will be used to build the corresponding markers.
+
+        :type  meshGroup: string
+        :param meshGroup: the name of a geometrical primitive. The orientation
+                          information will apply to the structural element
+                          part built along this primitive.
+
+        :type  orientParams: dictionary
+        :param orientParams: parameters defining the orientation of the
+                             structural element part. Those parameters are
+                             detailed in class
+                             :class:`~orientation.Orientation1D`.
+
+        """
+        if self._parts.has_key(meshGroup):
+            self._parts[meshGroup].addOrientation(orientParams)
+        else:
+            logger.warning('Mesh group "%s" not found in structural element, '
+                           'cannot set orientation.' % meshGroup)
+
+    def build(self):
+        """
+        Build the geometric shapes and the markers corresponding to the
+        different parts of the structural element, and add them to the study.
+        """
+        gg = salome.ImportComponentGUI("GEOM")
+        for part in self._parts.itervalues():
+            # Build the structural element part
+            logger.debug("Building %s" % part)
+            try:
+                (shape, markers) = part.build()
+                if shape is None:
+                    logger.error("Part %s has not been built" % part)
+                    continue
+            except:
+                logger.exception("Couldn't build part %s" % part)
+                continue
+            
+            # Add the new objects to the study
+            IOR = self._studyEditor.study.ConvertObjectToIOR(shape)
+            shapeSObjName = part.name + "_" + part.groupName
+            icon = None
+            if salome.hasDesktop():
+                icon = gg.getShapeTypeIcon(IOR)
+            shapeSObj = self._studyEditor.createItem(self._getSObject(),
+                                            name = shapeSObjName, IOR = IOR,
+                                            icon = icon)
+            if markers is not None and len(markers) > 0:
+                i = 1
+                for marker in markers:
+                    markerIOR = \
+                            self._studyEditor.study.ConvertObjectToIOR(marker)
+                    markerSObjName = "Orient_" + shapeSObjName
+                    if len(markers) > 1:
+                        markerSObjName += "_%d" % i
+                    markerSObj = self._studyEditor.createItem(
+                                                self._getSObject(),
+                                                name = markerSObjName,
+                                                IOR = markerIOR,
+                                                icon = "ICON_OBJBROWSER_LCS")
+                    i += 1
+
+    def display(self):
+        """
+        Display the structural element in the geom view.
+        """
+        StructuralElement.showElement(self._SObject)
+
+    @staticmethod
+    def showElement(theSObject):
+        """
+        Display the structural element corresponding to the study object
+        `theSObject`
+        """
+        if theSObject is not None:
+            gg = salome.ImportComponentGUI("GEOM")
+            aStudy = theSObject.GetStudy()
+            editor = getStudyEditor(aStudy._get_StudyId())
+            aIterator = aStudy.NewChildIterator(theSObject)
+            aIterator.Init()
+            while aIterator.More():
+                sobj = aIterator.Value()
+                icon = editor.getIcon(sobj)
+                if icon != "ICON_OBJBROWSER_LCS":
+                    entry = aIterator.Value().GetID()
+                    gg.createAndDisplayGO(entry)
+                    gg.setDisplayMode(entry, 1)
+                aIterator.Next()
+
+
+def TEST_CreateGeometry():
+    import geompy
+    import SALOMEDS
+    geompy.init_geom(salome.myStudy)
+    Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200)
+    edges = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["EDGE"])
+    edges[0].SetColor(SALOMEDS.Color(1.0,0.0,0.0))
+    edges[1].SetColor(SALOMEDS.Color(0.0,1.0,0.0))
+    edges[2].SetColor(SALOMEDS.Color(0.0,0.0,1.0))
+    edges[3].SetColor(SALOMEDS.Color(1.0,0.0,1.0))
+    edges[4].SetColor(SALOMEDS.Color(0.0,1.0,1.0))
+    edges[5].SetColor(SALOMEDS.Color(0.5,0.0,0.0))
+    edges[6].SetColor(SALOMEDS.Color(0.0,0.5,0.0))
+    edges[7].SetColor(SALOMEDS.Color(0.0,0.0,0.5))
+    geompy.addToStudy(Box_1, "Box_1")
+    for i in range(len(edges)):
+        geompy.addToStudyInFather(Box_1, edges[i], "Edge_%d" % i)
+    faces = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["FACE"])
+    faces[3].SetColor(SALOMEDS.Color(1.0,0.5,0.0))
+    faces[4].SetColor(SALOMEDS.Color(0.0,1.0,0.5))
+    for i in range(len(faces)):
+        geompy.addToStudyInFather(Box_1, faces[i], "Face_%d" % i)
+    Cylinder_1 = geompy.MakeCylinderRH(50, 200)
+    geompy.TranslateDXDYDZ(Cylinder_1, 300, 300, 0)
+    cyl_faces = geompy.SubShapeAllSorted(Cylinder_1, geompy.ShapeType["FACE"])
+    geompy.addToStudy(Cylinder_1, "Cylinder_1")
+    for i in range(len(cyl_faces)):
+        geompy.addToStudyInFather(Cylinder_1, cyl_faces[i], "CylFace_%d" % i)
+    Cylinder_2 = geompy.MakeTranslation(Cylinder_1, 100, 100, 0)
+    cyl_faces2 = geompy.SubShapeAllSorted(Cylinder_2,
+                                          geompy.ShapeType["FACE"])
+    geompy.addToStudy(Cylinder_2, "Cylinder_2")
+    for i in range(len(cyl_faces2)):
+        geompy.addToStudyInFather(Cylinder_2, cyl_faces2[i],
+                                  "CylFace2_%d" % i)
+
+
+def TEST_StructuralElement():
+    salome.salome_init()
+    TEST_CreateGeometry()
+    liste_commandes = [('Orientation', {'MeshGroups': 'Edge_4',
+                                        'VECT_Y': (1.0, 0.0, 1.0)}),
+                       ('Orientation', {'MeshGroups': 'Edge_5',
+                                        'ANGL_VRIL': 45.0}),
+                       ('GeneralBeam', {'MeshGroups': 'Edge_1, Edge_7'}),
+                       ('VisuPoutreCercle', {'MeshGroups': ['Edge_6'],
+                                             'R1': 30, 'R2': 20}),
+                       ('CircularBeam', {'MeshGroups': ['Edge_2', 'Edge_3'],
+                                         'R': 40, 'EP': 20}),
+                       ('RectangularBeam', {'MeshGroups': 'Edge_4, Edge_5',
+                                            'HZ1': 60, 'HY1': 40,
+                                            'EPZ1': 15, 'EPY1': 10,
+                                            'HZ2': 40, 'HY2': 60,
+                                            'EPZ2': 10, 'EPY2': 15}),
+                       ('VisuCable', {'MeshGroups': 'Edge_7', 'R': 5}),
+                       ('VisuCoque', {'MeshGroups': 'Face_4',
+                                      'Epais': 10, 'Excentre': 5,
+                                      'angleAlpha': 45, 'angleBeta': 60}),
+                       ('VisuCoque', {'MeshGroups': 'CylFace_2', 'Epais': 5}),
+                       ('VisuGrille', {'MeshGroups': 'Face_5', 'Excentre': 5,
+                                       'angleAlpha': 45, 'angleBeta': 60}),
+                       ('VisuGrille', {'MeshGroups': 'CylFace2_2',
+                                       'Excentre': 5, 'origAxeX': 400,
+                                       'origAxeY': 400, 'origAxeZ': 0,
+                                       'axeX': 0, 'axeY': 0, 'axeZ': 100}),
+                      ]
+
+    structElemManager = StructuralElementManager()
+    elem = structElemManager.createElement(liste_commandes)
+    if salome.hasDesktop():
+        elem.display()
+        salome.sg.updateObjBrowser(True)
+
+
+# Main function only used to test the module
+if __name__ == "__main__":
+    TEST_StructuralElement()
diff --git a/src/GEOM_PY/structelem/orientation.py b/src/GEOM_PY/structelem/orientation.py
new file mode 100644 (file)
index 0000000..1bf1117
--- /dev/null
@@ -0,0 +1,249 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright (C) 2007-2009      EDF R&D
+# 
+#    This file is part of PAL_SRC.
+#
+#    PAL_SRC 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 2 of the License, or
+#    (at your option) any later version.
+#
+#    PAL_SRC 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 PAL_SRC; if not, write to the Free Software
+#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+#
+"""
+This module is used to compute the orientation of the different parts in a
+structural element and to build the corresponding markers (trihedrons).
+"""
+
+import math
+
+from salome.kernel.logger import Logger
+from salome.kernel import termcolor
+logger = Logger("__PAL_GEOM__.structelem.orientation", color = termcolor.RED)
+
+
+class Orientation1D:
+    """
+    This class is used to compute the orientation of 1D elements and to build
+    the corresponding markers.
+    """
+    
+    def __init__(self):
+        self.geom = None
+        self._vectorYCoords = None
+        self._angle = 0.0
+
+    def __repr__(self):
+        reprdict = self.__dict__.copy()
+        del reprdict["geom"]
+        return '%s(%s)' % (self.__class__.__name__, reprdict)
+
+    def addParams(self, params):
+        """
+        Add orientation parameters. `params` is a dictionary containing one or
+        several orientation parameters. The valid parameters are:
+
+        * "VECT_Y": Triplet defining the local Y axis (the X axis is the
+          main direction of the 1D element).
+        * "ANGL_VRIL": Angle of rotation along the X axis to define the local
+          coordinate system.
+        
+        The parameters can be specified several times. In this case, only the
+        first "VECT_Y" is taken into account, and the values of "ANGL_VRIL"
+        are added to obtain the total rotation angle.
+        """
+        mydict = params.copy()
+        if mydict.has_key("VECT_Y"):
+            newVecCoords = mydict.pop("VECT_Y")
+            if self._vectorYCoords is None:
+                logger.debug("Setting orientation vector Y to %s" %
+                             str(newVecCoords))
+                self._vectorYCoords = newVecCoords
+            else:
+                logger.warning('Orientation parameter "VECT_Y" is specified '
+                               'several times for the same mesh group, vector'
+                               ' %s will be used' % str(self._vectorYCoords))
+        if mydict.has_key("ANGL_VRIL"):
+            newAngle = mydict.pop("ANGL_VRIL")
+            self._angle += newAngle
+            logger.debug("Adding angle %f to orientation, new angle is %f." %
+                         (newAngle, self._angle))
+        if len(mydict) > 0:
+            logger.warning("Invalid orientation parameter(s) (ignored): %s" %
+                           str(mydict))
+
+    def _buildDefaultMarker(self, center, vecX):
+        """
+        Create the default marker, that use the main direction of the 1D
+        object as the local X axis and the global Z axis to determine the
+        local Z axis.
+        """
+        xPoint = self.geom.MakeTranslationVector(center, vecX)
+        givenVecZ = self.geom.MakeVectorDXDYDZ(0.0, 0.0, 1.0)
+        angle = self.geom.GetAngleRadians(vecX, givenVecZ)
+        if abs(angle) < 1e-7 or abs(angle - math.pi) < 1e-7:
+            logger.warning("Beam X axis is colinear to absolute Z axis. "
+                           "Absolute X axis will be used to determine "
+                           "local Z axis.")
+            givenVecZ = self.geom.MakeVectorDXDYDZ(1.0, 0.0, 0.0)
+        zPoint = self.geom.MakeTranslationVector(center, givenVecZ)
+        locPlaneZX = self.geom.MakePlaneThreePnt(center, zPoint, xPoint, 1.0)
+        locY = self.geom.GetNormal(locPlaneZX)
+        marker = self.geom.MakeMarkerPntTwoVec(center,vecX,locY)
+        return marker
+
+    def buildMarker(self, geom, center, vecX):
+        """
+        Create a marker with origin `center` and X axis `vecX`. `geom` is the
+        pseudo-geompy object used to build the geometric shapes.
+        """
+        self.geom = geom
+        marker = None
+        if self._vectorYCoords is None:
+            marker = self._buildDefaultMarker(center, vecX)
+        else:
+            xPoint = self.geom.MakeTranslationVector(center, vecX)
+            givenLocY = self.geom.MakeVectorDXDYDZ(self._vectorYCoords[0],
+                                                   self._vectorYCoords[1],
+                                                   self._vectorYCoords[2])
+            angle = self.geom.GetAngleRadians(vecX, givenLocY)
+            if abs(angle) < 1e-7 or abs(angle - math.pi) < 1e-7:
+                logger.warning("Vector Y is colinear to the beam X axis, "
+                               "using default LCS.")
+                marker = self._buildDefaultMarker(center, vecX)
+            else:
+                yPoint = self.geom.MakeTranslationVector(center, givenLocY)
+                locPlaneXY = self.geom.MakePlaneThreePnt(center, xPoint,
+                                                         yPoint, 1.0)
+                locZ = self.geom.GetNormal(locPlaneXY)
+                zPoint = self.geom.MakeTranslationVector(center, locZ)
+                locPlaneZX = self.geom.MakePlaneThreePnt(center, zPoint,
+                                                         xPoint, 1.0)
+                locY = self.geom.GetNormal(locPlaneZX)
+                marker = self.geom.MakeMarkerPntTwoVec(center,vecX,locY)
+
+        if self._angle != 0.0:
+            angleRad = math.radians(self._angle)
+            marker = self.geom.Rotate(marker, vecX, angleRad)
+
+        return marker
+
+
+class Orientation2D:
+    """
+    This class is used to compute the orientation of 2D elements and to build
+    the corresponding markers. Angles `alpha` and `beta` are used to determine
+    the local coordinate system for the 2D element. If `vect` is not
+    :const:`None`, it is used instead of `alpha` and `beta`.
+    """
+
+    def __init__(self, alpha, beta, vect):
+        self.geom = None
+        self._alpha = alpha
+        self._beta = beta
+        self._vect = vect
+
+    def __repr__(self):
+        reprdict = self.__dict__.copy()
+        del reprdict["geom"]
+        return '%s(%s)' % (self.__class__.__name__, reprdict)
+
+    def _buildDefaultMarker(self, center, normal, warnings = True):
+        """
+        Create the default marker, that use the normal vector of the 2D object
+        as the local Z axis and the global X axis to determine the local X
+        axis. `warnings` can be used to enable or disable the logging of
+        warning messages.
+        """
+        marker = None
+        globalVecX = self.geom.MakeVectorDXDYDZ(1.0, 0.0, 0.0)
+        angle = self.geom.GetAngleRadians(normal, globalVecX)
+        if abs(angle) < 1e-7 or abs(angle - math.pi) < 1e-7:
+            if warnings:
+                logger.warning("Face normal is colinear to absolute X axis. "
+                               "Absolute Y axis will be used to determine "
+                               "local X axis.")
+            globalVecY = self.geom.MakeVectorDXDYDZ(0.0, 1.0, 0.0)
+            marker = self._buildMarkerRefVecX(center, normal, globalVecY)
+        else:
+            marker = self._buildMarkerRefVecX(center, normal, globalVecX)
+        return marker
+
+    def _buildMarkerRefVecX(self, center, normal, refVecX):
+        """
+        Create a marker using `normal` as Z axis and `refVecX` to determine
+        the X axis.
+        """
+        xPoint = self.geom.MakeTranslationVector(center, refVecX)
+        zPoint = self.geom.MakeTranslationVector(center, normal)
+        locPlaneZX = self.geom.MakePlaneThreePnt(center, zPoint, xPoint, 1.0)
+        locY = self.geom.GetNormal(locPlaneZX)
+        yPoint = self.geom.MakeTranslationVector(center, locY)
+        locPlaneYZ = self.geom.MakePlaneThreePnt(center, yPoint, zPoint, 1.0)
+        locX = self.geom.GetNormal(locPlaneYZ)
+        marker = self.geom.MakeMarkerPntTwoVec(center, locX, locY)
+        return marker
+
+    def buildMarker(self, geom, center, normal, warnings = True):
+        """
+        Create a marker with origin `center` and `normal` as Z axis. The other
+        axes are computed using the parameters alpha and beta of the
+        Orientation2D instance. `geom` is the pseudo-geompy object used to
+        build the geometric shapes. `warnings` can be used to enable or
+        disable the logging of warning messages.
+        """
+        self.geom = geom
+        marker = None
+        refVecX = None
+        if self._vect is not None:
+            # Using vector parameter
+            if abs(self._vect[0]) <= 1e-7 and abs(self._vect[1]) <= 1e-7 and \
+                                              abs(self._vect[2]) <= 1e-7:
+                if warnings:
+                    logger.warning("Vector too small: %s, using default LCS" %
+                                   self._vect)
+            else:
+                refVecX = self.geom.MakeVectorDXDYDZ(self._vect[0],
+                                                     self._vect[1],
+                                                     self._vect[2])
+        elif self._alpha is not None and self._beta is not None:
+            # Using alpha and beta angles
+            alphaRad = math.radians(self._alpha)
+            betaRad = math.radians(self._beta)
+            if abs(alphaRad) <= 1e-7 and abs(betaRad) <= 1e-7:
+                if warnings:
+                    logger.warning("Angles too small: (%g, %g), using "
+                                   "default LCS" % (self._alpha, self._beta))
+            else:
+                # rotate global CS with angles alpha and beta
+                refVecX = self.geom.MakeVectorDXDYDZ(1.0, 0.0, 0.0)
+                refVecY = self.geom.MakeVectorDXDYDZ(0.0, 1.0, 0.0)
+                globalVecZ = self.geom.MakeVectorDXDYDZ(0.0, 0.0, 1.0)
+                if abs(alphaRad) > 1e-7:
+                    refVecX = self.geom.Rotate(refVecX, globalVecZ, alphaRad)
+                    refVecY = self.geom.Rotate(refVecY, globalVecZ, alphaRad)
+                if abs(betaRad) > 1e-7:
+                    refVecX = self.geom.Rotate(refVecX, refVecY, betaRad)
+    
+        if refVecX is not None:
+            # build local coordinate system
+            angle = self.geom.GetAngleRadians(normal, refVecX)
+            if abs(angle) < 1e-7 or abs(angle - math.pi) < 1e-7:
+                if warnings:
+                    logger.warning("Face normal is colinear to the reference "
+                                   "X axis, using default LCS.")
+            else:
+                marker = self._buildMarkerRefVecX(center, normal, refVecX)
+
+        if marker is None:
+            marker = self._buildDefaultMarker(center, normal, warnings)
+
+        return marker
diff --git a/src/GEOM_PY/structelem/parts.py b/src/GEOM_PY/structelem/parts.py
new file mode 100644 (file)
index 0000000..0781f6d
--- /dev/null
@@ -0,0 +1,969 @@
+# -*- coding: utf-8 -*-
+#
+#  Copyright (C) 2007-2009      EDF R&D
+# 
+#    This file is part of PAL_SRC.
+#
+#    PAL_SRC 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 2 of the License, or
+#    (at your option) any later version.
+#
+#    PAL_SRC 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 PAL_SRC; if not, write to the Free Software
+#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+#
+"""
+This module defines the different structural element parts. It is used to
+build the geometric shapes of the structural elements. It should not be used
+directly in the general case. Structural elements should be created by the
+class :class:`~salome.geom.structelem.StructuralElementManager`.
+"""
+
+import salome
+
+from salome.kernel.logger import Logger
+from salome.kernel import termcolor
+logger = Logger("salome.geom.structelem.parts", color = termcolor.RED)
+from salome.geom.geomtools import getGeompy
+
+import orientation
+
+# Filling for the beams
+FULL = "FULL"
+HOLLOW = "HOLLOW"
+
+# Minimum dimension for the shapes to extrude
+MIN_DIM_FOR_EXTRUDED_SHAPE = 2e-4
+MIN_LENGTH_FOR_EXTRUSION = 1e-4
+MIN_THICKNESS = 1e-5
+
+
+class InvalidParameterError(Exception):
+    """
+    This exception is raised when an invalid parameter is used to build a
+    structural element part.
+    """
+    
+    def __init__(self, groupName, expression, minValue, value):
+        self.groupName = groupName
+        self.expression = expression
+        self.minValue = minValue
+        self.value = value
+        
+    def __str__(self):
+        return "%s < %g (%s = %g in %s)" % (self.expression, self.minValue,
+                                            self.expression, self.value,
+                                            self.groupName)
+
+
+class SubShapeID:
+    """
+    This class enables the use of subshapes in sets or as dictionary keys.
+    It implements __eq__ and __hash__ methods so that subshapes with the same
+    CORBA object `mainShape` and the same `id` are considered equal.
+    """
+
+    def __init__(self, mainShape, id):
+        self._mainShape = mainShape
+        self._id = id
+
+    def getObj(self, geom):
+        """
+        Return the subshape (GEOM object). `geom` is a pseudo-geompy object
+        used to find the geometrical object.
+        """
+        return geom.GetSubShape(self._mainShape, [self._id])
+    
+    def __eq__(self, other):
+        return self._mainShape._is_equivalent(other._mainShape) and \
+               self._id == other._id
+    
+    def __hash__(self):
+        return self._mainShape._hash(2147483647) ^ self._id
+
+
+class StructuralElementPart:
+    """
+    This class is the base class for all structural element parts. It should
+    not be instantiated directly (consider it as an "abstract" class).
+
+    :type  studyId: integer
+    :param studyId: the ID of the study in which the part is created.
+
+    :type  groupName: string
+    :param groupName: the name of the underlying geometrical primitive in the
+                      study.
+
+    :type  groupGeomObj: GEOM object
+    :param groupGeomObj: the underlying geometrical primitive.
+
+    :type  parameters: dictionary
+    :param parameters: parameters defining the structural element (see
+                       subclasses for details).
+
+    :type  name: string
+    :param name: name to use for the created object in the study.
+
+    """
+    
+    DEFAULT_NAME = "StructElemPart"
+
+    def __init__(self, studyId, groupName, groupGeomObj, parameters,
+                 name = DEFAULT_NAME):
+        self._parameters = parameters
+        self.groupName = groupName
+        self._groupGeomObj = groupGeomObj
+        self._orientation = None
+        self._paramUserName = {}
+        self.name = name
+        self.geom = getGeompy(studyId)
+        self.baseShapesSet = set()
+        mainShape = self.geom.GetMainShape(groupGeomObj)
+        listIDs = self.geom.GetObjectIDs(groupGeomObj)
+        if mainShape is not None and listIDs is not None:
+            for id in listIDs:
+                self.baseShapesSet.add(SubShapeID(mainShape, id))
+
+    def _getParameter(self, nameList, default = None):
+        """
+        This method finds the value of a parameter in the parameters
+        dictionary. The argument is a list because some parameters can have
+        several different names.
+        """
+        if len(nameList) > 0:
+            paramName = nameList[0]
+        for name in nameList:
+            if self._parameters.has_key(name):
+                self._paramUserName[paramName] = name
+                return self._parameters[name]
+        return default
+
+    def _getParamUserName(self, paramName):
+        """
+        This method finds the user name for a parameter.
+        """
+        if self._paramUserName.has_key(paramName):
+            return self._paramUserName[paramName]
+        else:
+            return paramName
+
+    def __repr__(self):
+        reprdict = self.__dict__.copy()
+        del reprdict["_parameters"]
+        del reprdict["groupName"]
+        del reprdict["_groupGeomObj"]
+        del reprdict["_paramUserName"]
+        del reprdict["name"]
+        del reprdict["geom"]
+        del reprdict["baseShapesSet"]
+        return '%s("%s", %s)' % (self.__class__.__name__, self.groupName,
+                                 reprdict)
+
+    def addOrientation(self, orientParams):
+        """
+        Add orientation information to the structural element part. See class
+        :class:`~salome.geom.structelem.orientation.Orientation1D` for the description
+        of the parameters.
+        """
+        self._orientation.addParams(orientParams)
+
+    def _checkSize(self, value, mindim, expression):
+        """
+        This method checks that some parameters or some expressions involving
+        those parameters are greater than a minimum value.
+        """
+        if value < mindim:
+            raise InvalidParameterError(self.groupName, expression,
+                                        mindim, value)
+
+    def build(self):
+        """
+        Build the geometric shapes and the markers corresponding to the
+        structural element part in the study `studyId`.
+        """
+        shape = self._buildPart()
+        markers = self._buildMarkers()
+        shape.SetColor(self._groupGeomObj.GetColor())
+        for marker in markers:
+            marker.SetColor(self._groupGeomObj.GetColor())
+        return (shape, markers)
+
+    def _buildPart(self):
+        """
+        This abstract method must be implemented in subclasses and should
+        create the geometrical shape(s) of the structural element part.
+        """
+        raise NotImplementedError("Method _buildPart not implemented in class"
+                                  " %s (it must be implemented in "
+                                  "StructuralElementPart subclasses)." %
+                                  self.__class__.__name__)
+
+    def _buildMarkers(self):
+        """
+        This abstract method must be implemented in subclasses and should
+        create the markers defining the orientation of the structural element
+        part.
+        """
+        raise NotImplementedError("Method _buildMarker not implemented in "
+                                  "class %s (it must be implemented in "
+                                  "StructuralElementPart subclasses)." %
+                                  self.__class__.__name__)
+
+    def _getSubShapes(self, minDim = MIN_LENGTH_FOR_EXTRUSION):
+        """
+        Find and return the base subshapes in the structural element part.
+        """
+        subShapes = []
+        for subShapeID in self.baseShapesSet:
+            subShape = subShapeID.getObj(self.geom)
+            length = self.geom.BasicProperties(subShape)[0]
+            if length < minDim:
+                logger.warning("Length too short (%s - ID %s, length = %g), "
+                               "subshape will not be used in structural "
+                               "element" % (self.groupName, subShapeID._id,
+                                            length))
+            else:
+                subShapes.append(subShape)
+        return subShapes
+
+
+class Beam(StructuralElementPart):
+    """
+    This class is an "abstract" class for all 1D structural element parts. It
+    should not be instantiated directly. See class
+    :class:`StructuralElementPart` for the description of the parameters.
+    """
+
+    DEFAULT_NAME = "Beam"
+
+    def __init__(self, studyId, groupName, groupGeomObj, parameters,
+                 name = DEFAULT_NAME):
+        StructuralElementPart.__init__(self, studyId, groupName, groupGeomObj,
+                                       parameters, name)
+        self._orientation = orientation.Orientation1D()
+
+    def _isReversed(self, path):
+        """
+        This method checks if a 1D object is "reversed", i.e. if its
+        orientation is different than the orientation of the underlying OCC
+        object.
+        """
+        fParam = 0.
+        lParam = 1.
+        fPoint = self.geom.MakeVertexOnCurve(path, fParam)
+        lPoint = self.geom.MakeVertexOnCurve(path, lParam)
+
+        fNormal = self.geom.MakeTangentOnCurve(path, fParam)
+        lNormal = self.geom.MakeTangentOnCurve(path, lParam)
+
+        fCircle = self.geom.MakeCircle(fPoint, fNormal, 10)
+        lCircle = self.geom.MakeCircle(lPoint, lNormal, 10)
+
+        try:
+            pipe = self.geom.MakePipeWithDifferentSections([fCircle, lCircle],
+                                                           [fPoint, lPoint],
+                                                           path, False, False)
+        except RuntimeError, e:
+            # This dirty trick is needed if the wire is not oriented in the
+            # direction corresponding to parameters 0.0 -> 1.0. In this case,
+            # we catch the error and invert the ends of the wire. This trick
+            # will be removed when the function giving the orientation of an
+            # edge will be added in geompy (see issue 1144 in PAL bugtracker).
+            if (str(e) == "MakePipeWithDifferentSections : First location "
+                          "shapes is not coincided with first vertex of "
+                          "aWirePath"):
+                return True
+            else:
+                raise
+        return False
+
+    def _getVertexAndTangentOnOrientedWire(self, path, param):
+        """
+        Get a vertex and the corresponding tangent on a wire by parameter.
+        This method takes into account the "real" orientation of the wire
+        (i.e. the orientation of the underlying OCC object).
+        """
+        if self._isReversed(path):
+            vertex = self.geom.MakeVertexOnCurve(path, 1.0 - param)
+            invtangent = self.geom.MakeTangentOnCurve(path, 1.0 - param)
+            tanpoint = self.geom.MakeTranslationVectorDistance(vertex,
+                                                               invtangent,
+                                                               -1.0)
+            tangent = self.geom.MakeVector(vertex, tanpoint)
+        else:
+            vertex = self.geom.MakeVertexOnCurve(path, param)
+            tangent = self.geom.MakeTangentOnCurve(path, param)
+        return (vertex, tangent)
+
+    def _makeSolidPipeFromWires(self, wire1, wire2, point1, point2, path):
+        """
+        Create a solid by the extrusion of section `wire1` to section `wire2`
+        along `path`.
+        """
+        face1 = self.geom.MakeFace(wire1, True)
+        face2 = self.geom.MakeFace(wire2, True)
+        shell = self.geom.MakePipeWithDifferentSections([wire1, wire2],
+                                                        [point1, point2],
+                                                        path, False, False)
+        closedShell = self.geom.MakeShell([face1, face2, shell])
+        solid = self.geom.MakeSolid([closedShell])
+        return solid
+
+    def _buildPart(self):
+        """
+        Build the structural element part.
+        """
+        # Get all the subshapes in the group (normally only edges and wires)
+        paths = self._getSubShapes()
+        listPipes = []
+        withContact = False
+        withCorrection = False
+    
+        for path in paths:
+            # Build the sections (rectangular or circular) at each end of the
+            # beam
+            (fPoint, fNormal) = self._getVertexAndTangentOnOrientedWire(path,
+                                                                        0.0)
+            (lPoint, lNormal) = self._getVertexAndTangentOnOrientedWire(path,
+                                                                        1.0)
+            (outerWire1, innerWire1, outerWire2, innerWire2) = \
+                    self._makeSectionWires(fPoint, fNormal, lPoint, lNormal)
+
+            # Create the resulting solid
+            outerSolid = self._makeSolidPipeFromWires(outerWire1, outerWire2,
+                                                      fPoint, lPoint, path)
+            if self.filling == HOLLOW:
+                innerSolid = self._makeSolidPipeFromWires(innerWire1,
+                                                          innerWire2, fPoint,
+                                                          lPoint, path)
+                resultSolid = self.geom.MakeCut(outerSolid, innerSolid)
+                listPipes.append(resultSolid)
+            else:
+                listPipes.append(outerSolid)
+
+        if len(listPipes) == 0:
+            return None
+        elif len(listPipes) == 1:
+            return listPipes[0]
+        else:
+            return self.geom.MakeCompound(listPipes)
+
+    def _buildMarkers(self):
+        """
+        Build the markers defining the orientation of the structural element
+        part.
+        """
+        param = 0.5
+        paths = self._getSubShapes()
+        listMarkers = []
+        for path in paths:
+            (center, vecX) = self._getVertexAndTangentOnOrientedWire(path,
+                                                                     param)
+            marker = self._orientation.buildMarker(self.geom, center, vecX)
+            listMarkers.append(marker)
+        return listMarkers
+
+
+class GeneralBeam(Beam):
+    """
+    This class defines a beam with a generic section. It is represented only
+    as the underlying wire. See class :class:`StructuralElementPart` for the
+    description of the parameters.
+    """
+
+    def __init__(self, studyId, groupName, groupGeomObj, parameters,
+                 name = Beam.DEFAULT_NAME):
+        Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
+                      name)
+        logger.debug(repr(self))
+
+    def _buildPart(self):
+        """
+        Create a copy of the underlying wire.
+        """
+        edges = self._getSubShapes(1e-7)
+        wire = None
+        if len(edges) > 0:
+            wire = self.geom.MakeWire(edges)
+        return wire
+
+
+class CircularBeam(Beam):
+    """
+    This class defines a beam with a circular section. It can be full or
+    hollow, and its radius and thickness can vary from one end of the beam to
+    the other. The valid parameters for circular beams are:
+
+    * "R1" or "R": radius at the first end of the beam.
+    * "R2" or "R": radius at the other end of the beam.
+    * "EP1" or "EP" (optional): thickness at the first end of the beam.
+      If not specified or equal to 0, the beam is considered full.
+    * "EP2" or "EP" (optional): thickness at the other end of the beam.
+      If not specified or equal to 0, the beam is considered full.
+
+    See class :class:`StructuralElementPart` for the description of the
+    other parameters.
+
+    """
+
+    def __init__(self, studyId, groupName, groupGeomObj, parameters,
+                 name = Beam.DEFAULT_NAME):
+        Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
+                      name)
+
+        self.R1 = self._getParameter(["R1", "R"])
+        self.R2 = self._getParameter(["R2", "R"])
+        self.EP1 = self._getParameter(["EP1", "EP"])
+        self.EP2 = self._getParameter(["EP2", "EP"])
+
+        if self.EP1 is None or self.EP2 is None or \
+                                self.EP1 == 0 or self.EP2 == 0:
+            self.filling = FULL
+        else:
+            self.filling = HOLLOW
+
+        logger.debug(repr(self))
+
+        # Check parameters
+        self._checkSize(self.R1, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
+                        self._getParamUserName("R1"))
+        self._checkSize(self.R2, MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
+                        self._getParamUserName("R2"))
+        if self.filling == HOLLOW:
+            self._checkSize(self.EP1, MIN_THICKNESS,
+                            self._getParamUserName("EP1"))
+            self._checkSize(self.EP2, MIN_THICKNESS,
+                            self._getParamUserName("EP2"))
+            self._checkSize(self.R1 - self.EP1,
+                            MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
+                            "%s - %s" % (self._getParamUserName("R1"),
+                                         self._getParamUserName("EP1")))
+            self._checkSize(self.R2 - self.EP2,
+                            MIN_DIM_FOR_EXTRUDED_SHAPE / 2.0,
+                            "%s - %s" % (self._getParamUserName("R2"),
+                                         self._getParamUserName("EP2")))
+
+    def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
+        """
+        Create the circular sections used to build the pipe.
+        """
+        outerCircle1 = self.geom.MakeCircle(fPoint, fNormal, self.R1)
+        outerCircle2 = self.geom.MakeCircle(lPoint, lNormal, self.R2)
+        if self.filling == HOLLOW:
+            innerCircle1 = self.geom.MakeCircle(fPoint, fNormal,
+                                                self.R1 - self.EP1)
+            innerCircle2 = self.geom.MakeCircle(lPoint, lNormal,
+                                                self.R2 - self.EP2)
+        else:
+            innerCircle1 = None
+            innerCircle2 = None
+
+        return (outerCircle1, innerCircle1, outerCircle2, innerCircle2)
+
+
+class RectangularBeam(Beam):
+    """
+    This class defines a beam with a rectangular section. It can be full or
+    hollow, and its dimensions can vary from one end of the beam to the other.
+    The valid parameters for rectangular beams are:
+
+    * "HY1", "HY", "H1" or "H": width at the first end of the beam.
+    * "HZ1", "HZ", "H1" or "H": height at the first end of the beam.
+    * "HY2", "HY", "H2" or "H": width at the other end of the beam.
+    * "HZ2", "HZ", "H2" or "H": height at the other end of the beam.
+    * "EPY1", "EPY", "EP1" or "EP" (optional): thickness in the width
+      direction at the first end of the beam. If not specified or equal to 0,
+      the beam is considered full.
+    * "EPZ1", "EPZ", "EP1" or "EP" (optional): thickness in the height
+      direction at the first end of the beam. If not specified or equal to 0,
+      the beam is considered full.
+    * "EPY2", "EPY", "EP2" or "EP" (optional): thickness in the width
+      direction at the other end of the beam. If not specified or equal to 0,
+      the beam is considered full.
+    * "EPZ2", "EPZ", "EP2" or "EP" (optional): thickness in the height
+      direction at the other end of the beam. If not specified or equal to 0,
+      the beam is considered full.
+
+    See class :class:`StructuralElementPart` for the description of the
+    other parameters.
+
+    """
+
+    def __init__(self, studyId, groupName, groupGeomObj, parameters,
+                 name = Beam.DEFAULT_NAME):
+        Beam.__init__(self, studyId, groupName, groupGeomObj, parameters,
+                      name)
+
+        self.HY1 = self._getParameter(["HY1", "HY", "H1", "H"])
+        self.HZ1 = self._getParameter(["HZ1", "HZ", "H1", "H"])
+        self.HY2 = self._getParameter(["HY2", "HY", "H2", "H"])
+        self.HZ2 = self._getParameter(["HZ2", "HZ", "H2", "H"])
+        self.EPY1 = self._getParameter(["EPY1", "EPY", "EP1", "EP"])
+        self.EPZ1 = self._getParameter(["EPZ1", "EPZ", "EP1", "EP"])
+        self.EPY2 = self._getParameter(["EPY2", "EPY", "EP2", "EP"])
+        self.EPZ2 = self._getParameter(["EPZ2", "EPZ", "EP2", "EP"])
+
+        if self.EPY1 is None or self.EPZ1 is None or \
+           self.EPY2 is None or self.EPZ2 is None or \
+           self.EPY1 == 0 or self.EPZ1 == 0 or \
+           self.EPY2 == 0 or self.EPZ2 == 0:
+            self.filling = FULL
+        else:
+            self.filling = HOLLOW
+
+        logger.debug(repr(self))
+
+        # Check parameters
+        self._checkSize(self.HY1, MIN_DIM_FOR_EXTRUDED_SHAPE,
+                        self._getParamUserName("HY1"))
+        self._checkSize(self.HZ1, MIN_DIM_FOR_EXTRUDED_SHAPE,
+                        self._getParamUserName("HZ1"))
+        self._checkSize(self.HY2, MIN_DIM_FOR_EXTRUDED_SHAPE,
+                        self._getParamUserName("HY2"))
+        self._checkSize(self.HZ2, MIN_DIM_FOR_EXTRUDED_SHAPE,
+                        self._getParamUserName("HZ2"))
+        if self.filling == HOLLOW:
+            self._checkSize(self.EPY1, MIN_THICKNESS,
+                            self._getParamUserName("EPY1"))
+            self._checkSize(self.EPZ1, MIN_THICKNESS,
+                            self._getParamUserName("EPZ1"))
+            self._checkSize(self.EPY2, MIN_THICKNESS,
+                            self._getParamUserName("EPY2"))
+            self._checkSize(self.EPZ2, MIN_THICKNESS,
+                            self._getParamUserName("EPZ2"))
+            self._checkSize(self.HY1 - 2 * self.EPY1,
+                            MIN_DIM_FOR_EXTRUDED_SHAPE,
+                            "%s - 2 * %s" % (self._getParamUserName("HY1"),
+                                             self._getParamUserName("EPY1")))
+            self._checkSize(self.HZ1 - 2 * self.EPZ1,
+                            MIN_DIM_FOR_EXTRUDED_SHAPE,
+                            "%s - 2 * %s" % (self._getParamUserName("HZ1"),
+                                             self._getParamUserName("EPZ1")))
+            self._checkSize(self.HY2 - 2 * self.EPY2,
+                            MIN_DIM_FOR_EXTRUDED_SHAPE,
+                            "%s - 2 * %s" % (self._getParamUserName("HY2"),
+                                             self._getParamUserName("EPY2")))
+            self._checkSize(self.HZ2 - 2 * self.EPZ2,
+                            MIN_DIM_FOR_EXTRUDED_SHAPE,
+                            "%s - 2 * %s" % (self._getParamUserName("HZ2"),
+                                             self._getParamUserName("EPZ2")))
+
+    def _makeRectangle(self, HY, HZ, planeSect):
+        """
+        Create a rectangle in the specified plane.
+        """
+        halfHY = HY / 2.0
+        halfHZ = HZ / 2.0
+        sketchStr = "Sketcher:F %g" % (-halfHZ) + " %g" % (-halfHY) + ":"
+        sketchStr += "TT %g" % (halfHZ) + " %g" % (-halfHY) + ":"
+        sketchStr += "TT %g" % (halfHZ) + " %g" % (halfHY) + ":" 
+        sketchStr += "TT %g" % (-halfHZ) + " %g" % (halfHY) + ":WW"
+        logger.debug('Drawing rectangle: "%s"' % sketchStr)
+        sketch = self.geom.MakeSketcherOnPlane(sketchStr, planeSect)
+        return sketch
+
+    def _makeSectionWires(self, fPoint, fNormal, lPoint, lNormal):
+        """
+        Create the rectangular sections used to build the pipe.
+        """
+        planeSect1 = self.geom.MakePlane(fPoint, fNormal, 1.0)
+        outerRect1 = self._makeRectangle(self.HY1, self.HZ1, planeSect1)
+        planeSect2 = self.geom.MakePlane(lPoint, lNormal, 1.0)
+        outerRect2 = self._makeRectangle(self.HY2, self.HZ2, planeSect2)
+        if self.filling == HOLLOW:
+            innerRect1 = self._makeRectangle(self.HY1 - 2 * self.EPY1,
+                                             self.HZ1 - 2 * self.EPZ1,
+                                             planeSect1)
+            innerRect2 = self._makeRectangle(self.HY2 - 2 * self.EPY2,
+                                             self.HZ2 - 2 * self.EPZ2,
+                                             planeSect2)
+        else:
+            innerRect1 = None
+            innerRect2 = None
+
+        return (outerRect1, innerRect1, outerRect2, innerRect2)
+
+
+class StructuralElementPart2D(StructuralElementPart):
+    """
+    This class is an "abstract" class for all 2D structural element parts. It
+    should not be instantiated directly. See class
+    :class:`StructuralElementPart` for the description of the parameters.
+    """
+
+    DEFAULT_NAME = "StructuralElementPart2D"
+
+    def __init__(self, studyId, groupName, groupGeomObj, parameters,
+                 name = DEFAULT_NAME):
+        StructuralElementPart.__init__(self, studyId, groupName, groupGeomObj,
+                                       parameters, name)
+        self._orientation = orientation.Orientation2D(
+                                        self._getParameter(["angleAlpha"]),
+                                        self._getParameter(["angleBeta"]),
+                                        self._getParameter(["Vecteur"]))
+        self.offset = self._getParameter(["Excentre"], 0.0)
+
+    def _makeFaceOffset(self, face, offset, epsilon = 1e-6):
+        """
+        Create a copy of a face at a given offset.
+        """
+        if abs(offset) < epsilon:
+            return self.geom.MakeCopy(face)
+        else:
+            offsetObj = self.geom.MakeOffset(face, offset)
+            # We have to explode the resulting object into faces because it is
+            # created as a polyhedron and not as a single face
+            faces = self.geom.SubShapeAll(offsetObj,
+                                          self.geom.ShapeType["FACE"])
+            return faces[0]
+
+    def _buildMarkersWithOffset(self, offset):
+        """
+        Build the markers for the structural element part with a given offset
+        from the base face.
+        """
+        uParam = 0.5
+        vParam = 0.5
+        listMarkers = []
+        subShapes = self._getSubShapes()
+    
+        for subShape in subShapes:
+            faces = self.geom.SubShapeAll(subShape,
+                                          self.geom.ShapeType["FACE"])
+            for face in faces:
+                offsetFace = self._makeFaceOffset(face, offset)
+                # get tangent plane on surface by parameters
+                center = self.geom.MakeVertexOnSurface(offsetFace,
+                                                       uParam, vParam)
+                tangPlane = self.geom.MakeTangentPlaneOnFace(offsetFace,
+                                                             uParam, vParam,
+                                                             1.0)
+                normal = self.geom.GetNormal(tangPlane)
+                marker = self._orientation.buildMarker(self.geom,
+                                                       center, normal)
+                listMarkers.append(marker)
+
+        return listMarkers
+
+
+class ThickShell(StructuralElementPart2D):
+    """
+    This class defines a shell with a given thickness. It can be shifted from
+    the base face. The valid parameters for thick shells are:
+
+    * "Epais": thickness of the shell.
+    * "Excentre": offset of the shell from the base face.
+    * "angleAlpha": angle used to build the markers (see class
+      :class:`~salome.geom.structelem.orientation.Orientation2D`)
+    * "angleBeta": angle used to build the markers (see class
+      :class:`~salome.geom.structelem.orientation.Orientation2D`)
+    * "Vecteur": vector used instead of the angles to build the markers (see
+      class :class:`~salome.geom.structelem.orientation.Orientation2D`)
+
+    See class :class:`StructuralElementPart` for the description of the
+    other parameters.
+    """
+
+    DEFAULT_NAME = "ThickShell"
+
+    def __init__(self, studyId, groupName, groupGeomObj, parameters,
+                 name = DEFAULT_NAME):
+        StructuralElementPart2D.__init__(self, studyId, groupName,
+                                         groupGeomObj, parameters, name)
+        self.thickness = self._getParameter(["Epais"])
+        logger.debug(repr(self))
+
+    def _buildPart(self):
+        """
+        Create the geometrical shapes corresponding to the thick shell.
+        """
+        subShapes = self._getSubShapes()
+        listSolids = []
+    
+        for subShape in subShapes:
+            faces = self.geom.SubShapeAll(subShape,
+                                          self.geom.ShapeType["FACE"])
+            for face in faces:
+                shape = self._buildThickShellForFace(face)
+                listSolids.append(shape)
+
+        if len(listSolids) == 0:
+            return None
+        elif len(listSolids) == 1:
+            return listSolids[0]
+        else:
+            return self.geom.MakeCompound(listSolids)
+
+    def _buildThickShellForFace(self, face):
+        """
+        Create the geometrical shapes corresponding to the thick shell for a
+        given face.
+        """
+        epsilon = 1e-6
+        if self.thickness < 2 * epsilon:
+            return self._makeFaceOffset(face, self.offset, epsilon)
+
+        upperOffset = self.offset + self.thickness / 2.0
+        lowerOffset = self.offset - self.thickness / 2.0
+        ruledMode = True
+        modeSolid = False
+
+        upperFace = self._makeFaceOffset(face, upperOffset, epsilon)
+        lowerFace = self._makeFaceOffset(face, lowerOffset, epsilon)
+        listShapes = [upperFace, lowerFace]
+        upperWires = self.geom.SubShapeAll(upperFace,
+                                           self.geom.ShapeType["WIRE"])
+        lowerWires = self.geom.SubShapeAll(lowerFace,
+                                           self.geom.ShapeType["WIRE"])
+        if self.geom.KindOfShape(face)[0] == self.geom.kind.CYLINDER2D:
+            # if the face is a cylinder, we remove the extra side edge
+            upperWires = self._removeCylinderExtraEdge(upperWires)
+            lowerWires = self._removeCylinderExtraEdge(lowerWires)
+        for i in range(len(upperWires)):
+            resShape = self.geom.MakeThruSections([upperWires[i],
+                                                   lowerWires[i]],
+                                                  modeSolid, epsilon,
+                                                  ruledMode)
+            listShapes.append(resShape)
+        resultShell = self.geom.MakeShell(listShapes)
+        resultSolid = self.geom.MakeSolid([resultShell])
+        return resultSolid
+
+    def _removeCylinderExtraEdge(self, wires):
+        """
+        Remove the side edge in a cylinder.
+        """
+        result = []
+        for wire in wires:
+            edges = self.geom.SubShapeAll(wire, self.geom.ShapeType["EDGE"])
+            for edge in edges:
+                if self.geom.KindOfShape(edge)[0] == self.geom.kind.CIRCLE:
+                    result.append(edge)
+        return result
+
+    def _buildMarkers(self):
+        """
+        Build the markers defining the orientation of the thick shell.
+        """
+        return self._buildMarkersWithOffset(self.offset +
+                                            self.thickness / 2.0)
+
+
+class Grid(StructuralElementPart2D):
+    """
+    This class defines a grid. A grid is represented by a 2D face patterned
+    with small lines in the main direction of the grid frame. The valid
+    parameters for grids are:
+
+    * "Excentre": offset of the grid from the base face.
+    * "angleAlpha": angle used to build the markers (see class
+      :class:`~salome.geom.structelem.orientation.Orientation2D`)
+    * "angleBeta": angle used to build the markers (see class
+      :class:`~salome.geom.structelem.orientation.Orientation2D`)
+    * "Vecteur": vector used instead of the angles to build the markers (see
+      class :class:`~salome.geom.structelem.orientation.Orientation2D`)
+    * "origAxeX": X coordinate of the origin of the axis used to determine the
+      orientation of the frame in the case of a cylindrical grid.
+    * "origAxeY": Y coordinate of the origin of the axis used to determine the
+      orientation of the frame in the case of a cylindrical grid.
+    * "origAxeZ": Z coordinate of the origin of the axis used to determine the
+      orientation of the frame in the case of a cylindrical grid.
+    * "axeX": X coordinate of the axis used to determine the orientation of
+      the frame in the case of a cylindrical grid.
+    * "axeY": Y coordinate of the axis used to determine the orientation of
+      the frame in the case of a cylindrical grid.
+    * "axeZ": Z coordinate of the axis used to determine the orientation of
+      the frame in the case of a cylindrical grid.
+
+    See class :class:`StructuralElementPart` for the description of the
+    other parameters.
+    """
+
+    DEFAULT_NAME = "Grid"
+
+    def __init__(self, studyId, groupName, groupGeomObj, parameters,
+                 name = DEFAULT_NAME):
+        StructuralElementPart2D.__init__(self, studyId, groupName,
+                                         groupGeomObj, parameters, name)
+        self.xr = self._getParameter(["origAxeX"])
+        self.yr = self._getParameter(["origAxeY"])
+        self.zr = self._getParameter(["origAxeZ"])
+        self.vx = self._getParameter(["axeX"])
+        self.vy = self._getParameter(["axeY"])
+        self.vz = self._getParameter(["axeZ"])
+        logger.debug(repr(self))
+
+    def _buildPart(self):
+        """
+        Create the geometrical shapes representing the grid.
+        """
+        subShapes = self._getSubShapes()
+        listGridShapes = []
+    
+        for subShape in subShapes:
+            faces = self.geom.SubShapeAll(subShape,
+                                          self.geom.ShapeType["FACE"])
+            for face in faces:
+                if self.geom.KindOfShape(face)[0] == \
+                                        self.geom.kind.CYLINDER2D and \
+                        self.xr is not None and self.yr is not None and \
+                        self.zr is not None and self.vx is not None and \
+                        self.vy is not None and self.vz is not None:
+                    shape = self._buildGridForCylinderFace(face)
+                else:
+                    shape = self._buildGridForNormalFace(face)
+                listGridShapes.append(shape)
+
+        if len(listGridShapes) == 0:
+            return None
+        elif len(listGridShapes) == 1:
+            return listGridShapes[0]
+        else:
+            return self.geom.MakeCompound(listGridShapes)
+
+    def _buildGridForNormalFace(self, face):
+        """
+        Create the geometrical shapes representing the grid for a given
+        non-cylindrical face.
+        """
+        baseFace = self._makeFaceOffset(face, self.offset)
+        gridList = [baseFace]
+        
+        # Compute display length for grid elements
+        p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
+        p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
+        length = self.geom.MinDistance(p1, p2) / 2.0
+
+        for u in range(1, 10):
+            uParam = u * 0.1
+            for v in range(1, 10):
+                vParam = v * 0.1
+                # get tangent plane on surface by parameters
+                center = self.geom.MakeVertexOnSurface(baseFace,
+                                                       uParam, vParam)
+                tangPlane = self.geom.MakeTangentPlaneOnFace(baseFace, uParam,
+                                                             vParam, 1.0)
+                
+                # use the marker to get the orientation of the frame
+                normal = self.geom.GetNormal(tangPlane)
+                marker = self._orientation.buildMarker(self.geom, center,
+                                                       normal, False)
+                [Ox,Oy,Oz, Zx,Zy,Zz, Xx,Xy,Xz] = self.geom.GetPosition(marker)
+                xPoint = self.geom.MakeTranslation(center, Xx * length,
+                                                   Xy * length, Xz * length)
+                gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
+                gridList.append(gridLine)
+        grid = self.geom.MakeCompound(gridList)
+        return grid
+
+    def _buildGridForCylinderFace(self, face):
+        """
+        Create the geometrical shapes representing the grid for a given
+        cylindrical face.
+        """
+        baseFace = self._makeFaceOffset(face, self.offset)
+        gridList = [baseFace]
+        
+        # Compute display length for grid elements
+        p1 = self.geom.MakeVertexOnSurface(baseFace, 0.0, 0.0)
+        p2 = self.geom.MakeVertexOnSurface(baseFace, 0.1, 0.1)
+        length = self.geom.MinDistance(p1, p2) / 2.0
+        
+        # Create reference vector V
+        origPoint = self.geom.MakeVertex(self.xr, self.yr, self.zr)
+        vPoint = self.geom.MakeTranslation(origPoint,
+                                           self.vx, self.vy, self.vz)
+        refVec = self.geom.MakeVector(origPoint, vPoint)
+
+        for u in range(10):
+            uParam = u * 0.1
+            for v in range(1, 10):
+                vParam = v * 0.1
+                
+                # Compute the local orientation of the frame
+                center = self.geom.MakeVertexOnSurface(baseFace,
+                                                       uParam, vParam)
+                locPlaneYZ = self.geom.MakePlaneThreePnt(origPoint, center,
+                                                         vPoint, 1.0)
+                locOrient = self.geom.GetNormal(locPlaneYZ)
+                xPoint = self.geom.MakeTranslationVectorDistance(center,
+                                                                 locOrient,
+                                                                 length)
+                gridLine = self.geom.MakeLineTwoPnt(center, xPoint)
+                gridList.append(gridLine)
+
+        grid = self.geom.MakeCompound(gridList)
+        return grid
+
+    def _buildMarkers(self):
+        """
+        Create the markers defining the orientation of the grid.
+        """
+        return self._buildMarkersWithOffset(self.offset)
+
+
+def VisuPoutreGenerale(studyId, groupName, groupGeomObj, parameters,
+                       name = "POUTRE"):
+    """
+    Alias for class :class:`GeneralBeam`.
+    """
+    return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name)
+
+def VisuPoutreCercle(studyId, groupName, groupGeomObj, parameters,
+                     name = "POUTRE"):
+    """
+    Alias for class :class:`CircularBeam`.
+    """
+    return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
+  
+def VisuPoutreRectangle(studyId, groupName, groupGeomObj, parameters,
+                        name = "POUTRE"):
+    """
+    Alias for class :class:`RectangularBeam`.
+    """
+    return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name)
+  
+def VisuBarreGenerale(studyId, groupName, groupGeomObj, parameters,
+                      name = "BARRE"):
+    """
+    Alias for class :class:`GeneralBeam`.
+    """
+    return GeneralBeam(studyId, groupName, groupGeomObj, parameters, name)
+      
+def VisuBarreRectangle(studyId, groupName, groupGeomObj, parameters,
+                       name = "BARRE"):
+    """
+    Alias for class :class:`RectangularBeam`.
+    """
+    return RectangularBeam(studyId, groupName, groupGeomObj, parameters, name)
+
+def VisuBarreCercle(studyId, groupName, groupGeomObj, parameters,
+                    name = "BARRE"):
+    """
+    Alias for class :class:`CircularBeam`.
+    """
+    return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
+
+def VisuCable(studyId, groupName, groupGeomObj, parameters, name = "CABLE"):
+    """
+    Alias for class :class:`CircularBeam`.
+    """
+    return CircularBeam(studyId, groupName, groupGeomObj, parameters, name)
+
+def VisuCoque(studyId, groupName, groupGeomObj, parameters, name = "COQUE"):
+    """
+    Alias for class :class:`ThickShell`.
+    """
+    return ThickShell(studyId, groupName, groupGeomObj, parameters, name)
+  
+def VisuGrille(studyId, groupName, groupGeomObj, parameters, name = "GRILLE"):
+    """
+    Alias for class :class:`Grid`.
+    """
+    return Grid(studyId, groupName, groupGeomObj, parameters, name)
index 761e3d6a96154abd12341be156597b0fee46abbf..704015b0db2977affe9d6a0759b7a1b15bbbf957 100644 (file)
@@ -24,7 +24,7 @@
 SUBDIRS = ARCHIMEDE NMTDS NMTTools GEOMAlgo SKETCHER GEOM BREPExport   \
           BREPImport IGESExport IGESImport STEPExport STEPImport       \
           STLExport ShHealOper GEOMImpl GEOM_I GEOMClient GEOM_I_Superv        \
-          GEOM_SWIG
+          GEOM_SWIG GEOM_PY
 
 if GEOM_ENABLE_GUI
   SUBDIRS += OBJECT DlgRef GEOMFiltersSelection GEOMGUI GEOMBase GEOMToolsGUI  \
@@ -39,4 +39,4 @@ DIST_SUBDIRS = ARCHIMEDE NMTDS NMTTools GEOMAlgo SKETCHER GEOM BREPExport     \
                OBJECT DlgRef GEOMFiltersSelection GEOMGUI GEOMBase GEOMToolsGUI        \
                DisplayGUI BasicGUI PrimitiveGUI GenerationGUI EntityGUI                \
                BuildGUI BooleanGUI TransformationGUI OperationGUI RepairGUI    \
-               MeasureGUI GroupGUI BlocksGUI AdvancedGUI GEOM_SWIG_WITHIHM
+               MeasureGUI GroupGUI BlocksGUI AdvancedGUI GEOM_SWIG_WITHIHM GEOM_PY